Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# Source code for semantic_release.commit_parser.scipy

    """
    Parses commit messages using `scipy tags <scipy-style>`_ of the form::

        <tag>(<scope>): <subject>

        <body>

    The elements <tag>, <scope> and <body> are optional. If no tag is present, the
    commit will be added to the changelog section "None" and no version increment
    will be performed.

    While <scope> is supported here it isn't actually part of the scipy style.
    If it is missing, parentheses around it are too. The commit should then be
    of the form::

        <tag>: <subject>

        <body>

    To communicate a breaking change add "BREAKING CHANGE" into the body at the
    beginning of a paragraph. Fill this paragraph with information how to migrate
    from the broken behavior to the new behavior. It will be added to the
    "Breaking" section of the changelog.

    Supported Tags::

        (
            API,
            DEP,
            ENH,
            REV,
            BUG,
            MAINT,
            BENCH,
            BLD,
        )
        DEV, DOC, STY, TST, REL, FEAT, TEST

    Supported Changelog Sections::

        breaking, feature, fix, Other, None

    .. _`scipy-style`: https://docs.scipy.org/doc/scipy/reference/dev/contributor/development_workflow.html#writing-the-commit-message
    """

    from __future__ import annotations

    import logging
    from typing import TYPE_CHECKING, Tuple

    from pydantic.dataclasses import dataclass

    from semantic_release.commit_parser.angular import (
        AngularCommitParser,
        AngularParserOptions,
    )
    from semantic_release.commit_parser.token import (
        ParsedMessageResult,
        ParseError,
    )
    from semantic_release.enums import LevelBump

    if TYPE_CHECKING:  # pragma: no cover
        from git.objects.commit import Commit

    logger = logging.getLogger(__name__)

    def _logged_parse_error(commit: Commit, error: str) -> ParseError:
        logger.debug(error)
        return ParseError(commit, error=error)

    tag_to_section = {
        "API": "breaking",
        "BENCH": "none",
        "BLD": "fix",
        "BUG": "fix",
        "DEP": "breaking",
        "DEV": "none",
        "DOC": "documentation",
        "ENH": "feature",
        "MAINT": "fix",
        "REV": "other",
        "STY": "none",
        "TST": "none",
        "REL": "none",
        # strictly speaking not part of the standard
        "FEAT": "feature",
        "TEST": "none",
    }

    @dataclass
    class ScipyParserOptions(AngularParserOptions):
        """
        Options dataclass for ScipyCommitParser

        Scipy-style commit messages follow the same format as Angular-style commit
        just with different tag names.
        """

        major_tags: Tuple[str, ...] = ("API",)
        """Commit-type prefixes that should result in a major release bump."""

        minor_tags: Tuple[str, ...] = ("DEP", "DEV", "ENH", "REV", "FEAT")
        """Commit-type prefixes that should result in a minor release bump."""

        patch_tags: Tuple[str, ...] = ("BLD", "BUG", "MAINT")
        """Commit-type prefixes that should result in a patch release bump."""

        allowed_tags: Tuple[str, ...] = (
            *major_tags,
            *minor_tags,
            *patch_tags,
            "BENCH",
            "DOC",
            "STY",
            "TST",
            "REL",
            "TEST",
        )
        """
        All commit-type prefixes that are allowed.

        These are used to identify a valid commit message. If a commit message does not start with
        one of these prefixes, it will not be considered a valid commit message.
        """

        # TODO: breaking v10, make consistent with AngularParserOptions
        default_level_bump: LevelBump = LevelBump.NO_RELEASE
        """The minimum bump level to apply to valid commit message."""

        def __post_init__(self) -> None:
            # TODO: breaking v10, remove as the name is now consistent
            self.default_bump_level = self.default_level_bump
            super().__post_init__()
            for tag in self.major_tags:
                self._tag_to_level[tag] = LevelBump.MAJOR

    class ScipyCommitParser(AngularCommitParser):
        """Parser for scipy-style commit messages"""

        # TODO: Deprecate in lieu of get_default_options()
        parser_options = ScipyParserOptions

        def __init__(self, options: ScipyParserOptions | None = None) -> None:
            super().__init__(options)

        @staticmethod
        def get_default_options() -> ScipyParserOptions:
            return ScipyParserOptions()

        def parse_message(self, message: str) -> ParsedMessageResult | None:
            return (
                None
                if not (pmsg_result := super().parse_message(message))
                else ParsedMessageResult(
                    **{
                        **pmsg_result._asdict(),
                        "category": tag_to_section.get(pmsg_result.type, "None"),
                    }
                )
            )