Source code for generate_changelog.context

"""Context definitions used in templates."""
import collections
import datetime
import re
from dataclasses import dataclass, field
from typing import Callable, List, Optional, Tuple

from generate_changelog.configuration import Configuration, get_config
from generate_changelog.utilities import diff_index


[docs] @dataclass class CommitContext: """Commit information for the template context.""" sha: str """The full hex SHA of the commit.""" commit_datetime: datetime.datetime """The date and time of the commit with timezone offset.""" summary: str """The first line of the commit message.""" body: str """The commit message sans the first line.""" committer: str """The name and email of the committer as `name <email@ex.com>`.""" grouping: tuple = field(default_factory=tuple) """The values to group this commit based on the ``group_by`` configuration.""" metadata: dict = field(default_factory=dict) """Metadata for this commit parsed from the commit message.""" files: set = field(default_factory=set) """The file paths (relative to the repository root) modified by this commit.""" _authors: Optional[list] = field(init=False) # list of dicts with name and email keys _author_names: Optional[list] = field(init=False) # list of just the names
[docs] def __post_init__(self): """Set the cached author information to None.""" self._authors = None self._author_names = None
@property def short_sha(self) -> str: """The first seven characters of the hex sha.""" return self.sha[:7] @property def authors(self) -> list: """ A list of authors' names and emails. Returns: A list of dictionaries with name and email keys. """ if self._authors is not None: return self._authors raw_authors = [self.committer] trailers = self.metadata.get("trailers", collections.defaultdict(list)) for token in get_config().valid_author_tokens: raw_authors.extend(trailers.get(token, [])) author_regex = re.compile(r"^(?P<name>[^<]+)\s+(?:<(?P<email>[^>]+)>)?$") self._authors = [] for author in raw_authors: match = author_regex.match(author) if match: self._authors.append(match.groupdict()) else: self._authors.append({"name": author, "email": ""}) self._authors.sort(key=lambda x: x["name"]) return self._authors @property def author_names(self) -> list: """A list of the authors' names.""" if self._author_names is not None: return self._author_names self._author_names = [x["name"] for x in self.authors] return self._author_names
[docs] @dataclass class GroupingContext: """A combination of a tuple of the sorted values and a list of the CommitContexts in that group.""" grouping: Tuple[str, ...] commits: List[CommitContext]
[docs] @dataclass class VersionContext: """Version information for the template context.""" label: str """The version label.""" date_time: Optional[datetime.datetime] = None """The date and time with timezone offset the version was tagged.""" tag: Optional[str] = None """The tag.""" previous_tag: Optional[str] = None """The previous tag.""" tagger: Optional[str] = None """The name and email of the person who tagged this version in `name <email@ex.com>` format.""" grouped_commits: List[GroupingContext] = field(default_factory=list) """The sections that group the commits in this version.""" metadata: dict = field(default_factory=dict) """Metadata for this version parsed from commits."""
[docs] @dataclass class ChangelogContext: """The primary context used when rendering a changelog.""" config: Configuration """The changelog generation configuration.""" versions: List[VersionContext] = field(default_factory=list) """The version contexts to render in the changelog.""" # Fields generated from the configuration post init unreleased_label: str = field(init=False) """The configured label used as the version title of the changes since the last valid tag.""" valid_author_tokens: List[str] = field(init=False, default_factory=list) """The configured tokens in git commit trailers that indicate authorship.""" group_by: List[str] = field(init=False, default_factory=list) """The configured grouping aspects for commits within a version.""" group_depth: int = field(init=False) """The number of levels version commits are grouped by.""" diff_index: Callable = field(init=False) def __post_init__(self): self.unreleased_label = self.config.unreleased_label self.valid_author_tokens = self.config.valid_author_tokens self.group_by = self.config.group_by self.group_depth = len(self.config.group_by) self.diff_index = diff_index for var, val in self.config.rendered_variables.items(): setattr(self, var, val)
[docs] def as_dict(self) -> dict: """Safely generate a dict version of this object.""" return self.__dict__