diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..ef07676fb6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 +updates: + - + # Maintain dependencies for GitHub Actions + package-ecosystem: github-actions + directory: / + schedule: + interval: daily + labels: + - dependencies + commit-message: + prefix: "ci(actions)" + include: "scope" + - + # Maintain python dependencies + package-ecosystem: pip + directory: / + schedule: + interval: daily + labels: + - dependencies + commit-message: + prefix: "build(poetry)" + include: "scope" diff --git a/.github/workflows/bumpversion.yml b/.github/workflows/bumpversion.yml index b213c6047d..58bc6b4850 100644 --- a/.github/workflows/bumpversion.yml +++ b/.github/workflows/bumpversion.yml @@ -20,3 +20,11 @@ jobs: uses: commitizen-tools/commitizen-action@master with: github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + changelog_increment_filename: body.md + - name: Release + uses: softprops/action-gh-release@v1 + with: + body_path: "body.md" + tag_name: ${{ env.REVISION }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index e505aa58ad..656abae054 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -6,7 +6,7 @@ jobs: python-check: strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] platform: [ubuntu-20.04, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: @@ -30,7 +30,7 @@ jobs: shell: bash - name: Upload coverage to Codecov if: runner.os == 'Linux' - uses: codecov/codecov-action@v1.0.3 + uses: codecov/codecov-action@v3 with: token: ${{secrets.CODECOV_TOKEN}} file: ./coverage.xml diff --git a/.gitignore b/.gitignore index 987c1a9c60..7eede0c966 100644 --- a/.gitignore +++ b/.gitignore @@ -108,8 +108,8 @@ venv.bak/ .vscode/ *.bak -# build -poetry.lock - # macOSX .DS_Store + +# ruff +.ruff_cache \ No newline at end of file diff --git a/commitizen/bump.py b/commitizen/bump.py index 766ce1fbd2..c405414e87 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -1,9 +1,11 @@ import os import re +import sys +import typing from collections import OrderedDict from itertools import zip_longest from string import Template -from typing import List, Optional, Tuple, Union +from typing import List, Optional, Tuple, Type, Union from packaging.version import Version @@ -11,6 +13,12 @@ from commitizen.exceptions import CurrentVersionNotFoundError from commitizen.git import GitCommit, smart_open +if sys.version_info >= (3, 8): + from commitizen.version_types import VersionProtocol +else: + # workaround mypy issue for 3.7 python + VersionProtocol = typing.Any + def find_increment( commits: List[GitCommit], regex: str, increments_map: Union[dict, OrderedDict] @@ -120,7 +128,8 @@ def generate_version( prerelease_offset: int = 0, devrelease: Optional[int] = None, is_local_version: bool = False, -) -> Version: + version_type_cls: Optional[Type[VersionProtocol]] = None, +) -> VersionProtocol: """Based on the given increment a proper semver will be generated. For now the rules and versioning scheme is based on @@ -132,15 +141,17 @@ def generate_version( MINOR 1.0.0 -> 1.1.0 MAJOR 1.0.0 -> 2.0.0 """ + if version_type_cls is None: + version_type_cls = Version if is_local_version: - version = Version(current_version) + version = version_type_cls(current_version) dev_version = devrelease_generator(devrelease=devrelease) pre_version = prerelease_generator( str(version.local), prerelease=prerelease, offset=prerelease_offset ) semver = semver_generator(str(version.local), increment=increment) - return Version(f"{version.public}+{semver}{pre_version}{dev_version}") + return version_type_cls(f"{version.public}+{semver}{pre_version}{dev_version}") else: dev_version = devrelease_generator(devrelease=devrelease) pre_version = prerelease_generator( @@ -149,7 +160,7 @@ def generate_version( semver = semver_generator(current_version, increment=increment) # TODO: post version - return Version(f"{semver}{pre_version}{dev_version}") + return version_type_cls(f"{semver}{pre_version}{dev_version}") def update_version_in_files( @@ -208,7 +219,9 @@ def _version_to_regex(version: str) -> str: def normalize_tag( - version: Union[Version, str], tag_format: Optional[str] = None + version: Union[VersionProtocol, str], + tag_format: Optional[str] = None, + version_type_cls: Optional[Type[VersionProtocol]] = None, ) -> str: """The tag and the software version might be different. @@ -221,8 +234,10 @@ def normalize_tag( | ver1.0.0 | 1.0.0 | | ver1.0.0.a0 | 1.0.0a0 | """ + if version_type_cls is None: + version_type_cls = Version if isinstance(version, str): - version = Version(version) + version = version_type_cls(version) if not tag_format: return str(version) diff --git a/commitizen/changelog.py b/commitizen/changelog.py index ead80ab775..1a6ca04cc1 100644 --- a/commitizen/changelog.py +++ b/commitizen/changelog.py @@ -27,22 +27,56 @@ import os import re +import sys +import typing from collections import OrderedDict, defaultdict from datetime import date -from typing import Callable, Dict, Iterable, List, Optional, Tuple +from typing import Callable, Dict, Iterable, List, Optional, Tuple, Type from jinja2 import Environment, PackageLoader +from packaging.version import InvalidVersion, Version from commitizen import defaults from commitizen.bump import normalize_tag from commitizen.exceptions import InvalidConfigurationError, NoCommitsFoundError from commitizen.git import GitCommit, GitTag +if sys.version_info >= (3, 8): + from commitizen.version_types import VersionProtocol +else: + # workaround mypy issue for 3.7 python + VersionProtocol = typing.Any + def get_commit_tag(commit: GitCommit, tags: List[GitTag]) -> Optional[GitTag]: return next((tag for tag in tags if tag.rev == commit.rev), None) +def get_version(tag: GitTag) -> Optional[Version]: + version = None + try: + version = Version(tag.name) + except InvalidVersion: + pass + return version + + +def tag_included_in_changelog( + tag: GitTag, used_tags: List, merge_prerelease: bool +) -> bool: + if tag in used_tags: + return False + + version = get_version(tag) + if version is None: + return False + + if merge_prerelease and version.is_prerelease: + return False + + return True + + def generate_tree_from_commits( commits: List[GitCommit], tags: List[GitTag], @@ -51,6 +85,7 @@ def generate_tree_from_commits( unreleased_version: Optional[str] = None, change_type_map: Optional[Dict[str, str]] = None, changelog_message_builder_hook: Optional[Callable] = None, + merge_prerelease: bool = False, ) -> Iterable[Dict]: pat = re.compile(changelog_pattern) map_pat = re.compile(commit_parser, re.MULTILINE) @@ -73,15 +108,15 @@ def generate_tree_from_commits( for commit in commits: commit_tag = get_commit_tag(commit, tags) - if commit_tag is not None and commit_tag not in used_tags: + if commit_tag is not None and tag_included_in_changelog( + commit_tag, used_tags, merge_prerelease + ): used_tags.append(commit_tag) yield { "version": current_tag_name, "date": current_tag_date, "changes": changes, } - # TODO: Check if tag matches the version pattern, otherwise skip it. - # This in order to prevent tags that are not versions. current_tag_name = commit_tag.name current_tag_date = commit_tag.date changes = defaultdict(list) @@ -286,7 +321,10 @@ def get_smart_tag_range( def get_oldest_and_newest_rev( - tags: List[GitTag], version: str, tag_format: str + tags: List[GitTag], + version: str, + tag_format: str, + version_type_cls: Optional[Type[VersionProtocol]] = None, ) -> Tuple[Optional[str], Optional[str]]: """Find the tags for the given version. @@ -301,11 +339,15 @@ def get_oldest_and_newest_rev( except ValueError: newest = version - newest_tag = normalize_tag(newest, tag_format=tag_format) + newest_tag = normalize_tag( + newest, tag_format=tag_format, version_type_cls=version_type_cls + ) oldest_tag = None if oldest: - oldest_tag = normalize_tag(oldest, tag_format=tag_format) + oldest_tag = normalize_tag( + oldest, tag_format=tag_format, version_type_cls=version_type_cls + ) tags_range = get_smart_tag_range(tags, newest=newest_tag, oldest=oldest_tag) if not tags_range: diff --git a/commitizen/cli.py b/commitizen/cli.py index 28f7e8d927..b59c257db3 100644 --- a/commitizen/cli.py +++ b/commitizen/cli.py @@ -2,12 +2,13 @@ import logging import sys from functools import partial +from types import TracebackType from typing import List import argcomplete from decli import cli -from commitizen import commands, config, out +from commitizen import commands, config, out, version_types from commitizen.exceptions import ( CommitizenException, ExitCode, @@ -202,6 +203,12 @@ "help": "bump to the given version (e.g: 1.5.3)", "metavar": "MANUAL_VERSION", }, + { + "name": ["--version-type"], + "help": "choose version type", + "default": None, + "choices": version_types.VERSION_TYPES, + }, ], }, { @@ -247,10 +254,19 @@ "name": "--start-rev", "default": None, "help": ( - "start rev of the changelog." + "start rev of the changelog. " "If not set, it will generate changelog from the start" ), }, + { + "name": "--merge-prerelease", + "action": "store_true", + "default": False, + "help": ( + "collect all changes from prereleases into next non-prerelease. " + "If not set, it will include prereleases in the changelog" + ), + }, ], }, { @@ -330,21 +346,22 @@ def commitizen_excepthook( - type, value, tracekback, debug=False, no_raise: List[int] = None + type, value, traceback, debug=False, no_raise: List[int] = None ): + traceback = traceback if isinstance(traceback, TracebackType) else None if not no_raise: no_raise = [] if isinstance(value, CommitizenException): if value.message: value.output_method(value.message) if debug: - original_excepthook(type, value, tracekback) + original_excepthook(type, value, traceback) exit_code = value.exit_code if exit_code in no_raise: exit_code = 0 sys.exit(exit_code) else: - original_excepthook(type, value, tracekback) + original_excepthook(type, value, traceback) commitizen_debug_excepthook = partial(commitizen_excepthook, debug=True) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 808141c32f..1477f79449 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -5,7 +5,7 @@ import questionary from packaging.version import InvalidVersion, Version -from commitizen import bump, cmd, defaults, factory, git, hooks, out +from commitizen import bump, cmd, defaults, factory, git, hooks, out, version_types from commitizen.commands.changelog import Changelog from commitizen.config import BaseConfig from commitizen.exceptions import ( @@ -21,6 +21,7 @@ NotAllowed, NoVersionSpecifiedError, ) +from commitizen.providers import get_provider logger = getLogger("commitizen") @@ -61,6 +62,10 @@ def __init__(self, config: BaseConfig, arguments: dict): self.retry = arguments["retry"] self.pre_bump_hooks = self.config.settings["pre_bump_hooks"] self.post_bump_hooks = self.config.settings["post_bump_hooks"] + version_type = arguments["version_type"] or self.config.settings.get( + "version_type" + ) + self.version_type = version_type and version_types.VERSION_TYPES[version_type] def is_initial_tag(self, current_tag_version: str, is_yes: bool = False) -> bool: """Check if reading the whole git tree up to HEAD is needed.""" @@ -94,14 +99,14 @@ def find_increment(self, commits: List[git.GitCommit]) -> Optional[str]: def __call__(self): # noqa: C901 """Steps executed to bump.""" + provider = get_provider(self.config) + current_version: str = provider.get_version() + try: - current_version_instance: Version = Version(self.bump_settings["version"]) + current_version_instance: Version = Version(current_version) except TypeError: raise NoVersionSpecifiedError() - # Initialize values from sources (conf) - current_version: str = self.config.settings["version"] - tag_format: str = self.bump_settings["tag_format"] bump_commit_message: str = self.bump_settings["bump_message"] version_files: List[str] = self.bump_settings["version_files"] @@ -152,7 +157,9 @@ def __call__(self): # noqa: C901 self.cz.bump_map = defaults.bump_map_major_version_zero current_tag_version: str = bump.normalize_tag( - current_version, tag_format=tag_format + current_version, + tag_format=tag_format, + version_type_cls=self.version_type, ) is_initial = self.is_initial_tag(current_tag_version, is_yes) @@ -208,9 +215,14 @@ def __call__(self): # noqa: C901 prerelease_offset=prerelease_offset, devrelease=devrelease, is_local_version=is_local_version, + version_type_cls=self.version_type, ) - new_tag_version = bump.normalize_tag(new_version, tag_format=tag_format) + new_tag_version = bump.normalize_tag( + new_version, + tag_format=tag_format, + version_type_cls=self.version_type, + ) message = bump.create_commit_message( current_version, new_version, bump_commit_message ) @@ -280,7 +292,7 @@ def __call__(self): # noqa: C901 check_consistency=self.check_consistency, ) - self.config.set_key("version", str(new_version)) + provider.set_version(str(new_version)) if self.pre_bump_hooks: hooks.run( diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index aab613fffc..4c291d3ac2 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -3,7 +3,7 @@ from operator import itemgetter from typing import Callable, Dict, List, Optional -from commitizen import bump, changelog, defaults, factory, git, out +from commitizen import bump, changelog, defaults, factory, git, out, version_types from commitizen.config import BaseConfig from commitizen.exceptions import ( DryRunExit, @@ -49,6 +49,12 @@ def __init__(self, config: BaseConfig, args): self.tag_format = args.get("tag_format") or self.config.settings.get( "tag_format" ) + self.merge_prerelease = args.get( + "merge_prerelease" + ) or self.config.settings.get("changelog_merge_prerelease") + + version_type = self.config.settings.get("version_type") + self.version_type = version_type and version_types.VERSION_TYPES[version_type] def _find_incremental_rev(self, latest_version: str, tags: List[GitTag]) -> str: """Try to find the 'start_rev'. @@ -110,6 +116,7 @@ def __call__(self): changelog_message_builder_hook: Optional[ Callable ] = self.cz.changelog_message_builder_hook + merge_prerelease = self.merge_prerelease if not changelog_pattern or not commit_parser: raise NoPatternMapError( @@ -133,7 +140,9 @@ def __call__(self): latest_version = changelog_meta.get("latest_version") if latest_version: latest_tag_version: str = bump.normalize_tag( - latest_version, tag_format=self.tag_format + latest_version, + tag_format=self.tag_format, + version_type_cls=self.version_type, ) start_rev = self._find_incremental_rev(latest_tag_version, tags) @@ -142,11 +151,10 @@ def __call__(self): tags, version=self.rev_range, tag_format=self.tag_format, + version_type_cls=self.version_type, ) - commits = git.get_commits( - start=start_rev, end=end_rev, args="--author-date-order" - ) + commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order") if not commits: raise NoCommitsFoundError("No commits found") @@ -158,6 +166,7 @@ def __call__(self): unreleased_version, change_type_map=change_type_map, changelog_message_builder_hook=changelog_message_builder_hook, + merge_prerelease=merge_prerelease, ) if self.change_type_order: tree = changelog.order_changelog_tree(tree, self.change_type_order) diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index 91b5bd4b70..fe6167b7f7 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -13,59 +13,152 @@ from commitizen.defaults import config_files from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open +from commitizen.version_types import VERSION_TYPES + + +class ProjectInfo: + """Discover information about the current folder.""" + + @property + def has_pyproject(self) -> bool: + return os.path.isfile("pyproject.toml") + + @property + def has_setup(self) -> bool: + return os.path.isfile("setup.py") + + @property + def has_pre_commit_config(self) -> bool: + return os.path.isfile(".pre-commit-config.yaml") + + @property + def is_python_poetry(self) -> bool: + if not self.has_pyproject: + return False + with open("pyproject.toml") as f: + return "tool.poetry.version" in f.read() + + @property + def is_python(self) -> bool: + return self.has_pyproject or self.has_setup + + @property + def is_rust_cargo(self) -> bool: + return os.path.isfile("Cargo.toml") + + @property + def is_npm_package(self) -> bool: + return os.path.isfile("package.json") + + @property + def is_php_composer(self) -> bool: + return os.path.isfile("composer.json") + + @property + def latest_tag(self) -> Optional[str]: + return get_latest_tag_name() + + def tags(self) -> Optional[List]: + """Not a property, only use if necessary""" + if self.latest_tag is None: + return None + return get_tag_names() + + @property + def is_pre_commit_installed(self) -> bool: + return not shutil.which("pre-commit") class Init: def __init__(self, config: BaseConfig, *args): self.config: BaseConfig = config self.cz = factory.commiter_factory(self.config) + self.project_info = ProjectInfo() def __call__(self): if self.config.path: out.line(f"Config file {self.config.path} already exists") return - # No config for commitizen exist - config_path = self._ask_config_path() + out.info("Welcome to commitizen!\n") + out.line( + "Answer the questions to configure your project.\n" + "For further configuration visit:\n" + "\n" + "https://commitizen-tools.github.io/commitizen/config/" + "\n" + ) + + # Collect information + try: + config_path = self._ask_config_path() # select + cz_name = self._ask_name() # select + version_provider = self._ask_version_provider() # select + tag = self._ask_tag() # confirm & select + version = Version(tag) + tag_format = self._ask_tag_format(tag) # confirm & text + version_type = self._ask_version_type() # select + update_changelog_on_bump = self._ask_update_changelog_on_bump() # confirm + major_version_zero = self._ask_major_version_zero(version) # confirm + except KeyboardInterrupt: + raise InitFailedError("Stopped by user") + + # Initialize configuration if "toml" in config_path: self.config = TomlConfig(data="", path=config_path) elif "json" in config_path: self.config = JsonConfig(data="{}", path=config_path) elif "yaml" in config_path: self.config = YAMLConfig(data="", path=config_path) - self.config.init_empty_config_content() - values_to_add = {} - values_to_add["name"] = self._ask_name() - tag = self._ask_tag() - values_to_add["version"] = Version(tag).public - values_to_add["tag_format"] = self._ask_tag_format(tag) - self._update_config_file(values_to_add) + values_to_add["name"] = cz_name + values_to_add["tag_format"] = tag_format + values_to_add["version_type"] = version_type + if version_provider == "commitizen": + values_to_add["version"] = version.public + else: + values_to_add["version_provider"] = version_provider + + if update_changelog_on_bump: + values_to_add["update_changelog_on_bump"] = update_changelog_on_bump + + if major_version_zero: + values_to_add["major_version_zero"] = major_version_zero + + # Collect hook data hook_types = questionary.checkbox( "What types of pre-commit hook you want to install? (Leave blank if you don't want to install)", choices=[ - questionary.Choice("commit-msg", checked=True), - questionary.Choice("pre-push", checked=True), + questionary.Choice("commit-msg", checked=False), + questionary.Choice("pre-push", checked=False), ], - ).ask() + ).unsafe_ask() if hook_types: try: self._install_pre_commit_hook(hook_types) except InitFailedError as e: raise InitFailedError(f"Failed to install pre-commit hook.\n{e}") - out.write("You can bump the version and create changelog running:\n") - out.info("cz bump --changelog") - out.success("The configuration are all set.") + # Create and initialize config + self.config.init_empty_config_content() + self._update_config_file(values_to_add) + + out.write("\nYou can bump the version running:\n") + out.info("\tcz bump\n") + out.success("Configuration complete 🚀") def _ask_config_path(self) -> str: + default_path = ".cz.toml" + if self.project_info.has_pyproject: + default_path = "pyproject.toml" + name: str = questionary.select( - "Please choose a supported config file: (default: pyproject.toml)", + "Please choose a supported config file: ", choices=config_files, - default="pyproject.toml", + default=default_path, style=self.cz.style, - ).ask() + ).unsafe_ask() return name def _ask_name(self) -> str: @@ -74,29 +167,29 @@ def _ask_name(self) -> str: choices=list(registry.keys()), default="cz_conventional_commits", style=self.cz.style, - ).ask() + ).unsafe_ask() return name def _ask_tag(self) -> str: - latest_tag = get_latest_tag_name() + latest_tag = self.project_info.latest_tag if not latest_tag: out.error("No Existing Tag. Set tag to v0.0.1") return "0.0.1" is_correct_tag = questionary.confirm( f"Is {latest_tag} the latest tag?", style=self.cz.style, default=False - ).ask() + ).unsafe_ask() if not is_correct_tag: - tags = get_tag_names() + tags = self.project_info.tags() if not tags: out.error("No Existing Tag. Set tag to v0.0.1") return "0.0.1" latest_tag = questionary.select( "Please choose the latest tag: ", - choices=get_tag_names(), # type: ignore + choices=tags, style=self.cz.style, - ).ask() + ).unsafe_ask() if not latest_tag: raise NoAnswersError("Tag is required!") @@ -108,21 +201,90 @@ def _ask_tag_format(self, latest_tag) -> str: tag_format = r"v$version" is_correct_format = questionary.confirm( f'Is "{tag_format}" the correct tag format?', style=self.cz.style - ).ask() + ).unsafe_ask() if not is_correct_format: tag_format = questionary.text( 'Please enter the correct version format: (default: "$version")', style=self.cz.style, - ).ask() + ).unsafe_ask() if not tag_format: tag_format = "$version" return tag_format - def _search_pre_commit(self) -> bool: - """Check whether pre-commit is installed""" - return shutil.which("pre-commit") is not None + def _ask_version_provider(self) -> str: + """Ask for setting: version_provider""" + + OPTS = { + "commitizen": "commitizen: Fetch and set version in commitizen config (default)", + "cargo": "cargo: Get and set version from Cargo.toml:project.version field", + "composer": "composer: Get and set version from composer.json:project.version field", + "npm": "npm: Get and set version from package.json:project.version field", + "pep621": "pep621: Get and set version from pyproject.toml:project.version field", + "poetry": "poetry: Get and set version from pyproject.toml:tool.poetry.version field", + "scm": "scm: Fetch the version from git and does not need to set it back", + } + + default_val = "commitizen" + if self.project_info.is_python: + if self.project_info.is_python_poetry: + default_val = "poetry" + else: + default_val = "pep621" + elif self.project_info.is_rust_cargo: + default_val = "cargo" + elif self.project_info.is_npm_package: + default_val = "npm" + elif self.project_info.is_php_composer: + default_val = "composer" + + choices = [ + questionary.Choice(title=title, value=value) + for value, title in OPTS.items() + ] + default = next(filter(lambda x: x.value == default_val, choices)) + version_provider: str = questionary.select( + "Choose the source of the version:", + choices=choices, + style=self.cz.style, + default=default, + ).unsafe_ask() + return version_provider + + def _ask_version_type(self) -> str: + """Ask for setting: version_type""" + default = "semver" + if self.project_info.is_python: + default = "pep440" + + version_type: str = questionary.select( + "Choose version type scheme: ", + choices=[*VERSION_TYPES], + style=self.cz.style, + default=default, + ).unsafe_ask() + return version_type + + def _ask_major_version_zero(self, version: Version) -> bool: + """Ask for setting: major_version_zero""" + if version.major > 0: + return False + major_version_zero: bool = questionary.confirm( + "Keep major version zero (0.x) during breaking changes", + default=True, + auto_enter=True, + ).unsafe_ask() + return major_version_zero + + def _ask_update_changelog_on_bump(self) -> bool: + "Ask for setting: update_changelog_on_bump" + update_changelog_on_bump: bool = questionary.confirm( + "Create changelog automatically on bump", + default=True, + auto_enter=True, + ).unsafe_ask() + return update_changelog_on_bump def _exec_install_pre_commit_hook(self, hook_types: List[str]): cmd_str = self._gen_pre_commit_cmd(hook_types) @@ -157,7 +319,7 @@ def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None): } config_data = {} - if not os.path.isfile(pre_commit_config_filename): + if not self.project_info.has_pre_commit_config: # .pre-commit-config.yaml does not exist config_data["repos"] = [cz_hook_config] else: @@ -180,10 +342,8 @@ def _install_pre_commit_hook(self, hook_types: Optional[List[str]] = None): with smart_open(pre_commit_config_filename, "w") as config_file: yaml.safe_dump(config_data, stream=config_file) - if not self._search_pre_commit(): - raise InitFailedError( - "pre-commit is not installed in current environement." - ) + if not self.project_info.is_pre_commit_installed: + raise InitFailedError("pre-commit is not installed in current environment.") if hook_types is None: hook_types = ["commit-msg", "pre-push"] self._exec_install_pre_commit_hook(hook_types) diff --git a/commitizen/commands/version.py b/commitizen/commands/version.py index dc47e7aa0c..45d553c710 100644 --- a/commitizen/commands/version.py +++ b/commitizen/commands/version.py @@ -4,6 +4,7 @@ from commitizen import out from commitizen.__version__ import __version__ from commitizen.config import BaseConfig +from commitizen.providers import get_provider class Version: @@ -21,14 +22,14 @@ def __call__(self): out.write(f"Python Version: {self.python_version}") out.write(f"Operating System: {self.operating_system}") elif self.parameter.get("project"): - version = self.config.settings["version"] + version = get_provider(self.config).get_version() if version: out.write(f"{version}") else: out.error("No project information in this project.") elif self.parameter.get("verbose"): out.write(f"Installed Commitizen Version: {__version__}") - version = self.config.settings["version"] + version = get_provider(self.config).get_version() if version: out.write(f"Project Version: {version}") else: diff --git a/commitizen/cz/__init__.py b/commitizen/cz/__init__.py index e14cb9f7c9..5b974a99f2 100644 --- a/commitizen/cz/__init__.py +++ b/commitizen/cz/__init__.py @@ -1,15 +1,18 @@ +from __future__ import annotations + import importlib import pkgutil import warnings -from typing import Dict, Iterable, Type +from typing import Iterable, Optional + +import importlib_metadata as metadata from commitizen.cz.base import BaseCommitizen -from commitizen.cz.conventional_commits import ConventionalCommitsCz -from commitizen.cz.customize import CustomizeCommitsCz -from commitizen.cz.jira import JiraSmartCz -def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitizen]]: +def discover_plugins( + path: Optional[Iterable[str]] = None, +) -> dict[str, type[BaseCommitizen]]: """Discover commitizen plugins on the path Args: @@ -19,21 +22,19 @@ def discover_plugins(path: Iterable[str] = None) -> Dict[str, Type[BaseCommitize Returns: Dict[str, Type[BaseCommitizen]]: Registry with found plugins """ - plugins = {} - for _finder, name, _ispkg in pkgutil.iter_modules(path): - try: - if name.startswith("cz_"): - plugins[name] = importlib.import_module(name).discover_this - except AttributeError as e: - warnings.warn(UserWarning(e.args[0])) - continue - return plugins - - -registry: Dict[str, Type[BaseCommitizen]] = { - "cz_conventional_commits": ConventionalCommitsCz, - "cz_jira": JiraSmartCz, - "cz_customize": CustomizeCommitsCz, -} - -registry.update(discover_plugins()) + for _, name, _ in pkgutil.iter_modules(path): + if name.startswith("cz_"): + mod = importlib.import_module(name) + if hasattr(mod, "discover_this"): + warnings.warn( + UserWarning( + f"Legacy plugin '{name}' has been ignored: please expose it the 'commitizen.plugin' entrypoint" + ) + ) + + return { + ep.name: ep.load() for ep in metadata.entry_points(group="commitizen.plugin") + } + + +registry: dict[str, type[BaseCommitizen]] = discover_plugins() diff --git a/commitizen/defaults.py b/commitizen/defaults.py index da029db87d..713cfc2a40 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -33,12 +33,14 @@ class Settings(TypedDict, total=False): name: str version: Optional[str] version_files: List[str] + version_provider: Optional[str] tag_format: Optional[str] bump_message: Optional[str] allow_abort: bool changelog_file: str changelog_incremental: bool changelog_start_rev: Optional[str] + changelog_merge_prerelease: bool update_changelog_on_bump: bool use_shortcuts: bool style: Optional[List[Tuple[str, str]]] @@ -47,6 +49,7 @@ class Settings(TypedDict, total=False): pre_bump_hooks: Optional[List[str]] post_bump_hooks: Optional[List[str]] prerelease_offset: int + version_type: Optional[str] name: str = "cz_conventional_commits" @@ -63,18 +66,21 @@ class Settings(TypedDict, total=False): "name": "cz_conventional_commits", "version": None, "version_files": [], + "version_provider": "commitizen", "tag_format": None, # example v$version "bump_message": None, # bumped v$current_version to $new_version "allow_abort": False, "changelog_file": "CHANGELOG.md", "changelog_incremental": False, "changelog_start_rev": None, + "changelog_merge_prerelease": False, "update_changelog_on_bump": False, "use_shortcuts": False, "major_version_zero": False, "pre_bump_hooks": [], "post_bump_hooks": [], "prerelease_offset": 0, + "version_type": None, } MAJOR = "MAJOR" diff --git a/commitizen/exceptions.py b/commitizen/exceptions.py index c7d0b50e69..ba4aca1397 100644 --- a/commitizen/exceptions.py +++ b/commitizen/exceptions.py @@ -31,6 +31,7 @@ class ExitCode(enum.IntEnum): INVALID_MANUAL_VERSION = 24 INIT_FAILED = 25 RUN_HOOK_FAILED = 26 + VERSION_PROVIDER_UNKNOWN = 27 class CommitizenException(Exception): @@ -173,3 +174,7 @@ class InitFailedError(CommitizenException): class RunHookError(CommitizenException): exit_code = ExitCode.RUN_HOOK_FAILED + + +class VersionProviderUnknown(CommitizenException): + exit_code = ExitCode.VERSION_PROVIDER_UNKNOWN diff --git a/commitizen/providers.py b/commitizen/providers.py new file mode 100644 index 0000000000..17b99e7b23 --- /dev/null +++ b/commitizen/providers.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +import json +import re +from abc import ABC, abstractmethod +from pathlib import Path +from typing import Any, Callable, ClassVar, Optional, cast + +import importlib_metadata as metadata +import tomlkit +from packaging.version import VERSION_PATTERN, Version + +from commitizen.config.base_config import BaseConfig +from commitizen.exceptions import VersionProviderUnknown +from commitizen.git import get_tags + +PROVIDER_ENTRYPOINT = "commitizen.provider" +DEFAULT_PROVIDER = "commitizen" + + +class VersionProvider(ABC): + """ + Abstract base class for version providers. + + Each version provider should inherit and implement this class. + """ + + config: BaseConfig + + def __init__(self, config: BaseConfig): + self.config = config + + @abstractmethod + def get_version(self) -> str: + """ + Get the current version + """ + ... + + @abstractmethod + def set_version(self, version: str): + """ + Set the new current version + """ + ... + + +class CommitizenProvider(VersionProvider): + """ + Default version provider: Fetch and set version in commitizen config. + """ + + def get_version(self) -> str: + return self.config.settings["version"] # type: ignore + + def set_version(self, version: str): + self.config.set_key("version", version) + + +class FileProvider(VersionProvider): + """ + Base class for file-based version providers + """ + + filename: ClassVar[str] + + @property + def file(self) -> Path: + return Path() / self.filename + + +class TomlProvider(FileProvider): + """ + Base class for TOML-based version providers + """ + + def get_version(self) -> str: + document = tomlkit.parse(self.file.read_text()) + return self.get(document) + + def set_version(self, version: str): + document = tomlkit.parse(self.file.read_text()) + self.set(document, version) + self.file.write_text(tomlkit.dumps(document)) + + def get(self, document: tomlkit.TOMLDocument) -> str: + return document["project"]["version"] # type: ignore + + def set(self, document: tomlkit.TOMLDocument, version: str): + document["project"]["version"] = version # type: ignore + + +class Pep621Provider(TomlProvider): + """ + PEP-621 version management + """ + + filename = "pyproject.toml" + + +class PoetryProvider(TomlProvider): + """ + Poetry version management + """ + + filename = "pyproject.toml" + + def get(self, pyproject: tomlkit.TOMLDocument) -> str: + return pyproject["tool"]["poetry"]["version"] # type: ignore + + def set(self, pyproject: tomlkit.TOMLDocument, version: str): + pyproject["tool"]["poetry"]["version"] = version # type: ignore + + +class CargoProvider(TomlProvider): + """ + Cargo version management + """ + + filename = "Cargo.toml" + + def get(self, document: tomlkit.TOMLDocument) -> str: + return document["package"]["version"] # type: ignore + + def set(self, document: tomlkit.TOMLDocument, version: str): + document["package"]["version"] = version # type: ignore + + +class JsonProvider(FileProvider): + """ + Base class for JSON-based version providers + """ + + indent: ClassVar[int] = 2 + + def get_version(self) -> str: + document = json.loads(self.file.read_text()) + return self.get(document) + + def set_version(self, version: str): + document = json.loads(self.file.read_text()) + self.set(document, version) + self.file.write_text(json.dumps(document, indent=self.indent) + "\n") + + def get(self, document: dict[str, Any]) -> str: + return document["version"] # type: ignore + + def set(self, document: dict[str, Any], version: str): + document["version"] = version + + +class NpmProvider(JsonProvider): + """ + npm package.json version management + """ + + filename = "package.json" + + +class ComposerProvider(JsonProvider): + """ + Composer version management + """ + + filename = "composer.json" + indent = 4 + + +class ScmProvider(VersionProvider): + """ + A provider fetching the current/last version from the repository history + + The version is fetched using `git describe` and is never set. + + It is meant for `setuptools-scm` or any package manager `*-scm` provider. + """ + + TAG_FORMAT_REGEXS = { + "$version": r"(?P.+)", + "$major": r"(?P\d+)", + "$minor": r"(?P\d+)", + "$patch": r"(?P\d+)", + "$prerelease": r"(?P\w+\d+)?", + "$devrelease": r"(?P\.dev\d+)?", + } + + def _tag_format_matcher(self) -> Callable[[str], Optional[str]]: + pattern = self.config.settings.get("tag_format") or VERSION_PATTERN + for var, tag_pattern in self.TAG_FORMAT_REGEXS.items(): + pattern = pattern.replace(var, tag_pattern) + + regex = re.compile(f"^{pattern}$", re.VERBOSE) + + def matcher(tag: str) -> Optional[str]: + match = regex.match(tag) + if not match: + return None + groups = match.groupdict() + if "version" in groups: + return groups["version"] + elif "major" in groups: + return "".join( + ( + groups["major"], + f".{groups['minor']}" if groups.get("minor") else "", + f".{groups['patch']}" if groups.get("patch") else "", + groups["prerelease"] if groups.get("prerelease") else "", + groups["devrelease"] if groups.get("devrelease") else "", + ) + ) + elif pattern == VERSION_PATTERN: + return str(Version(tag)) + return None + + return matcher + + def get_version(self) -> str: + matcher = self._tag_format_matcher() + return next( + (cast(str, matcher(t.name)) for t in get_tags() if matcher(t.name)), "0.0.0" + ) + + def set_version(self, version: str): + # Not necessary + pass + + +def get_provider(config: BaseConfig) -> VersionProvider: + """ + Get the version provider as defined in the configuration + + :raises VersionProviderUnknown: if the provider named by `version_provider` is not found. + """ + provider_name = config.settings["version_provider"] or DEFAULT_PROVIDER + try: + (ep,) = metadata.entry_points(name=provider_name, group=PROVIDER_ENTRYPOINT) + except ValueError: + raise VersionProviderUnknown(f'Version Provider "{provider_name}" unknown.') + provider_cls = ep.load() + return cast(VersionProvider, provider_cls(config)) diff --git a/commitizen/version_types.py b/commitizen/version_types.py new file mode 100644 index 0000000000..39a965e0c3 --- /dev/null +++ b/commitizen/version_types.py @@ -0,0 +1,99 @@ +import sys +from typing import Optional, Tuple, Union + +if sys.version_info >= (3, 8): + from typing import Protocol as _Protocol +else: + _Protocol = object + +from packaging.version import Version + + +class VersionProtocol(_Protocol): + def __init__(self, _version: Union[Version, str]): + raise NotImplementedError("must be implemented") + + def __str__(self) -> str: + raise NotImplementedError("must be implemented") + + @property + def release(self) -> Tuple[int, ...]: + raise NotImplementedError("must be implemented") + + @property + def is_prerelease(self) -> bool: + raise NotImplementedError("must be implemented") + + @property + def pre(self) -> Optional[Tuple[str, int]]: + raise NotImplementedError("must be implemented") + + @property + def local(self) -> Optional[str]: + raise NotImplementedError("must be implemented") + + @property + def public(self) -> str: + raise NotImplementedError("must be implemented") + + +class SemVerVersion(VersionProtocol): + def __init__(self, version: str): + self._version = Version(version) + + @property + def release(self) -> Tuple[int, ...]: + return self._version.release + + @property + def is_prerelease(self) -> bool: + return self._version.is_prerelease + + @property + def pre(self) -> Optional[Tuple[str, int]]: + return self._version.pre + + @property + def local(self) -> Optional[str]: + return self._version.local + + @property + def public(self) -> str: + return self._version.public + + def __str__(self) -> str: + parts = [] + + version = self._version + + # Epoch + if version.epoch != 0: + parts.append(f"{version.epoch}!") + + # Release segment + parts.append(".".join(str(x) for x in version.release)) + + # Pre-release + if version.pre: + pre = "".join(str(x) for x in version.pre) + parts.append(f"-{pre}") + + # Post-release + if version.post is not None: + parts.append(f"-post{version.post}") + + # Development release + if version.dev is not None: + parts.append(f"-dev{version.dev}") + + # Local version segment + if version.local: + parts.append(f"+{version.local}") + + return "".join(parts) + + +VERSION_TYPES = { + "semver": SemVerVersion, + "pep440": Version, +} diff --git a/docs/README.md b/docs/README.md index a167f5d775..c839ddda13 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,37 +17,41 @@ ## About -Commitizen is a tool designed for teams. +Commitizen is release management tool designed for teams. -Its main purpose is to define a standard way of committing rules -and communicating it (using the cli provided by commitizen). +Commitizen assumes your team uses a standard way of commiting rules +and from that foundation, it can bump your project's version, create +the changelog, and update files. -The reasoning behind it is that it is easier to read, and enforces writing -descriptive commits. +By default, commitizen uses [conventional commits][conventional_commits], but you +can build your own set of rules, and publish them. -Besides that, having a convention on your commits makes it possible to -parse them and use them for something else, like generating automatically -the version or a changelog. +Using a standarized set of rules to write commits, makes commits easier to read, and enforces writing +descriptive commits. -### Commitizen features +### Features - Command-line utility to create commits with your rules. Defaults: [Conventional commits][conventional_commits] -- Display information about your commit rules (commands: schema, example, info) - Bump version automatically using [semantic versioning][semver] based on the commits. [Read More](./bump.md) - Generate a changelog using [Keep a changelog][keepchangelog] +- Update your project's version files automatically +- Display information about your commit rules (commands: schema, example, info) +- Create your own set of rules and publish them to pip. Read more on [Customization](./customization.md) ## Requirements -Python 3.6+ +[Python](https://www.python.org/downloads/) `3.7+` + +[Poetry](https://python-poetry.org/docs/) `1.2.0+` -[Git][gitscm] `1.8.5.2`+ +[Git][gitscm] `1.8.5.2+` ## Installation -Global installation +To make commitizen available in your system ```bash -sudo pip3 install -U Commitizen +pip install --user -U Commitizen ``` ### Python project @@ -72,7 +76,7 @@ poetry add commitizen --dev ### macOS -On macOS, it can also be installed via [homebrew](https://formulae.brew.sh/formula/commitizen): +via [homebrew](https://formulae.brew.sh/formula/commitizen): ```bash brew install commitizen @@ -80,71 +84,19 @@ brew install commitizen ## Usage -### Committing - -Run in your terminal - -```bash -cz commit -``` - -or the shortcut - -```bash -cz c -``` - -#### Sign off the commit - -Run in the terminal - -```bash -cz commit --signoff -``` - -or the shortcut - -```bash -cz commit -s -``` - -### Integrating with Pre-commit - -Commitizen can lint your commit message for you with `cz check`. - -You can integrate this in your [pre-commit](https://pre-commit.com/) config with: - -```yaml ---- -repos: - - repo: https://github.com/commitizen-tools/commitizen - rev: master - hooks: - - id: commitizen - - id: commitizen-branch - stages: [push] -``` - -After the configuration is added, you'll need to run: +Most of the time this is the only command you'll run: ```sh -pre-commit install --hook-type commit-msg --hook-type pre-push +cz bump ``` -If you aren't using both hooks, you needn't install both stages. - -| Hook | Recommended Stage | -| ----------------- | ----------------- | -| commitizen | commit-msg | -| commitizen-branch | pre-push | - -Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with: +On top of that, you can use commitizen to assist you with the creation of commits: ```sh -pre-commit autoupdate +cz commit ``` -Read more about the `check` command [here](check.md). +Read more in the section [Getting Started](./getting_started.md). ### Help diff --git a/docs/bump.md b/docs/bump.md index ca5bdf7fe2..1c96fb43aa 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -2,15 +2,15 @@ ## About -The version is bumped **automatically** based on the commits. +`cz bump` **automatically** increases the version, based on the commits. -The commits should follow the rules of the committer to be parsed correctly. +The commits should follow the rules established by the committer in order to be parsed correctly. -It is possible to specify a **prerelease** (alpha, beta, release candidate) version. +**prerelease** versions are supported (alpha, beta, release candidate). The version can also be **manually** bumped. -The version format follows [semantic versioning][semver]. +The version format follows [PEP 0440][pep440] and [semantic versioning][semver]. This means `MAJOR.MINOR.PATCH` @@ -20,15 +20,13 @@ This means `MAJOR.MINOR.PATCH` | `MINOR` | New features | `feat` | | `PATCH` | Fixes | `fix` + everything else | -Prereleases are supported following python's [PEP 0440][pep440] +[PEP 0440][pep440] is the default, you can switch by using the setting `version_type` or the cli: -The scheme of this format is - -```bash -[N!]N(.N)*[{a|b|rc}N][.postN][.devN] +```sh +cz bump --version-type semver ``` -Some examples: +Some examples of pep440: ```bash 0.9.0 @@ -96,6 +94,9 @@ options: --retry retry commit if it fails the 1st time --major-version-zero keep major version at zero, even for breaking changes --prerelease-offset start pre-releases with this offset + --version-type {pep440,semver} + choose version type + ``` ### `--files-only` @@ -214,12 +215,38 @@ We recommend setting `major_version_zero = true` in your configuration file whil is in its initial development. Remove that configuration using a breaking-change commit to bump your project’s major version to `v1.0.0` once your project has reached maturity. +### `--version-type` + +Choose the version format, options: `pep440`, `semver`. + +Default: `pep440` + +Recommended for python: `pep440` + +Recommended for other: `semver` + +You can also set this in the [configuration](#version_type) with `version_type = "semver"`. + +[pep440][pep440] and [semver][semver] are quite similar, their difference lies in +how the prereleases look. + +| types | pep440 | semver | +| -------------- | -------------- | --------------- | +| non-prerelease | `0.1.0` | `0.1.0` | +| prerelease | `0.3.1a0` | `0.3.1-a0` | +| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | +| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | + +Can I transition from one to the other? + +Yes, you shouldn't have any issues. + ## Avoid raising errors -Some situations from commitizen rise an exit code different than 0. +Some situations from commitizen raise an exit code different than 0. If the error code is different than 0, any CI or script running commitizen might be interrupted. -If you have special use case, where you don't want one of this error codes to be raised, you can +If you have a special use case, where you don't want to raise one of this error codes, you can tell commitizen to not raise them. ### Recommended use case @@ -465,6 +492,8 @@ release. During execution of the script, some environment variables are availabl post_bump_hooks = [ "scripts/slack_notification.sh" ] +``` + ### `prerelease_offset` Offset with which to start counting prereleses. @@ -476,6 +505,26 @@ Defaults to: `0` prerelease_offset = 1 ``` +### `version_type` + +Choose version type + +| types | pep440 | semver | +| -------------- | -------------- | --------------- | +| non-prerelease | `0.1.0` | `0.1.0` | +| prerelease | `0.3.1a0` | `0.3.1-a0` | +| devrelease | `0.1.1.dev1` | `0.1.1-dev1` | +| dev and pre | `1.0.0a3.dev1` | `1.0.0-a3-dev1` | + +Options: `sever`, `pep440` + +Defaults to: `pep440` + +```toml +[tool.commitizen] +version_type = "semver" +``` + ## Custom bump Read the [customizing section](./customization.md). diff --git a/docs/changelog.md b/docs/changelog.md index 6f92bb21cd..d6799e198f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,13 @@ This command will generate a changelog following the committing rules established. +To create the changelog automatically on bump, add the setting [update_changelog_on_bump](./bump.md#update_changelog_on_bump) + +```toml +[tool.commitizen] +update_changelog_on_bump = true +``` + ## Usage ```bash @@ -21,7 +28,9 @@ optional arguments: set the value for the new version (use the tag value), instead of using unreleased --incremental generates changelog from last created version, useful if the changelog has been manually modified --start-rev START_REV - start rev of the changelog.If not set, it will generate changelog from the start + start rev of the changelog. If not set, it will generate changelog from the start + --merge-prerelease + collect all changes from prereleases into next non-prerelease. If not set, it will include prereleases in the changelog ``` ### Examples @@ -39,13 +48,13 @@ cz ch #### Get the changelog for the given version ```bash -cz changelog 0.3.0 +cz changelog 0.3.0 --dry-run ``` #### Get the changelog for the given version range ```bash -cz changelog 0.3.0..0.4.0 +cz changelog 0.3.0..0.4.0 --dry-run ``` ## Constrains @@ -161,6 +170,22 @@ cz changelog --start-rev="v0.2.0" changelog_start_rev = "v0.2.0" ``` +### merge-prerelease + +This flag can be set in the `toml` file with the key `changelog_merge_prerelease` under `tools.commitizen` + +Collects changes from prereleases into the next non-prerelease. This means that if you have a prerelease version, and then a normal release, the changelog will show the prerelease changes as part of the changes of the normal release. If not set, it will include prereleases in the changelog. + +```bash +cz changelog --merge-prerelease +``` + +```toml +[tools.commitizen] +# ... +changelog_merge_prerelease = true +``` + ## Hooks Supported hook methods: diff --git a/docs/check.md b/docs/check.md index 759d60d915..ca4f266b72 100644 --- a/docs/check.md +++ b/docs/check.md @@ -1,12 +1,14 @@ # Check ## About + This feature checks whether the commit message follows the given committing rules. And comment in git message will be ignored. If you want to setup an automatic check before every git commit, please refer to -[Automatically check message before commit](auto_check.md). +[Automatically check message before commit](./tutorials/auto_check.md). ## Usage + There are three mutually exclusive ways to use `cz check`: - with `--rev-range` to check a range of pre-existing commits @@ -14,6 +16,7 @@ There are three mutually exclusive ways to use `cz check`: - or with `--commit-msg-file` to read the commit message from a file ### Git Rev Range + If you'd like to check a commit's message after it has already been created, then you can specify the range of commits to check with `--rev-range REV_RANGE`. ```bash @@ -25,7 +28,9 @@ For example, if you'd like to check all commits on a branch, you can use `--rev- For more info on how git commit ranges work, you can check the [git documentation](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_commit_ranges). ### Commit Message + There are two ways you can provide your plain message and check it. + #### Method 1: use -m or --message ```bash @@ -49,7 +54,7 @@ $ cz check --commit-msg-file COMMIT_MSG_FILE ``` In this option, COMMIT_MSG_FILE is the path of the temporal file that contains the commit message. -This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](auto_check.md) for more information about how to use this argument with git hook. +This argument can be useful when cooperating with git hook, please check [Automatically check message before commit](./tutorials/auto_check.md) for more information about how to use this argument with git hook. ### Allow Abort diff --git a/docs/config.md b/docs/config.md index c6cf12b02f..3e5734a89a 100644 --- a/docs/config.md +++ b/docs/config.md @@ -2,27 +2,185 @@ ## Settings -| Variable | Type | Default | Description | -| -------------------------- | ------ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | `str` | `"cz_conventional_commits"` | Name of the committing rules to use | -| `version` | `str` | `None` | Current version. Example: "0.1.2" | -| `version_files` | `list` | `[ ]` | Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [See more][version_files] | -| `tag_format` | `str` | `None` | Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [See more][tag_format] | -| `update_changelog_on_bump` | `bool` | `false` | Create changelog when running `cz bump` | -| `gpg_sign` | `bool` | `false` | Use gpg signed tags instead of lightweight tags. | -| `annotated_tag` | `bool` | `false` | Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] | -| `bump_message` | `str` | `None` | Create custom commit message, useful to skip ci. [See more][bump_message] | -| `allow_abort` | `bool` | `false` | Disallow empty commit messages, useful in ci. [See more][allow_abort] | -| `changelog_file` | `str` | `CHANGELOG.md` | filename of exported changelog | -| `changelog_incremental` | `bool` | `false` | Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` | -| `changelog_start_rev` | `str` | `None` | Start from a given git rev to generate the changelog | -| `style` | `list` | see above | Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] | -| `customize` | `dict` | `None` | **This is only supported when config through `toml`.** Custom rules for committing and bumping. [See more][customization] | -| `use_shortcuts` | `bool` | `false` | If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [See more][shortcuts] | -| `major_version_zero` | `bool` | `false` | When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] | -| `prerelease_offset` | `int` | `0` | In special cases it may be necessary that a prerelease cannot start with a 0, e.g. in an embedded project the individual characters are encoded in bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset] | - -## pyproject.toml or .cz.toml +### `name` + +Type: `str` + +Default: `"cz_conventional_commits"` + +Name of the committing rules to use + +### `version` + +Type: `str` + +Default: `None` + +Current version. Example: "0.1.2". Required if you use `provider = "commitizen"`. + +### `version_files` + +Type: `list` + +Default: `[ ]` + +Files were the version will be updated. A pattern to match a line, can also be specified, separated by `:` [Read more][version_files] + +### `version_provider` + +Type: `str` + +Default: `commitizen` + +Version provider used to read and write version [Read more](#version-providers) + +### `tag_format` + +Type: `str` + +Default: `None` + +Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] + +### `update_changelog_on_bump` + +Type: `bool` + +Default: `false` + +Create changelog when running `cz bump` + +### `gpg_sign` + +Type: `bool` + +Default: `false` + +Use gpg signed tags instead of lightweight tags. + +### `annotated_tag` + +Type: `bool` + +Default: `false` + +Use annotated tags instead of lightweight tags. [See difference][annotated-tags-vs-lightweight] + +### `bump_message` + +Type: `str` + +Default: `None` + +Create custom commit message, useful to skip ci. [Read more][bump_message] + +### `allow_abort` + +Type: `bool` + +Default: `false` + +Disallow empty commit messages, useful in ci. [Read more][allow_abort] + +### `changelog_file` + +Type: `str` + +Default: `CHANGELOG.md` + +Filename of exported changelog + +### `changelog_incremental` + +Type: `bool` + +Default: `false` + +Update changelog with the missing versions. This is good if you don't want to replace previous versions in the file. Note: when doing `cz bump --changelog` this is automatically set to `true` + +### `changelog_start_rev` + +Type: `str` + +Default: `None` + +Start from a given git rev to generate the changelog + +### `changelog_merge_prerelease` + +Type: `bool` + +Default: `false` + +Collect all changes of prerelease versions into the next non-prerelease version when creating the changelog. + +### `style` + +Type: `list` + +see above + +Style for the prompts (It will merge this value with default style.) [See More (Styling your prompts with your favorite colors)][additional-features] + +### `customize` + +Type: `dict` + +Default: `None` + +**This is only supported when config through `toml`.** Custom rules for committing and bumping. [Read more][customization] + +### `use_shortcuts` + +Type: `bool` + +Default: `false` + +If enabled, commitizen will show keyboard shortcuts when selecting from a list. Define a `key` for each of your choices to set the key. [Read more][shortcuts] + +### `major_version_zero` + +Type: `bool` + +Default: `false` + +When true, breaking changes on a `0.x` will remain as a `0.x` version. On `false`, a breaking change will bump a `0.x` version to `1.0`. [major-version-zero] + +### `prerelease_offset` + +Type: `int` + +Default: `0` + +In some circumstances, a prerelease cannot start with a 0, e.g. in an embedded project individual characters are encoded as bytes. This can be done by specifying an offset from which to start counting. [prerelease-offset] | + +### `version_type` + +Type: `str` + +Default: `pep440` + +Select a version type from the following options [`pep440`, `semver`]. Useful for non-python projects. [Read more][version_type] + +### `pre_bump_hooks` + +Type: `list[str]` + +Default: `[]` + +Calls the hook scripts **before** bumping version. [Read more][pre_bump_hooks] + +### `post_bump_hooks` + +Type: `list[str]` + +Default: `[]` + +Calls the hook scripts **after** bumping the version. [Read more][post_bump_hooks] + +## Configuration file + +### pyproject.toml or .cz.toml Default and recommended configuration format for a project. For a **python** project, we recommend adding an entry to your `pyproject.toml`. @@ -38,6 +196,7 @@ version_files = [ "src/__version__.py", "pyproject.toml:version" ] +update_changelog_on_bump = true style = [ ["qmark", "fg:#ff9d00 bold"], ["question", "bold"], @@ -52,7 +211,7 @@ style = [ ] ``` -## .cz.json or cz.json +### .cz.json or cz.json Commitizen has support for JSON configuration. Recommended for `NodeJS` projects. @@ -78,7 +237,7 @@ Commitizen has support for JSON configuration. Recommended for `NodeJS` projects } ``` -## .cz.yaml or cz.yaml +### .cz.yaml or cz.yaml YAML configuration is supported by Commitizen. Recommended for `Go`, `ansible`, or even `helm` charts projects. @@ -112,12 +271,74 @@ commitizen: - fg:#858585 italic ``` +## Version providers + +Commitizen can read and write version from different sources. +By default, it use the `commitizen` one which is using the `version` field from the commitizen settings. +But you can use any `commitizen.provider` entrypoint as value for `version_provider`. + +Commitizen provides some version providers for some well known formats: + +| name | description | +| ------------ | --------------------------------------------------------------------- | +| `commitizen` | Default version provider: Fetch and set version in commitizen config. | +| `scm` | Fetch the version from git and does not need to set it back | +| `pep621` | Get and set version from `pyproject.toml` `project.version` field | +| `poetry` | Get and set version from `pyproject.toml` `tool.poetry.version` field | +| `cargo` | Get and set version from `Cargo.toml` `project.version` field | +| `npm` | Get and set version from `package.json` `project.version` field | +| `composer` | Get and set version from `composer.json` `project.version` field | + +!!! note +The `scm` provider is meant to be used with `setuptools-scm` or any packager `*-scm` plugin. + +### Custom version provider + +You can add you own version provider by extending `VersionProvider` and exposing it on the `commitizen.provider` entrypoint. + +Here a quick example of a `my-provider` provider reading and writing version in a `VERSION` file. + +```python title="my_provider.py" +from pathlib import Path +from commitizen.providers import VersionProvider + + +class MyProvider(VersionProvider): + file = Path() / "VERSION" + + def get_version(self) -> str: + return self.file.read_text() + + def set_version(self, version: str): + self.file.write_text(version) + +``` + +```python title="setup.py" +from setuptools import setup + +setup( + name='my-commitizen-provider', + version='0.1.0', + py_modules=['my_provider'], + install_requires=['commitizen'], + entry_points = { + 'commitizen.provider': [ + 'my-provider = my_provider:MyProvider', + ] + } +) +``` + [version_files]: bump.md#version_files [tag_format]: bump.md#tag_format [bump_message]: bump.md#bump_message [major-version-zero]: bump.md#-major-version-zero [prerelease-offset]: bump.md#-prerelease_offset [allow_abort]: check.md#allow-abort +[version_type]: bump.md#version_type +[pre_bump_hooks]: bump.md#pre_bump_hooks +[post_bump_hooks]: bump.md#post_bump_hooks [additional-features]: https://github.com/tmbo/questionary#additional-features [customization]: customization.md [shortcuts]: customization.md#shortcut-keys diff --git a/docs/contributing.md b/docs/contributing.md index ffda41e11b..6cf5877c48 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -22,7 +22,9 @@ If you're a first-time contributor, you can check the issues with [good first is (We use [CodeCov](https://codecov.io/) to ensure our test coverage does not drop.) 7. Use [commitizen](https://github.com/commitizen-tools/commitizen) to do git commit. We follow [conventional commmits][conventional-commmits] 8. Run `./scripts/format` and `./scripts/test` to ensure you follow the coding style and the tests pass. -9. Update `README.md`. Do **not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`. +9. Optionally, update the `README.md`. +9. **Do not** update the `CHANGELOG.md`, it will be automatically created after merging to `master`. +10. **Do not** update the versions in the project, they will be automatically updated. 10. If your changes are about documentation. Run `poetry run mkdocs serve` to serve documentation locally and check whether there is any warning or error. 11. Send a [pull request](https://github.com/commitizen-tools/commitizen/pulls) 🙏 diff --git a/docs/customization.md b/docs/customization.md index 053dbc1230..3ed8bd30ba 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -190,8 +190,8 @@ The basic steps are: 1. Inheriting from `BaseCommitizen` 2. Give a name to your rules. -3. Expose the class at the end of your file assigning it to `discover_this` -4. Create a python package starting with `cz_` using `setup.py`, `poetry`, etc +3. Create a python package using `setup.py`, `poetry`, etc +4. Expose the class as a `commitizen.plugin` entrypoint Check an [example](convcomms) on how to configure `BaseCommitizen`. @@ -201,11 +201,13 @@ You can also automate the steps above through [cookiecutter](https://cookiecutte cookiecutter gh:commitizen-tools/commitizen_cz_template ``` -See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for detail. +See [commitizen_cz_template](https://github.com/commitizen-tools/commitizen_cz_template) for details. + +Once you publish your rules, you can send us a PR to the [Third-party section](./third-party-commitizen.md). ### Custom commit rules -Create a file starting with `cz_`, for example `cz_jira.py`. This prefix is used to detect the plug-in. Same method [flask uses] +Create a Python module, for example `cz_jira.py`. Inherit from `BaseCommitizen`, and you must define `questions` and `message`. The others are optional. @@ -257,8 +259,6 @@ class JiraCz(BaseCommitizen): """ return 'We use this because is useful' - -discover_this = JiraCz # used by the plug-in system ``` The next file required is `setup.py` modified from flask version. @@ -272,7 +272,12 @@ setup( py_modules=['cz_jira'], license='MIT', long_description='this is a long description', - install_requires=['commitizen'] + install_requires=['commitizen'], + entry_points = { + 'commitizen.plugin': [ + 'cz_jira = cz_jira:JiraCz' + ] + } ) ``` @@ -287,8 +292,6 @@ doing `pip install .` If you feel like it should be part of this repo, create a PR. -[flask uses]: http://flask.pocoo.org/docs/0.12/extensiondev/ - ### Custom bump rules You need to define 2 parameters inside your custom `BaseCommitizen`. @@ -381,3 +384,56 @@ from commitizen.cz.exception import CzException class NoSubjectProvidedException(CzException): ... ``` + +### Migrating from legacy plugin format + +Commitizen migrated to a new plugin format relying on `importlib.metadata.EntryPoint`. +Migration should be straight-forward for legacy plugins: + +- Remove the `discover_this` line from you plugin module +- Expose the plugin class under as a `commitizen.plugin` entrypoint. + +The name of the plugin is now determined by the name of the entrypoint. + +#### Example + +If you were having a `CzPlugin` class in a `cz_plugin.py` module like this: + +```python +from commitizen.cz.base import BaseCommitizen + +class PluginCz(BaseCommitizen): + ... + +discover_this = PluginCz +``` + +Then remove the `discover_this` line: + +```python +from commitizen.cz.base import BaseCommitizen + +class PluginCz(BaseCommitizen): + ... +``` + +and expose the class as entrypoint in you setuptools: + +```python +from setuptools import setup + +setup( + name='MyPlugin', + version='0.1.0', + py_modules=['cz_plugin'], + ... + entry_points = { + 'commitizen.plugin': [ + 'plugin = cz_plugin:PluginCz' + ] + } + ... +) +``` + +Then your plugin will be available under the name `plugin`. diff --git a/docs/exit_codes.md b/docs/exit_codes.md index 54be954ff4..e7c7454478 100644 --- a/docs/exit_codes.md +++ b/docs/exit_codes.md @@ -30,4 +30,7 @@ These exit codes can be found in `commitizen/exceptions.py::ExitCode`. | NotAllowed | 20 | `--incremental` cannot be combined with a `rev_range` | | NoneIncrementExit | 21 | The commits found are not eligible to be bumped | | CharacterSetDecodeError | 22 | The character encoding of the command output could not be determined | -| GitCommandError | 23 | Unexpected failure while calling a git command | +| GitCommandError | 23 | Unexpected failure while calling a git command | +| InvalidManualVersion | 24 | Manually provided version is invalid | +| InitFailedError | 25 | Failed to initialize pre-commit | +| VersionProviderUnknown | 26 | `version_provider` setting is set to an unknown version provider indentifier | diff --git a/docs/external_links.md b/docs/external_links.md index c9857e2fb4..388bcc8dea 100644 --- a/docs/external_links.md +++ b/docs/external_links.md @@ -13,5 +13,6 @@ - [Python Table Manners - Commitizen: 規格化 commit message](https://lee-w.github.io/posts/tech/2020/03/python-table-manners-commitizen/) (Written in Traditional Mandarin) - [Automating semantic release with commitizen](https://woile.dev/posts/automating-semver-releases-with-commitizen/) (English) - [How to Write Better Git Commit Messages – A Step-By-Step Guide](https://www.freecodecamp.org/news/how-to-write-better-git-commit-messages/?utm_source=tldrnewsletter) (English) +- [Continuous delivery made easy (in Python)](https://medium.com/dev-genius/continuous-delivery-made-easy-in-python-c085e9c82e69) [automatizando]: https://youtu.be/t3aE2M8UPBo diff --git a/docs/faq.md b/docs/faq.md index 7c076f0a62..060de78c30 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -5,26 +5,19 @@ PEP621 establishes a `[project]` definition inside `pyproject.toml` ```toml [project] name = "spam" -version = "2020.0.0" +version = "2.5.1" ``` -Commitizen **won't** use the `project.version` as a source of truth because it's a -tool aimed for any kind of project. - -If we were to use it, it would increase the complexity of the tool. Also why -wouldn't we support other project files like `cargo.toml` or `package.json`? - -Instead of supporting all the different project files, you can use `version_files` -inside `[tool.commitizen]`, and it will cheaply keep any of these project files in sync +Commitizen provides a [`pep621` version provider](config.md#version-providers) to get and set version from this field. +You just need to set the proper `version_provider` setting: ```toml -[tool.commitizen] +[project] +name = "spam" version = "2.5.1" -version_files = [ - "pyproject.toml:^version", - "cargo.toml:^version", - "package.json:\"version\":" -] + +[tool.commitizen] +version_provider = "pep621" ``` ## Why are `revert` and `chore` valid types in the check pattern of cz conventional_commits but not types we can select? diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 0000000000..7fef3a9126 --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,119 @@ +## Initialize commitizen + +If it's your first time, you'll need to create a commitizen configuration file. + +The assistant utility will help you set up everything + +```sh +cz init +``` + +Alternatively, create a file `.cz.toml` in your project's directory. + +```toml +[tool.commitizen] +version = "0.1.0" +update_changelog_on_bump = true +``` + +## Usage + +### Bump version + +```sh +cz bump +``` + +This command will bump your project's version, and it will create a tag. + +Because of the setting `update_changelog_on_bump`, bump will also create the **changelog**. +You can also [update files](./bump.md#version_files). +You can configure the [version type](./bump.md#version-type) and [version provider](./config.md#version-providers). + +There are many more options available, please read the docs for the [bump command](./bump.md). + +### Committing + +Run in your terminal + +```bash +cz commit +``` + +or the shortcut + +```bash +cz c +``` + +#### Sign off the commit + +Run in the terminal + +```bash +cz commit --signoff +``` + +or the shortcut + +```bash +cz commit -s +``` + +### Get project version + +Running `cz version` will return the version of commitizen, but if you want +your project's version you can run: + +```sh +cz version -p +``` + +This can be useful in many situations, where otherwise, you would require a way +to parse the version of your project. Maybe it's simple if you use a `VERSION` file, +but once you start working with many different projects, it becomes tricky. + +A common example is, when you need to send to slack, the changes for the version that you +just created: + +```sh +cz changelog --dry-run "$(cz version -p)" +``` + +### Integration with Pre-commit + +Commitizen can lint your commit message for you with `cz check`. + +You can integrate this in your [pre-commit](https://pre-commit.com/) config with: + +```yaml +--- +repos: + - repo: https://github.com/commitizen-tools/commitizen + rev: master + hooks: + - id: commitizen + - id: commitizen-branch + stages: [push] +``` + +After the configuration is added, you'll need to run: + +```sh +pre-commit install --hook-type commit-msg --hook-type pre-push +``` + +If you aren't using both hooks, you needn't install both stages. + +| Hook | Recommended Stage | +| ----------------- | ----------------- | +| commitizen | commit-msg | +| commitizen-branch | pre-push | + +Note that pre-commit discourages using `master` as a revision, and the above command will print a warning. You should replace the `master` revision with the [latest tag](https://github.com/commitizen-tools/commitizen/tags). This can be done automatically with: + +```sh +pre-commit autoupdate +``` + +Read more about the `check` command [here](check.md). \ No newline at end of file diff --git a/docs/init.md b/docs/init.md index a8eb3afa9d..f818dac62f 100644 --- a/docs/init.md +++ b/docs/init.md @@ -1,15 +1,18 @@ -For new projects, it is possible to run `cz init`. +To start using commitizen, the recommended approach is to run -This command will prompt the user for information about the project and will -configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.). - -This will help you quickly set up your project with `commitizen`. +```sh +cz init +``` -Currently `init` is detecting +This command will ask you for information about the project and will +configure the selected file type (`pyproject.toml`, `.cz.toml`, etc.). -1. Commit convention rules (`name`). -2. Version detection based on the existing tags (`version`). -3. Tag format, if your tag convention uses, for example `v*` in front of the version. (`tag_format`) +The `init` will help you with -We hope to detect the files where the version is also repeated eventually, -like `package.json` or `__version__`. +1. Choose a convention rules (`name`) +2. Choosing a version provider (`commitizen` or for example `Cargo.toml`) +3. Detecting your project's version +4. Detecting the tag format used +5. Choosing a version type (`semver` or `pep440`) +6. Whether to create the changelog automatically or not during bump +7. Whether you want to keep the major as zero while building alpha software. diff --git a/docs/third-party-commitizen.md b/docs/third-party-commitizen.md index ff6144e6b9..671cdc0fd1 100644 --- a/docs/third-party-commitizen.md +++ b/docs/third-party-commitizen.md @@ -9,7 +9,11 @@ Just like *conventional commit* format, but the scope has been restricted to a JIRA issue format, i.e. `project-issueNumber`. This standardises scopes in a meaningful way. -It can be installed with `pip install conventional-JIRA`. +### Installation + +```sh +pip install conventional-JIRA +``` ### [GitHub JIRA Conventional](https://pypi.org/project/cz-github-jira-conventional/) @@ -18,11 +22,32 @@ This plugin extends the commitizen tools by: - creating links to GitHub commits in the CHANGELOG.md - creating links to JIRA issues in the CHANGELOG.md -It can be installed with `cz-github-jira-conventional`. +### Installation + +```sh +pip install cz-github-jira-conventional +``` For installation instructions (configuration and pre-commit) please visit https://github.com/apheris/cz-github-jira-conventional -### [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) +### [cz-emoji](https://github.com/adam-grant-hendry/cz-emoji) + +*conventional commit* format, but with emojis + +### Installation + +```sh +pip install cz-emoji +``` + +### Usage + +```sh +cz --name cz_emoji commit +``` + + +### [Commitizen emoji](https://pypi.org/project/commitizen-emoji/) (Unmaintained) Just like *conventional commit* format, but with emojis and optionally time spent and related tasks. diff --git a/docs/auto_check.md b/docs/tutorials/auto_check.md similarity index 89% rename from docs/auto_check.md rename to docs/tutorials/auto_check.md index fc76bdf88b..ede8759e68 100644 --- a/docs/auto_check.md +++ b/docs/tutorials/auto_check.md @@ -1,19 +1,22 @@ # Automatically check message before commit ## About + To automatically check a commit message prior to committing, you can use a [git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). ## How to + There are two common methods for installing the hook: + ### Method 1: Add git hook through [pre-commit](https://pre-commit.com/) -* Step 1: Install [pre-commit](https://pre-commit.com/) +- Step 1: Install [pre-commit](https://pre-commit.com/) ```sh python -m pip install pre-commit ``` -* Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content +- Step 2: Create `.pre-commit-config.yaml` at your root directory with the following content ```yaml --- @@ -24,13 +27,14 @@ repos: - id: commitizen ``` -* Step 3: Install the configuration into git hook through `pre-commit` +- Step 3: Install the configuration into git hook through `pre-commit` ```bash pre-commit install --hook-type commit-msg ``` ### Method 2: Manually add git hook + The command might be included inside of a Git hook (inside of `.git/hooks/` at the root of the project). The selected hook might be the file called commit-msg. diff --git a/docs/tutorials/gitlab_ci.md b/docs/tutorials/gitlab_ci.md index 2859db7318..8c3fd73711 100644 --- a/docs/tutorials/gitlab_ci.md +++ b/docs/tutorials/gitlab_ci.md @@ -74,7 +74,7 @@ test: auto-bump: stage: auto-bump - image: python:3.6 + image: python:3.7 before_script: - "which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )" - eval `ssh-agent -s` diff --git a/docs/tutorials/jenkins_pipeline.md b/docs/tutorials/jenkins_pipeline.md index 0d5d0693e2..ef39cece84 100644 --- a/docs/tutorials/jenkins_pipeline.md +++ b/docs/tutorials/jenkins_pipeline.md @@ -17,7 +17,7 @@ pipeline { stage('Bump version') { when { beforeAgent true - branch 'master' + expression { env.BRANCH_IS_PRIMARY } not { changelog '^bump:.+' } diff --git a/mkdocs.yml b/mkdocs.yml index 7d8dc430b0..da71e30abe 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,32 +2,55 @@ site_name: Commitizen site_description: commit rules, semantic version, conventional commits theme: - name: 'material' + name: "material" + palette: + - primary: 'deep purple' + # Palette toggle for automatic mode + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + toggle: + icon: material/brightness-7 + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + toggle: + icon: material/brightness-4 + name: Switch to system preference repo_name: commitizen-tools/commitizen repo_url: https://github.com/commitizen-tools/commitizen edit_uri: "" nav: - - Introduction: 'README.md' - - Commands: - - Init: 'init.md' - - Commit: 'commit.md' - - Bump: 'bump.md' - - Check: 'check.md' - - Changelog: 'changelog.md' - - Configuration: 'config.md' - - Customization: 'customization.md' - - Tutorials: - - Writing commits: 'tutorials/writing_commits.md' - - GitLab CI: 'tutorials/gitlab_ci.md' - - Github Actions: 'tutorials/github_actions.md' - - Jenkins pipeline: 'tutorials/jenkins_pipeline.md' - - FAQ: 'faq.md' - - Exit Codes: 'exit_codes.md' - - Third-Party Commitizen Templates: 'third-party-commitizen.md' - - Contributing: 'contributing.md' - - Resources: 'external_links.md' + - Introduction: "README.md" + - Getting Started: "getting_started.md" + - Commands: + - Init: "init.md" + - Commit: "commit.md" + - Bump: "bump.md" + - Check: "check.md" + - Changelog: "changelog.md" + - Configuration: "config.md" + - Customization: "customization.md" + - Tutorials: + - Writing commits: "tutorials/writing_commits.md" + - Auto check commits: "tutorials/auto_check.md" + - GitLab CI: "tutorials/gitlab_ci.md" + - Github Actions: "tutorials/github_actions.md" + - Jenkins pipeline: "tutorials/jenkins_pipeline.md" + - FAQ: "faq.md" + - Exit Codes: "exit_codes.md" + - Third-Party Commitizen Templates: "third-party-commitizen.md" + - Contributing: "contributing.md" + - Resources: "external_links.md" markdown_extensions: - markdown.extensions.codehilite: @@ -35,3 +58,7 @@ markdown_extensions: - admonition - codehilite - extra + - pymdownx.highlight + - pymdownx.superfences + - toc: + permalink: true diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000000..32a67a148f --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1602 @@ +# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. + +[[package]] +name = "appnope" +version = "0.1.3" +description = "Disable App Nap on macOS >= 10.9" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, + {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, +] + +[[package]] +name = "argcomplete" +version = "2.1.2" +description = "Bash tab completion for argparse" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "argcomplete-2.1.2-py3-none-any.whl", hash = "sha256:4ba9cdaa28c361d251edce884cd50b4b1215d65cdc881bd204426cdde9f52731"}, + {file = "argcomplete-2.1.2.tar.gz", hash = "sha256:fc82ef070c607b1559b5c720529d63b54d9dcf2dcfc2632b10e6372314a34457"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.23,<6", markers = "python_version == \"3.7\""} + +[package.extras] +lint = ["flake8", "mypy"] +test = ["coverage", "flake8", "mypy", "pexpect", "wheel"] + +[[package]] +name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, + {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, +] + +[[package]] +name = "black" +version = "22.12.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.2.3" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e58c0d41d336569d63d1b113bd573db8363bc4146f39444125b7f8060e4e04f5"}, + {file = "coverage-7.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:344e714bd0fe921fc72d97404ebbdbf9127bac0ca1ff66d7b79efc143cf7c0c4"}, + {file = "coverage-7.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974bc90d6f6c1e59ceb1516ab00cf1cdfbb2e555795d49fa9571d611f449bcb2"}, + {file = "coverage-7.2.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0743b0035d4b0e32bc1df5de70fba3059662ace5b9a2a86a9f894cfe66569013"}, + {file = "coverage-7.2.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d0391fb4cfc171ce40437f67eb050a340fdbd0f9f49d6353a387f1b7f9dd4fa"}, + {file = "coverage-7.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a42e1eff0ca9a7cb7dc9ecda41dfc7cbc17cb1d02117214be0561bd1134772b"}, + {file = "coverage-7.2.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:be19931a8dcbe6ab464f3339966856996b12a00f9fe53f346ab3be872d03e257"}, + {file = "coverage-7.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72fcae5bcac3333a4cf3b8f34eec99cea1187acd55af723bcbd559adfdcb5535"}, + {file = "coverage-7.2.3-cp310-cp310-win32.whl", hash = "sha256:aeae2aa38395b18106e552833f2a50c27ea0000122bde421c31d11ed7e6f9c91"}, + {file = "coverage-7.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:83957d349838a636e768251c7e9979e899a569794b44c3728eaebd11d848e58e"}, + {file = "coverage-7.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfd393094cd82ceb9b40df4c77976015a314b267d498268a076e940fe7be6b79"}, + {file = "coverage-7.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:182eb9ac3f2b4874a1f41b78b87db20b66da6b9cdc32737fbbf4fea0c35b23fc"}, + {file = "coverage-7.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bb1e77a9a311346294621be905ea8a2c30d3ad371fc15bb72e98bfcfae532df"}, + {file = "coverage-7.2.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca0f34363e2634deffd390a0fef1aa99168ae9ed2af01af4a1f5865e362f8623"}, + {file = "coverage-7.2.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55416d7385774285b6e2a5feca0af9652f7f444a4fa3d29d8ab052fafef9d00d"}, + {file = "coverage-7.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06ddd9c0249a0546997fdda5a30fbcb40f23926df0a874a60a8a185bc3a87d93"}, + {file = "coverage-7.2.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fff5aaa6becf2c6a1699ae6a39e2e6fb0672c2d42eca8eb0cafa91cf2e9bd312"}, + {file = "coverage-7.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ea53151d87c52e98133eb8ac78f1206498c015849662ca8dc246255265d9c3c4"}, + {file = "coverage-7.2.3-cp311-cp311-win32.whl", hash = "sha256:8f6c930fd70d91ddee53194e93029e3ef2aabe26725aa3c2753df057e296b925"}, + {file = "coverage-7.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:fa546d66639d69aa967bf08156eb8c9d0cd6f6de84be9e8c9819f52ad499c910"}, + {file = "coverage-7.2.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2317d5ed777bf5a033e83d4f1389fd4ef045763141d8f10eb09a7035cee774c"}, + {file = "coverage-7.2.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be9824c1c874b73b96288c6d3de793bf7f3a597770205068c6163ea1f326e8b9"}, + {file = "coverage-7.2.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3b2803e730dc2797a017335827e9da6da0e84c745ce0f552e66400abdfb9a1"}, + {file = "coverage-7.2.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f69770f5ca1994cb32c38965e95f57504d3aea96b6c024624fdd5bb1aa494a1"}, + {file = "coverage-7.2.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1127b16220f7bfb3f1049ed4a62d26d81970a723544e8252db0efde853268e21"}, + {file = "coverage-7.2.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:aa784405f0c640940595fa0f14064d8e84aff0b0f762fa18393e2760a2cf5841"}, + {file = "coverage-7.2.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3146b8e16fa60427e03884301bf8209221f5761ac754ee6b267642a2fd354c48"}, + {file = "coverage-7.2.3-cp37-cp37m-win32.whl", hash = "sha256:1fd78b911aea9cec3b7e1e2622c8018d51c0d2bbcf8faaf53c2497eb114911c1"}, + {file = "coverage-7.2.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0f3736a5d34e091b0a611964c6262fd68ca4363df56185902528f0b75dbb9c1f"}, + {file = "coverage-7.2.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:981b4df72c93e3bc04478153df516d385317628bd9c10be699c93c26ddcca8ab"}, + {file = "coverage-7.2.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0045f8f23a5fb30b2eb3b8a83664d8dc4fb58faddf8155d7109166adb9f2040"}, + {file = "coverage-7.2.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f760073fcf8f3d6933178d67754f4f2d4e924e321f4bb0dcef0424ca0215eba1"}, + {file = "coverage-7.2.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c86bd45d1659b1ae3d0ba1909326b03598affbc9ed71520e0ff8c31a993ad911"}, + {file = "coverage-7.2.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:172db976ae6327ed4728e2507daf8a4de73c7cc89796483e0a9198fd2e47b462"}, + {file = "coverage-7.2.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2a3a6146fe9319926e1d477842ca2a63fe99af5ae690b1f5c11e6af074a6b5c"}, + {file = "coverage-7.2.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f649dd53833b495c3ebd04d6eec58479454a1784987af8afb77540d6c1767abd"}, + {file = "coverage-7.2.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c4ed4e9f3b123aa403ab424430b426a1992e6f4c8fd3cb56ea520446e04d152"}, + {file = "coverage-7.2.3-cp38-cp38-win32.whl", hash = "sha256:eb0edc3ce9760d2f21637766c3aa04822030e7451981ce569a1b3456b7053f22"}, + {file = "coverage-7.2.3-cp38-cp38-win_amd64.whl", hash = "sha256:63cdeaac4ae85a179a8d6bc09b77b564c096250d759eed343a89d91bce8b6367"}, + {file = "coverage-7.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:20d1a2a76bb4eb00e4d36b9699f9b7aba93271c9c29220ad4c6a9581a0320235"}, + {file = "coverage-7.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ea748802cc0de4de92ef8244dd84ffd793bd2e7be784cd8394d557a3c751e21"}, + {file = "coverage-7.2.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21b154aba06df42e4b96fc915512ab39595105f6c483991287021ed95776d934"}, + {file = "coverage-7.2.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd214917cabdd6f673a29d708574e9fbdb892cb77eb426d0eae3490d95ca7859"}, + {file = "coverage-7.2.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2e58e45fe53fab81f85474e5d4d226eeab0f27b45aa062856c89389da2f0d9"}, + {file = "coverage-7.2.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:87ecc7c9a1a9f912e306997ffee020297ccb5ea388421fe62a2a02747e4d5539"}, + {file = "coverage-7.2.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:387065e420aed3c71b61af7e82c7b6bc1c592f7e3c7a66e9f78dd178699da4fe"}, + {file = "coverage-7.2.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ea3f5bc91d7d457da7d48c7a732beaf79d0c8131df3ab278e6bba6297e23c6c4"}, + {file = "coverage-7.2.3-cp39-cp39-win32.whl", hash = "sha256:ae7863a1d8db6a014b6f2ff9c1582ab1aad55a6d25bac19710a8df68921b6e30"}, + {file = "coverage-7.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:3f04becd4fcda03c0160d0da9c8f0c246bc78f2f7af0feea1ec0930e7c93fa4a"}, + {file = "coverage-7.2.3-pp37.pp38.pp39-none-any.whl", hash = "sha256:965ee3e782c7892befc25575fa171b521d33798132692df428a09efacaffe8d0"}, + {file = "coverage-7.2.3.tar.gz", hash = "sha256:d298c2815fa4891edd9abe5ad6e6cb4207104c7dd9fd13aea3fdebf6f9b91259"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "decli" +version = "0.5.2" +description = "Minimal, easy-to-use, declarative cli tool" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "decli-0.5.2-py3-none-any.whl", hash = "sha256:d3207bc02d0169bf6ed74ccca09ce62edca0eb25b0ebf8bf4ae3fb8333e15ca0"}, + {file = "decli-0.5.2.tar.gz", hash = "sha256:f2cde55034a75c819c630c7655a844c612f2598c42c21299160465df6ad463ad"}, +] + +[[package]] +name = "decorator" +version = "5.1.1" +description = "Decorators for Humans" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, + {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, +] + +[[package]] +name = "distlib" +version = "0.3.6" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, + {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "execnet" +version = "1.9.0" +description = "execnet: rapid multi-Python deployment" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, + {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, +] + +[package.extras] +testing = ["pre-commit"] + +[[package]] +name = "filelock" +version = "3.12.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, + {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "freezegun" +version = "1.2.2" +description = "Let your Python tests travel through time" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"}, + {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "identify" +version = "2.5.22" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.22-py2.py3-none-any.whl", hash = "sha256:f0faad595a4687053669c112004178149f6c326db71ee999ae4636685753ad2f"}, + {file = "identify-2.5.22.tar.gz", hash = "sha256:f7a93d6cf98e29bd07663c60728e7a4057615068d7a639d132dc883b2d54d31e"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "ipython" +version = "7.34.0" +description = "IPython: Productive Interactive Computing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ipython-7.34.0-py3-none-any.whl", hash = "sha256:c175d2440a1caff76116eb719d40538fbb316e214eda85c5515c303aacbfb23e"}, + {file = "ipython-7.34.0.tar.gz", hash = "sha256:af3bdb46aa292bce5615b1b2ebc76c2080c5f77f54bda2ec72461317273e7cd6"}, +] + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} +backcall = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[package.extras] +all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] +doc = ["Sphinx (>=1.3)"] +kernel = ["ipykernel"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments", "requests", "testpath"] + +[[package]] +name = "jedi" +version = "0.18.2" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "jedi-0.18.2-py2.py3-none-any.whl", hash = "sha256:203c1fd9d969ab8f2119ec0a3342e0b49910045abe6af0a3ae83a5764d54639e"}, + {file = "jedi-0.18.2.tar.gz", hash = "sha256:bae794c30d07f6d910d32a7048af09b5a39ed740918da923c6b780790ebac612"}, +] + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.3.7" +description = "Python implementation of Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, + {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.6" +description = "Inline Matplotlib backend for Jupyter" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, + {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, +] + +[package.dependencies] +traitlets = "*" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mkdocs" +version = "1.4.2" +description = "Project documentation with Markdown." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs-1.4.2-py3-none-any.whl", hash = "sha256:c8856a832c1e56702577023cd64cc5f84948280c1c0fcc6af4cd39006ea6aa8c"}, + {file = "mkdocs-1.4.2.tar.gz", hash = "sha256:8947af423a6d0facf41ea1195b8e1e8c85ad94ac95ae307fe11232e0424b11c5"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.2.1,<3.4" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.8\""} +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-material" +version = "9.1.7" +description = "Documentation that simply works" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_material-9.1.7-py3-none-any.whl", hash = "sha256:4ab0e2f378bb811ca44a001a63d687403a2725f0485c41d6a2a7aed3aa34cd31"}, + {file = "mkdocs_material-9.1.7.tar.gz", hash = "sha256:244f85d13e3aa773aaa66c74b95bb4b00d89a3a917f6f9d33704a5655bf0c423"}, +] + +[package.dependencies] +colorama = ">=0.4" +jinja2 = ">=3.0" +markdown = ">=3.2" +mkdocs = ">=1.4.2" +mkdocs-material-extensions = ">=1.1" +pygments = ">=2.14" +pymdown-extensions = ">=9.9.1" +regex = ">=2022.4.24" +requests = ">=2.26" + +[[package]] +name = "mkdocs-material-extensions" +version = "1.1.1" +description = "Extension pack for Python Markdown and MkDocs Material." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_material_extensions-1.1.1-py3-none-any.whl", hash = "sha256:e41d9f38e4798b6617ad98ca8f7f1157b1e4385ac1459ca1e4ea219b556df945"}, + {file = "mkdocs_material_extensions-1.1.1.tar.gz", hash = "sha256:9c003da71e2cc2493d910237448c672e00cefc800d3d6ae93d2fc69979e3bd93"}, +] + +[[package]] +name = "mypy" +version = "0.931" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, + {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, + {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, + {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, + {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, + {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, + {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, + {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, + {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, + {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, + {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, + {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, + {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, + {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, + {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, + {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, + {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, + {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, + {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, + {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, +] + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = ">=1.1.0" +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "nodeenv" +version = "1.7.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] + +[package.dependencies] +setuptools = "*" + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, + {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, +] + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pathspec" +version = "0.11.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] + +[[package]] +name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, + {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "pickleshare" +version = "0.7.5" +description = "Tiny 'shelve'-like database with concurrency support" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, + {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, +] + +[[package]] +name = "platformdirs" +version = "3.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"}, + {file = "platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "2.21.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, + {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "prompt-toolkit" +version = "3.0.38" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, + {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pymdown-extensions" +version = "9.11" +description = "Extension pack for Python Markdown." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pymdown_extensions-9.11-py3-none-any.whl", hash = "sha256:a499191d8d869f30339de86fcf072a787e86c42b6f16f280f5c2cf174182b7f3"}, + {file = "pymdown_extensions-9.11.tar.gz", hash = "sha256:f7e86c1d3981f23d9dc43294488ecb54abadd05b0be4bf8f0e15efc90f7853ff"}, +] + +[package.dependencies] +markdown = ">=3.2" +pyyaml = "*" + +[[package]] +name = "pytest" +version = "7.3.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-cov-4.0.0.tar.gz", hash = "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470"}, + {file = "pytest_cov-4.0.0-py3-none-any.whl", hash = "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-datadir" +version = "1.4.1" +description = "pytest plugin for test data directories and files" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-datadir-1.4.1.tar.gz", hash = "sha256:9f7a3c4def6ac4cac3cc8181139ab53bd2667231052bd40cb07081748d4420f0"}, + {file = "pytest_datadir-1.4.1-py3-none-any.whl", hash = "sha256:095f441782b1b907587eca7227fdbae94be43f1c96b4b2cbcc6801a4645be1af"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[[package]] +name = "pytest-freezer" +version = "0.4.6" +description = "Pytest plugin providing a fixture interface for spulec/freezegun" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest_freezer-0.4.6-py3-none-any.whl", hash = "sha256:ca549c30a7e12bc7b242978b6fa0bb91e73cd1bd7d5b2bb658f0f9d7f1694cac"}, + {file = "pytest_freezer-0.4.6.tar.gz", hash = "sha256:8e88cd571d3ba10dd9e0cc09897eb01c32a37bef5ca4ff7c4ea8598c91aa6d96"}, +] + +[package.dependencies] +freezegun = ">=1.0" +pytest = ">=3.6" + +[[package]] +name = "pytest-mock" +version = "3.10.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-mock-3.10.0.tar.gz", hash = "sha256:fbbdb085ef7c252a326fd8cdcac0aa3b1333d8811f131bdcc701002e1be7ed4f"}, + {file = "pytest_mock-3.10.0-py3-none-any.whl", hash = "sha256:f4c973eeae0282963eb293eb173ce91b091a79c1334455acfac9ddee8a1c784b"}, +] + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "pytest-regressions" +version = "2.4.2" +description = "Easy to use fixtures to write regression tests." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pytest-regressions-2.4.2.tar.gz", hash = "sha256:eab89e7ba4aa339bdfe07b31047e8e6a70b1e7846e74a8bbfc31a588d01e7303"}, + {file = "pytest_regressions-2.4.2-py3-none-any.whl", hash = "sha256:b47f92c39d5dd2faa90716ee74c584d705c08e14f435a947f67706c4e5b8ef9a"}, +] + +[package.dependencies] +pytest = ">=6.2.0" +pytest-datadir = ">=1.2.0" +pyyaml = "*" + +[package.extras] +dataframe = ["numpy", "pandas"] +dev = ["matplotlib", "mypy", "numpy", "pandas", "pillow", "pre-commit", "restructuredtext-lint", "tox"] +image = ["numpy", "pillow"] +num = ["numpy", "pandas"] + +[[package]] +name = "pytest-xdist" +version = "3.2.1" +description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-xdist-3.2.1.tar.gz", hash = "sha256:1849bd98d8b242b948e472db7478e090bf3361912a8fed87992ed94085f54727"}, + {file = "pytest_xdist-3.2.1-py3-none-any.whl", hash = "sha256:37290d161638a20b672401deef1cba812d110ac27e35d213f091d15b8beb40c9"}, +] + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "questionary" +version = "1.10.0" +description = "Python library to build pretty command line user prompts ⭐️" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" +files = [ + {file = "questionary-1.10.0-py3-none-any.whl", hash = "sha256:fecfcc8cca110fda9d561cb83f1e97ecbb93c613ff857f655818839dac74ce90"}, + {file = "questionary-1.10.0.tar.gz", hash = "sha256:600d3aefecce26d48d97eee936fdb66e4bc27f934c3ab6dd1e292c4f43946d90"}, +] + +[package.dependencies] +prompt_toolkit = ">=2.0,<4.0" + +[package.extras] +docs = ["Sphinx (>=3.3,<4.0)", "sphinx-autobuild (>=2020.9.1,<2021.0.0)", "sphinx-autodoc-typehints (>=1.11.1,<2.0.0)", "sphinx-copybutton (>=0.3.1,<0.4.0)", "sphinx-rtd-theme (>=0.5.0,<0.6.0)"] + +[[package]] +name = "regex" +version = "2022.10.31" +description = "Alternative regular expression module, to replace re." +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "regex-2022.10.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a8ff454ef0bb061e37df03557afda9d785c905dab15584860f982e88be73015f"}, + {file = "regex-2022.10.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eba476b1b242620c266edf6325b443a2e22b633217a9835a52d8da2b5c051f9"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0e5af9a9effb88535a472e19169e09ce750c3d442fb222254a276d77808620b"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d03fe67b2325cb3f09be029fd5da8df9e6974f0cde2c2ac6a79d2634e791dd57"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9d0b68ac1743964755ae2d89772c7e6fb0118acd4d0b7464eaf3921c6b49dd4"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a45b6514861916c429e6059a55cf7db74670eaed2052a648e3e4d04f070e001"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8b0886885f7323beea6f552c28bff62cbe0983b9fbb94126531693ea6c5ebb90"}, + {file = "regex-2022.10.31-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5aefb84a301327ad115e9d346c8e2760009131d9d4b4c6b213648d02e2abe144"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:702d8fc6f25bbf412ee706bd73019da5e44a8400861dfff7ff31eb5b4a1276dc"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a3c1ebd4ed8e76e886507c9eddb1a891673686c813adf889b864a17fafcf6d66"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:50921c140561d3db2ab9f5b11c5184846cde686bb5a9dc64cae442926e86f3af"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:7db345956ecce0c99b97b042b4ca7326feeec6b75facd8390af73b18e2650ffc"}, + {file = "regex-2022.10.31-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:763b64853b0a8f4f9cfb41a76a4a85a9bcda7fdda5cb057016e7706fde928e66"}, + {file = "regex-2022.10.31-cp310-cp310-win32.whl", hash = "sha256:44136355e2f5e06bf6b23d337a75386371ba742ffa771440b85bed367c1318d1"}, + {file = "regex-2022.10.31-cp310-cp310-win_amd64.whl", hash = "sha256:bfff48c7bd23c6e2aec6454aaf6edc44444b229e94743b34bdcdda2e35126cf5"}, + {file = "regex-2022.10.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4b4b1fe58cd102d75ef0552cf17242705ce0759f9695334a56644ad2d83903fe"}, + {file = "regex-2022.10.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:542e3e306d1669b25936b64917285cdffcd4f5c6f0247636fec037187bd93542"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c27cc1e4b197092e50ddbf0118c788d9977f3f8f35bfbbd3e76c1846a3443df7"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8e38472739028e5f2c3a4aded0ab7eadc447f0d84f310c7a8bb697ec417229e"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76c598ca73ec73a2f568e2a72ba46c3b6c8690ad9a07092b18e48ceb936e9f0c"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c28d3309ebd6d6b2cf82969b5179bed5fefe6142c70f354ece94324fa11bf6a1"}, + {file = "regex-2022.10.31-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9af69f6746120998cd9c355e9c3c6aec7dff70d47247188feb4f829502be8ab4"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a5f9505efd574d1e5b4a76ac9dd92a12acb2b309551e9aa874c13c11caefbe4f"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5ff525698de226c0ca743bfa71fc6b378cda2ddcf0d22d7c37b1cc925c9650a5"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4fe7fda2fe7c8890d454f2cbc91d6c01baf206fbc96d89a80241a02985118c0c"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2cdc55ca07b4e70dda898d2ab7150ecf17c990076d3acd7a5f3b25cb23a69f1c"}, + {file = "regex-2022.10.31-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:44a6c2f6374e0033873e9ed577a54a3602b4f609867794c1a3ebba65e4c93ee7"}, + {file = "regex-2022.10.31-cp311-cp311-win32.whl", hash = "sha256:d8716f82502997b3d0895d1c64c3b834181b1eaca28f3f6336a71777e437c2af"}, + {file = "regex-2022.10.31-cp311-cp311-win_amd64.whl", hash = "sha256:61edbca89aa3f5ef7ecac8c23d975fe7261c12665f1d90a6b1af527bba86ce61"}, + {file = "regex-2022.10.31-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a069c8483466806ab94ea9068c34b200b8bfc66b6762f45a831c4baaa9e8cdd"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d26166acf62f731f50bdd885b04b38828436d74e8e362bfcb8df221d868b5d9b"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac741bf78b9bb432e2d314439275235f41656e189856b11fb4e774d9f7246d81"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75f591b2055523fc02a4bbe598aa867df9e953255f0b7f7715d2a36a9c30065c"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b30bddd61d2a3261f025ad0f9ee2586988c6a00c780a2fb0a92cea2aa702c54"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef4163770525257876f10e8ece1cf25b71468316f61451ded1a6f44273eedeb5"}, + {file = "regex-2022.10.31-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7b280948d00bd3973c1998f92e22aa3ecb76682e3a4255f33e1020bd32adf443"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:d0213671691e341f6849bf33cd9fad21f7b1cb88b89e024f33370733fec58742"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:22e7ebc231d28393dfdc19b185d97e14a0f178bedd78e85aad660e93b646604e"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:8ad241da7fac963d7573cc67a064c57c58766b62a9a20c452ca1f21050868dfa"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:586b36ebda81e6c1a9c5a5d0bfdc236399ba6595e1397842fd4a45648c30f35e"}, + {file = "regex-2022.10.31-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0653d012b3bf45f194e5e6a41df9258811ac8fc395579fa82958a8b76286bea4"}, + {file = "regex-2022.10.31-cp36-cp36m-win32.whl", hash = "sha256:144486e029793a733e43b2e37df16a16df4ceb62102636ff3db6033994711066"}, + {file = "regex-2022.10.31-cp36-cp36m-win_amd64.whl", hash = "sha256:c14b63c9d7bab795d17392c7c1f9aaabbffd4cf4387725a0ac69109fb3b550c6"}, + {file = "regex-2022.10.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4cac3405d8dda8bc6ed499557625585544dd5cbf32072dcc72b5a176cb1271c8"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23cbb932cc53a86ebde0fb72e7e645f9a5eec1a5af7aa9ce333e46286caef783"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74bcab50a13960f2a610cdcd066e25f1fd59e23b69637c92ad470784a51b1347"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78d680ef3e4d405f36f0d6d1ea54e740366f061645930072d39bca16a10d8c93"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce6910b56b700bea7be82c54ddf2e0ed792a577dfaa4a76b9af07d550af435c6"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:659175b2144d199560d99a8d13b2228b85e6019b6e09e556209dfb8c37b78a11"}, + {file = "regex-2022.10.31-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1ddf14031a3882f684b8642cb74eea3af93a2be68893901b2b387c5fd92a03ec"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b683e5fd7f74fb66e89a1ed16076dbab3f8e9f34c18b1979ded614fe10cdc4d9"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2bde29cc44fa81c0a0c8686992c3080b37c488df167a371500b2a43ce9f026d1"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4919899577ba37f505aaebdf6e7dc812d55e8f097331312db7f1aab18767cce8"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:9c94f7cc91ab16b36ba5ce476f1904c91d6c92441f01cd61a8e2729442d6fcf5"}, + {file = "regex-2022.10.31-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae1e96785696b543394a4e3f15f3f225d44f3c55dafe3f206493031419fedf95"}, + {file = "regex-2022.10.31-cp37-cp37m-win32.whl", hash = "sha256:c670f4773f2f6f1957ff8a3962c7dd12e4be54d05839b216cb7fd70b5a1df394"}, + {file = "regex-2022.10.31-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0caeff18b96ea90fc0eb6e3bdb2b10ab5b01a95128dfeccb64a7238decf5f0"}, + {file = "regex-2022.10.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:131d4be09bea7ce2577f9623e415cab287a3c8e0624f778c1d955ec7c281bd4d"}, + {file = "regex-2022.10.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e613a98ead2005c4ce037c7b061f2409a1a4e45099edb0ef3200ee26ed2a69a8"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052b670fafbe30966bbe5d025e90b2a491f85dfe5b2583a163b5e60a85a321ad"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa62a07ac93b7cb6b7d0389d8ef57ffc321d78f60c037b19dfa78d6b17c928ee"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5352bea8a8f84b89d45ccc503f390a6be77917932b1c98c4cdc3565137acc714"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20f61c9944f0be2dc2b75689ba409938c14876c19d02f7585af4460b6a21403e"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29c04741b9ae13d1e94cf93fca257730b97ce6ea64cfe1eba11cf9ac4e85afb6"}, + {file = "regex-2022.10.31-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:543883e3496c8b6d58bd036c99486c3c8387c2fc01f7a342b760c1ea3158a318"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7a8b43ee64ca8f4befa2bea4083f7c52c92864d8518244bfa6e88c751fa8fff"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6a9a19bea8495bb419dc5d38c4519567781cd8d571c72efc6aa959473d10221a"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6ffd55b5aedc6f25fd8d9f905c9376ca44fcf768673ffb9d160dd6f409bfda73"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4bdd56ee719a8f751cf5a593476a441c4e56c9b64dc1f0f30902858c4ef8771d"}, + {file = "regex-2022.10.31-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ca88da1bd78990b536c4a7765f719803eb4f8f9971cc22d6ca965c10a7f2c4c"}, + {file = "regex-2022.10.31-cp38-cp38-win32.whl", hash = "sha256:5a260758454580f11dd8743fa98319bb046037dfab4f7828008909d0aa5292bc"}, + {file = "regex-2022.10.31-cp38-cp38-win_amd64.whl", hash = "sha256:5e6a5567078b3eaed93558842346c9d678e116ab0135e22eb72db8325e90b453"}, + {file = "regex-2022.10.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5217c25229b6a85049416a5c1e6451e9060a1edcf988641e309dbe3ab26d3e49"}, + {file = "regex-2022.10.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4bf41b8b0a80708f7e0384519795e80dcb44d7199a35d52c15cc674d10b3081b"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf0da36a212978be2c2e2e2d04bdff46f850108fccc1851332bcae51c8907cc"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d403d781b0e06d2922435ce3b8d2376579f0c217ae491e273bab8d092727d244"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a37d51fa9a00d265cf73f3de3930fa9c41548177ba4f0faf76e61d512c774690"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4f781ffedd17b0b834c8731b75cce2639d5a8afe961c1e58ee7f1f20b3af185"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d243b36fbf3d73c25e48014961e83c19c9cc92530516ce3c43050ea6276a2ab7"}, + {file = "regex-2022.10.31-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:370f6e97d02bf2dd20d7468ce4f38e173a124e769762d00beadec3bc2f4b3bc4"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:597f899f4ed42a38df7b0e46714880fb4e19a25c2f66e5c908805466721760f5"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7dbdce0c534bbf52274b94768b3498abdf675a691fec5f751b6057b3030f34c1"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:22960019a842777a9fa5134c2364efaed5fbf9610ddc5c904bd3a400973b0eb8"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7f5a3ffc731494f1a57bd91c47dc483a1e10048131ffb52d901bfe2beb6102e8"}, + {file = "regex-2022.10.31-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7ef6b5942e6bfc5706301a18a62300c60db9af7f6368042227ccb7eeb22d0892"}, + {file = "regex-2022.10.31-cp39-cp39-win32.whl", hash = "sha256:395161bbdbd04a8333b9ff9763a05e9ceb4fe210e3c7690f5e68cedd3d65d8e1"}, + {file = "regex-2022.10.31-cp39-cp39-win_amd64.whl", hash = "sha256:957403a978e10fb3ca42572a23e6f7badff39aa1ce2f4ade68ee452dc6807692"}, + {file = "regex-2022.10.31.tar.gz", hash = "sha256:a3a98921da9a1bf8457aeee6a551948a83601689e5ecdd736894ea9bbec77e83"}, +] + +[[package]] +name = "requests" +version = "2.28.2" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=3.7, <4" +files = [ + {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, + {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruff" +version = "0.0.262" +description = "An extremely fast Python linter, written in Rust." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.0.262-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c26c1abd420d041592d05d63aee8c6a18feb24aed4deb6e91129e9f2c7b4914a"}, + {file = "ruff-0.0.262-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b379e9765afa679316e52288a942df085e590862f8945088936a7bce3116d8f3"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b7e0ca6821aafbd2b059df3119fcd5881250721ca8e825789fd2c471f7c59be"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4cca35e2aeddff72bb4379a1dabc134e0c0d25ebc754a2cb733a1f8d4dbbb5e0"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15bf5533ce169aebbafa00017987f673e879f60a625d932b464b8cdaf32a4fce"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3909e249d984c4517194005a1c30eaa0c3a6d906c789d9fc0c9c7e007fb3e759"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e2813013a19b3e147e840bdb2e42db5825b53b47364e58e7b467c5fa47ffda2"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d25a94996b2037e566c2a801c8b324c0a826194d5d4d90ad7c1ccb8cf06521fa"}, + {file = "ruff-0.0.262-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ca04348372efc59f6ee808d903d35e0d352cf2c78e487757cd48b65104b83e"}, + {file = "ruff-0.0.262-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:24f989363e9bb5d0283490298102a5218682e49ebf300e445d69e24bee03ac83"}, + {file = "ruff-0.0.262-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3c24e678e43ca4b67e29cc9a7a54eea05f31a5898cbf17bfec47b68f08d32a60"}, + {file = "ruff-0.0.262-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0baff3c9a22227358ea109c165efe62dbdd0f2b9fd5256567dda8682b444fe23"}, + {file = "ruff-0.0.262-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:083bac6e238d8b7d5ac3618666ea63b7ac661cf94c5da160070a58e190082831"}, + {file = "ruff-0.0.262-py3-none-win32.whl", hash = "sha256:15bbfa2d15c137717627e0d56b0e535ae297b734551e34e03fcc25d7642cf43a"}, + {file = "ruff-0.0.262-py3-none-win_amd64.whl", hash = "sha256:973ac29193f718349cf5746b7d86dfeaf7d40e9651ed97790a9b9327305888b9"}, + {file = "ruff-0.0.262-py3-none-win_arm64.whl", hash = "sha256:f102904ebe395acd2a181d295b98120acd7a63f732b691672977fc688674f4af"}, + {file = "ruff-0.0.262.tar.gz", hash = "sha256:faea54231c265f5349975ba6f3d855b71881a01f391b2000c47740390c6d5f68"}, +] + +[[package]] +name = "setuptools" +version = "67.7.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.1-py3-none-any.whl", hash = "sha256:6f0839fbdb7e3cfef1fc38d7954f5c1c26bf4eebb155a55c9bf8faf997b9fb67"}, + {file = "setuptools-67.7.1.tar.gz", hash = "sha256:bb16732e8eb928922eabaa022f881ae2b7cdcfaf9993ef1f5e841a96d32b8e0c"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "termcolor" +version = "2.2.0" +description = "ANSI color formatting for output in terminal" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "termcolor-2.2.0-py3-none-any.whl", hash = "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7"}, + {file = "termcolor-2.2.0.tar.gz", hash = "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tomlkit" +version = "0.11.7" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.11.7-py3-none-any.whl", hash = "sha256:5325463a7da2ef0c6bbfefb62a3dc883aebe679984709aee32a317907d0a8d3c"}, + {file = "tomlkit-0.11.7.tar.gz", hash = "sha256:f392ef70ad87a672f02519f99967d28a4d3047133e2d1df936511465fbb3791d"}, +] + +[[package]] +name = "traitlets" +version = "5.9.0" +description = "Traitlets Python configuration system" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, + {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] + +[[package]] +name = "types-pyyaml" +version = "5.4.12" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-PyYAML-5.4.12.tar.gz", hash = "sha256:3f4daa754357491625ae8c3a39c9e1b0d7cd5632bc4e1c35e7a7f75a64aa124b"}, + {file = "types_PyYAML-5.4.12-py3-none-any.whl", hash = "sha256:e06083f85375a5678e4c19452ed6467ce2167b71db222313e1792cb8fc76859a"}, +] + +[[package]] +name = "types-termcolor" +version = "0.1.1" +description = "Typing stubs for termcolor" +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "types-termcolor-0.1.1.tar.gz", hash = "sha256:4d9e09ce7f3267985f5280b22e25790c98cb64628b6466e1fb915dbb52ad7136"}, + {file = "types_termcolor-0.1.1-py2.py3-none-any.whl", hash = "sha256:3694c312e32f71fdc0f469c334ea21645f3130d90c93cd53bcb06b1233e174d5"}, +] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.21.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.21.1-py3-none-any.whl", hash = "sha256:09ddbe1af0c8ed2bb4d6ed226b9e6415718ad18aef9fa0ba023d96b7a8356049"}, + {file = "virtualenv-20.21.1.tar.gz", hash = "sha256:4c104ccde994f8b108163cf9ba58f3d11511d9403de87fb9b4f52bf33dbc8668"}, +] + +[package.dependencies] +distlib = ">=0.3.6,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<4" + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] + +[[package]] +name = "watchdog" +version = "3.0.0" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, + {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"}, + {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"}, + {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"}, + {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"}, + {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"}, + {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"}, + {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"}, + {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"}, + {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"}, + {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"}, + {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"}, + {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"}, + {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "1c2ebb7edcfa73e87d806c2e821933ff58a81a3cb07f708d5eb257ab131710a9" diff --git a/pyproject.toml b/pyproject.toml index 7369391f57..04e6d3358a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,27 +7,6 @@ version_files = [ ".pre-commit-config.yaml:rev:.+Commitizen" ] -[tool.black] -line-length = 88 -include = '\.pyi?$' -exclude = ''' -/( - \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - # The following are specific to Black, you probably don't want those. - | blib2to3 - | tests/data - | profiling -)/ -''' - [tool.poetry] name = "commitizen" version = "2.42.1" @@ -44,14 +23,22 @@ classifiers = [ "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: Implementation :: CPython", ] [tool.poetry.dependencies] -python = "^3.6.2" +python = "^3.7" questionary = "^1.4.0" decli = "^0.5.2" colorama = "^0.4.1" -termcolor = ">=1.1,<3" +termcolor = ">= 1.1, < 3" packaging = ">=19" tomlkit = ">=0.5.3,<1.0.0" jinja2 = ">=2.10.3" @@ -59,38 +46,48 @@ pyyaml = ">=3.08" argcomplete = ">=1.12.1,<2.2" typing-extensions = { version = "^4.0.1", python = "<3.8" } charset-normalizer = ">=2.1.0,<4" +# Use the Python 3.11 compatible API: https://github.com/python/importlib_metadata#compatibility +importlib_metadata = { version = ">=4.13,<5"} -[tool.poetry.dev-dependencies] -ipython = "^7.2" +[tool.poetry.group.dev.dependencies] +ipython = "^7.34" # test -pytest = "^7.0.0" -pytest-cov = "^2.6" -pytest-mock = "^2.0" -codecov = "^2.0" +pytest = "^7.2.0" +pytest-cov = "^4.0" +pytest-mock = "^3.10" pytest-regressions = "^2.4.0" pytest-freezer = "^0.4.6" +pytest-xdist = "^3.1.0" # code formatter -black = "^21.12b0" -isort = "^5.7.0" +black = "^22.10" # linter -flake8 = "^3.6" +ruff = "^0.0.262" pre-commit = "^2.6.0" mypy = "^0.931" types-PyYAML = "^5.4.3" types-termcolor = "^0.1.1" # documentation -mkdocs = "^1.0" -mkdocs-material = "^4.1" -pydocstyle = "^5.0.2" -pytest-xdist = "^2.5.0" +mkdocs = "^1.4.2" +mkdocs-material = "^9.1.6" + [tool.poetry.scripts] cz = "commitizen.cli:main" git-cz = "commitizen.cli:main" -[tool.isort] -profile = "black" -known_first_party = ["commitizen", "tests"] +[tool.poetry.plugins."commitizen.plugin"] +cz_conventional_commits = "commitizen.cz.conventional_commits:ConventionalCommitsCz" +cz_jira = "commitizen.cz.jira:JiraSmartCz" +cz_customize = "commitizen.cz.customize:CustomizeCommitsCz" + +[tool.poetry.plugins."commitizen.provider"] +cargo = "commitizen.providers:CargoProvider" +commitizen = "commitizen.providers:CommitizenProvider" +composer = "commitizen.providers:ComposerProvider" +npm = "commitizen.providers:NpmProvider" +pep621 = "commitizen.providers:Pep621Provider" +poetry = "commitizen.providers:PoetryProvider" +scm = "commitizen.providers:ScmProvider" [tool.coverage] [tool.coverage.report] @@ -114,6 +111,7 @@ known_first_party = ["commitizen", "tests"] omit = [ 'env/*', 'venv/*', + '.venv/*', '*/virtualenv/*', '*/virtualenvs/*', '*/tests/*' @@ -122,3 +120,30 @@ known_first_party = ["commitizen", "tests"] [build-system] requires = ["poetry_core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +addopts = "--strict-markers" + +[tool.ruff] +line-length = 88 +ignore = [ + "E501", + "D1", + "D415", +] + +[tool.ruff.isort] +known-first-party = ["commitizen", "tests"] + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.mypy] +files = "commitizen" +ignore_missing_imports = true +disallow_untyped_decorators = true +disallow_subclassing_any = true +warn_return_any = true +warn_redundant_casts = true +warn_unused_ignores = true +warn_unused_configs = true diff --git a/scripts/format b/scripts/format index 9d300e964b..e6a82477a9 100755 --- a/scripts/format +++ b/scripts/format @@ -5,5 +5,5 @@ export PREFIX="poetry run python -m " set -x -${PREFIX}isort commitizen tests +${PREFIX}ruff commitizen tests --fix ${PREFIX}black commitizen tests diff --git a/scripts/test b/scripts/test index d57bdddc48..e2fa065142 100755 --- a/scripts/test +++ b/scripts/test @@ -4,10 +4,8 @@ set -e export PREFIX='poetry run python -m ' export REGEX='^(?![.]|venv).*' -${PREFIX}pytest -n 3 --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/ +${PREFIX}pytest -n 3 --dist=loadfile --cov-report term-missing --cov-report=xml:coverage.xml --cov=commitizen tests/ ${PREFIX}black commitizen tests --check -${PREFIX}isort --check-only commitizen tests -${PREFIX}flake8 commitizen/ tests/ +${PREFIX}ruff commitizen/ tests/ ${PREFIX}mypy commitizen/ tests/ -${PREFIX}pydocstyle --convention=google --add-ignore=D1,D415 --match-dir='"${REGEX}"' ${PREFIX}commitizen -nr 3 check --rev-range origin/master.. diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 0c3a421421..0000000000 --- a/setup.cfg +++ /dev/null @@ -1,39 +0,0 @@ -[tool:pytest] -addopts = --strict-markers -norecursedirs = .* build dist CVS _darcs {arch} *.egg venv env virtualenv - - -[mypy] -files = commitizen -ignore_missing_imports = true -# disallow_untyped_calls = True -# disallow_untyped_defs = True -# disallow_incomplete_defs = True -disallow_untyped_decorators = true -# disallow_any_generics = True -disallow_subclassing_any = true -warn_return_any = True -warn_redundant_casts = true -warn_unused_ignores = true -warn_unused_configs = true -[mypy-tests.*] -disallow_untyped_decorators = false - -[flake8] -ignore = - # F632: use ==/!= to compare str, bytes, and int literals - F632, - # W503: Line break occurred before a binary operator - W503, - # E501: Line too long - E501, - # E203: Whitespace before ':' (for black) - E203 -exclude = - .git, - __pycache__, - docs/source/conf.py, - build, - dist -max-line-length = 88 -max-complexity = 12 diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 7b5a44b745..59a0e01ef3 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -278,7 +278,8 @@ def test_bump_on_git_with_hooks_no_verify_enabled(mocker: MockFixture): assert tag_exists is True -def test_bump_when_bumpping_is_not_support(mocker: MockFixture, tmp_commitizen_project): +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_when_bumpping_is_not_support(mocker: MockFixture): create_file_and_commit( "feat: new user interface\n\nBREAKING CHANGE: age is no longer supported" ) @@ -432,7 +433,8 @@ def test_bump_local_version(mocker: MockFixture, tmp_commitizen_project): assert "4.5.1+0.2.0" in f.read() -def test_bump_dry_run(mocker: MockFixture, capsys, tmp_commitizen_project): +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_dry_run(mocker: MockFixture, capsys): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes", "--dry-run"] @@ -475,7 +477,7 @@ def test_none_increment_exit_is_exception(): @pytest.mark.usefixtures("tmp_commitizen_project") def test_none_increment_should_not_call_git_tag_and_error_code_is_not_zero( - mocker: MockFixture, tmp_commitizen_project + mocker: MockFixture, ): create_file_and_commit("test(test_get_all_droplets): fix bad comparison test") testargs = ["cz", "bump", "--yes"] @@ -531,9 +533,8 @@ def test_bump_with_changelog_config(mocker: MockFixture, changelog_path, config_ assert "0.2.0" in out -def test_prevent_prerelease_when_no_increment_detected( - mocker: MockFixture, capsys, tmp_commitizen_project -): +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_prevent_prerelease_when_no_increment_detected(mocker: MockFixture, capsys): create_file_and_commit("feat: new file") testargs = ["cz", "bump", "--yes"] @@ -690,6 +691,7 @@ def test_bump_changelog_command_commits_untracked_changelog_and_version_files( ["cz", "bump", "--increment", "PATCH", "1.2.3"], ], ) +@pytest.mark.usefixtures("tmp_commitizen_project") def test_bump_invalid_manual_args_raises_exception(mocker, testargs): mocker.patch.object(sys, "argv", testargs) @@ -831,3 +833,150 @@ def test_bump_manual_version_disallows_prerelease_offset(mocker): "--prerelease-offset cannot be combined with MANUAL_VERSION" ) assert expected_error_message in str(excinfo.value) + + +@pytest.mark.usefixtures("tmp_git_project") +def test_bump_use_version_provider(mocker: MockFixture): + mock = mocker.MagicMock(name="provider") + mock.get_version.return_value = "0.0.0" + get_provider = mocker.patch( + "commitizen.commands.bump.get_provider", return_value=mock + ) + + create_file_and_commit("fix: fake commit") + testargs = ["cz", "bump", "--yes", "--changelog"] + mocker.patch.object(sys, "argv", testargs) + + cli.main() + + assert git.tag_exist("0.0.1") + get_provider.assert_called_once() + mock.get_version.assert_called_once() + mock.set_version.assert_called_once_with("0.0.1") + + +def test_bump_command_prelease_version_type_via_cli( + tmp_commitizen_project_initial, mocker: MockFixture +): + tmp_commitizen_project = tmp_commitizen_project_initial() + tmp_version_file = tmp_commitizen_project.join("__version__.py") + tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") + + testargs = [ + "cz", + "bump", + "--prerelease", + "alpha", + "--yes", + "--version-type", + "semver", + ] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0-a0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0-a0" in f.read() + + # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE + testargs = ["cz", "bump", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0" in f.read() + + +def test_bump_command_prelease_version_type_via_config( + tmp_commitizen_project_initial, mocker: MockFixture +): + tmp_commitizen_project = tmp_commitizen_project_initial( + config_extra='version_type = "semver"\n', + ) + tmp_version_file = tmp_commitizen_project.join("__version__.py") + tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") + + testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0-a0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0-a0" in f.read() + + testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0-a1") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0-a1" in f.read() + + # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE + testargs = ["cz", "bump", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("0.2.0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0" in f.read() + + +def test_bump_command_prelease_version_type_check_old_tags( + tmp_commitizen_project_initial, mocker: MockFixture +): + tmp_commitizen_project = tmp_commitizen_project_initial( + config_extra=('tag_format = "v$version"\nversion_type = "semver"\n'), + ) + tmp_version_file = tmp_commitizen_project.join("__version__.py") + tmp_commitizen_cfg_file = tmp_commitizen_project.join("pyproject.toml") + + testargs = ["cz", "bump", "--prerelease", "alpha", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("v0.2.0-a0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0-a0" in f.read() + + testargs = ["cz", "bump", "--prerelease", "alpha"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("v0.2.0-a1") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0-a1" in f.read() + + # PRERELEASE BUMP CREATES VERSION WITHOUT PRERELEASE + testargs = ["cz", "bump"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + tag_exists = git.tag_exist("v0.2.0") + assert tag_exists is True + + for version_file in [tmp_version_file, tmp_commitizen_cfg_file]: + with open(version_file, "r") as f: + assert "0.2.0" in f.read() diff --git a/tests/commands/test_changelog_command.py b/tests/commands/test_changelog_command.py index f37915baa2..bdc384c3ca 100644 --- a/tests/commands/test_changelog_command.py +++ b/tests/commands/test_changelog_command.py @@ -13,7 +13,14 @@ NotAGitProjectError, NotAllowed, ) -from tests.utils import create_file_and_commit, wait_for_tag +from tests.utils import ( + create_branch, + create_file_and_commit, + get_current_branch, + merge_branch, + switch_branch, + wait_for_tag, +) @pytest.mark.usefixtures("tmp_commitizen_project") @@ -268,6 +275,75 @@ def test_changelog_hook_customize(mocker: MockFixture, config_customize): changelog_hook_mock.assert_called_with(full_changelog, full_changelog) +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_changelog_with_non_linear_merges_commit_order( + mocker: MockFixture, config_customize +): + """Test that commits merged non-linearly are correctly ordered in the changelog + + A typical scenario is having two branches from main like so: + * feat: I will be merged first - (2023-03-01 11:35:51 +0100) | (branchB) + | * feat: I will be merged second - (2023-03-01 11:35:22 +0100) | (branchA) + |/ + * feat: initial commit - (2023-03-01 11:34:54 +0100) | (HEAD -> main) + + And merging them, for example in the reverse order they were created on would give the following: + * Merge branch 'branchA' - (2023-03-01 11:42:59 +0100) | (HEAD -> main) + |\ + | * feat: I will be merged second - (2023-03-01 11:35:22 +0100) | (branchA) + * | feat: I will be merged first - (2023-03-01 11:35:51 +0100) | (branchB) + |/ + * feat: initial commit - (2023-03-01 11:34:54 +0100) | + + In this case we want the changelog to reflect the topological order of commits, + i.e. the order in which they were merged into the main branch + + So the above example should result in the following: + ## Unreleased + + ### Feat + - I will be merged second + - I will be merged first + - initial commit + """ + changelog_hook_mock = mocker.Mock() + changelog_hook_mock.return_value = "cool changelog hook" + + create_file_and_commit("feat: initial commit") + + main_branch = get_current_branch() + + create_branch("branchA") + create_branch("branchB") + + switch_branch("branchA") + create_file_and_commit("feat: I will be merged second") + + switch_branch("branchB") + create_file_and_commit("feat: I will be merged first") + + # Note we merge branches opposite order than author_date + switch_branch(main_branch) + merge_branch("branchB") + merge_branch("branchA") + + changelog = Changelog( + config_customize, + {"unreleased_version": None, "incremental": True, "dry_run": False}, + ) + mocker.patch.object(changelog.cz, "changelog_hook", changelog_hook_mock) + changelog() + full_changelog = "\ +## Unreleased\n\n\ +\ +### Feat\n\n\ +- I will be merged second\n\ +- I will be merged first\n\ +- initial commit\n" + + changelog_hook_mock.assert_called_with(full_changelog, full_changelog) + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_multiple_incremental_do_not_add_new_lines( mocker: MockFixture, capsys, changelog_path, file_regression @@ -345,6 +421,7 @@ def test_changelog_without_revision(mocker: MockFixture, tmp_commitizen_project) cli.main() +@pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_incremental_with_revision(mocker): """combining incremental with a revision doesn't make sense""" testargs = ["cz", "changelog", "--incremental", "0.2.0"] @@ -461,6 +538,40 @@ def test_changelog_config_flag_increment( file_regression.check(out, extension=".md") +@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"]) +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_changelog_config_flag_merge_prerelease( + mocker: MockFixture, changelog_path, config_path, file_regression, test_input +): + with open(config_path, "a") as f: + f.write("changelog_merge_prerelease = true\n") + + create_file_and_commit("irrelevant commit") + mocker.patch("commitizen.git.GitTag.date", "1970-01-01") + git.tag("1.0.0") + + create_file_and_commit("feat: add new output") + create_file_and_commit("fix: output glitch") + + testargs = ["cz", "bump", "--prerelease", test_input, "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + create_file_and_commit("fix: mama gotta work") + create_file_and_commit("feat: add more stuff") + create_file_and_commit("Merge into master") + + testargs = ["cz", "changelog"] + + mocker.patch.object(sys, "argv", testargs) + cli.main() + + with open(changelog_path, "r") as f: + out = f.read() + + file_regression.check(out, extension=".md") + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_config_start_rev_option( mocker: MockFixture, capsys, config_path, file_regression @@ -549,6 +660,80 @@ def test_changelog_incremental_with_release_candidate_version( file_regression.check(out, extension=".md") +@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"]) +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_changelog_release_candidate_version_with_merge_prerelease( + mocker: MockFixture, changelog_path, file_regression, test_input +): + """Fix #357""" + with open(changelog_path, "w") as f: + f.write(KEEP_A_CHANGELOG) + create_file_and_commit("irrelevant commit") + mocker.patch("commitizen.git.GitTag.date", "1970-01-01") + git.tag("1.0.0") + + create_file_and_commit("feat: add new output") + create_file_and_commit("fix: output glitch") + + testargs = ["cz", "bump", "--prerelease", test_input, "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + create_file_and_commit("fix: mama gotta work") + create_file_and_commit("feat: add more stuff") + create_file_and_commit("Merge into master") + + testargs = ["cz", "changelog", "--merge-prerelease"] + + mocker.patch.object(sys, "argv", testargs) + cli.main() + + with open(changelog_path, "r") as f: + out = f.read() + + file_regression.check(out, extension=".md") + + +@pytest.mark.parametrize("test_input", ["rc", "alpha", "beta"]) +@pytest.mark.usefixtures("tmp_commitizen_project") +@pytest.mark.freeze_time("2023-04-16") +def test_changelog_incremental_with_merge_prerelease( + mocker: MockFixture, changelog_path, file_regression, test_input +): + """Fix #357""" + with open(changelog_path, "w") as f: + f.write(KEEP_A_CHANGELOG) + create_file_and_commit("irrelevant commit") + mocker.patch("commitizen.git.GitTag.date", "1970-01-01") + git.tag("1.0.0") + + create_file_and_commit("feat: add new output") + + testargs = ["cz", "bump", "--prerelease", test_input, "--yes", "--changelog"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + create_file_and_commit("fix: output glitch") + + testargs = ["cz", "bump", "--prerelease", test_input, "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + + create_file_and_commit("fix: mama gotta work") + create_file_and_commit("feat: add more stuff") + create_file_and_commit("Merge into master") + + testargs = ["cz", "changelog", "--merge-prerelease", "--incremental"] + + mocker.patch.object(sys, "argv", testargs) + cli.main() + + with open(changelog_path, "r") as f: + out = f.read() + + file_regression.check(out, extension=".md") + + @pytest.mark.usefixtures("tmp_commitizen_project") def test_changelog_with_filename_as_empty_string( mocker: MockFixture, changelog_path, config_path @@ -968,3 +1153,60 @@ def test_empty_commit_list(mocker): mocker.patch.object(sys, "argv", testargs) with pytest.raises(NoCommitsFoundError): cli.main() + + +@pytest.mark.usefixtures("tmp_commitizen_project") +@pytest.mark.freeze_time("2022-02-13") +def test_changelog_prerelease_rev_with_use_version_type_semver( + mocker: MockFixture, capsys, config_path, changelog_path, file_regression +): + mocker.patch("commitizen.git.GitTag.date", "2022-02-13") + + with open(config_path, "a") as f: + f.write('tag_format = "$version"\n' 'version_type = "semver"') + + # create commit and tag + create_file_and_commit("feat: new file") + testargs = ["cz", "bump", "--yes"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + wait_for_tag() + + create_file_and_commit("feat: after 0.2.0") + create_file_and_commit("feat: another feature") + + testargs = ["cz", "bump", "--yes", "--prerelease", "alpha"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + capsys.readouterr() + wait_for_tag() + + tag_exists = git.tag_exist("0.3.0-a0") + assert tag_exists is True + + testargs = ["cz", "changelog", "0.3.0-a0", "--dry-run"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(DryRunExit): + cli.main() + + out, _ = capsys.readouterr() + + file_regression.check(out, extension=".md") + + testargs = ["cz", "bump", "--yes", "--prerelease", "alpha"] + mocker.patch.object(sys, "argv", testargs) + cli.main() + capsys.readouterr() + wait_for_tag() + + tag_exists = git.tag_exist("0.3.0-a1") + assert tag_exists is True + + testargs = ["cz", "changelog", "0.3.0-a1", "--dry-run"] + mocker.patch.object(sys, "argv", testargs) + with pytest.raises(DryRunExit): + cli.main() + + out, _ = capsys.readouterr() + + file_regression.check(out, extension=".second-prerelease.md") diff --git a/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_alpha_.md b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_alpha_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_alpha_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_beta_.md b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_beta_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_beta_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_rc_.md b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_rc_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_config_flag_merge_prerelease_rc_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_alpha_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_alpha_.md new file mode 100644 index 0000000000..8e81f62572 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_alpha_.md @@ -0,0 +1,37 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Feat + +- add more stuff + +### Fix + +- mama gotta work +- output glitch + +## 0.2.0a0 (2023-04-16) + +### Feat + +- add new output + +## [1.0.0] - 2017-06-20 +### Added +- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). +- Version navigation. + +### Changed +- Start using "changelog" over "change log" since it's the common usage. + +### Removed +- Section about "changelog" vs "CHANGELOG". + +## [0.3.0] - 2015-12-03 +### Added +- RU translation from [@aishek](https://github.com/aishek). diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_beta_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_beta_.md new file mode 100644 index 0000000000..65f14c068e --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_beta_.md @@ -0,0 +1,37 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Feat + +- add more stuff + +### Fix + +- mama gotta work +- output glitch + +## 0.2.0b0 (2023-04-16) + +### Feat + +- add new output + +## [1.0.0] - 2017-06-20 +### Added +- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). +- Version navigation. + +### Changed +- Start using "changelog" over "change log" since it's the common usage. + +### Removed +- Section about "changelog" vs "CHANGELOG". + +## [0.3.0] - 2015-12-03 +### Added +- RU translation from [@aishek](https://github.com/aishek). diff --git a/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_rc_.md b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_rc_.md new file mode 100644 index 0000000000..0987e1127d --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_incremental_with_merge_prerelease_rc_.md @@ -0,0 +1,37 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +### Feat + +- add more stuff + +### Fix + +- mama gotta work +- output glitch + +## 0.2.0rc0 (2023-04-16) + +### Feat + +- add new output + +## [1.0.0] - 2017-06-20 +### Added +- New visual identity by [@tylerfortune8](https://github.com/tylerfortune8). +- Version navigation. + +### Changed +- Start using "changelog" over "change log" since it's the common usage. + +### Removed +- Section about "changelog" vs "CHANGELOG". + +## [0.3.0] - 2015-12-03 +### Added +- RU translation from [@aishek](https://github.com/aishek). diff --git a/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.md b/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.md new file mode 100644 index 0000000000..9a66210a74 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.md @@ -0,0 +1,7 @@ +## 0.3.0-a0 (2022-02-13) + +### Feat + +- another feature +- after 0.2.0 + diff --git a/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.second-prerelease.md b/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.second-prerelease.md new file mode 100644 index 0000000000..09fd10ee8d --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_prerelease_rev_with_use_version_type_semver.second-prerelease.md @@ -0,0 +1,2 @@ +## 0.3.0-a1 (2022-02-13) + diff --git a/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_alpha_.md b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_alpha_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_alpha_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_beta_.md b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_beta_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_beta_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_rc_.md b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_rc_.md new file mode 100644 index 0000000000..dca7824480 --- /dev/null +++ b/tests/commands/test_changelog_command/test_changelog_release_candidate_version_with_merge_prerelease_rc_.md @@ -0,0 +1,13 @@ +## Unreleased + +### Feat + +- add more stuff +- add new output + +### Fix + +- mama gotta work +- output glitch + +## 1.0.0 (1970-01-01) diff --git a/tests/commands/test_commit_command.py b/tests/commands/test_commit_command.py index 05e72cfabd..dd62fafe85 100644 --- a/tests/commands/test_commit_command.py +++ b/tests/commands/test_commit_command.py @@ -17,9 +17,10 @@ @pytest.fixture -def staging_is_clean(mocker: MockFixture): +def staging_is_clean(mocker: MockFixture, tmp_git_project): is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") is_staging_clean_mock.return_value = False + return tmp_git_project @pytest.mark.usefixtures("staging_is_clean") @@ -128,6 +129,7 @@ def test_commit_command_with_signoff_option(config, mocker: MockFixture): success_mock.assert_called_once() +@pytest.mark.usefixtures("tmp_git_project") def test_commit_when_nothing_to_commit(config, mocker: MockFixture): is_staging_clean_mock = mocker.patch("commitizen.git.is_staging_clean") is_staging_clean_mock.return_value = True diff --git a/tests/commands/test_init_command.py b/tests/commands/test_init_command.py index 43b03f112d..8f9bb6b238 100644 --- a/tests/commands/test_init_command.py +++ b/tests/commands/test_init_command.py @@ -18,6 +18,9 @@ def __init__(self, expected_return): def ask(self): return self.expected_return + def unsafe_ask(self): + return self.expected_return + pre_commit_config_filename = ".pre-commit-config.yaml" cz_hook_config = { @@ -32,15 +35,21 @@ def ask(self): expected_config = ( "[tool.commitizen]\n" 'name = "cz_conventional_commits"\n' - 'version = "0.0.1"\n' 'tag_format = "$version"\n' + 'version_type = "semver"\n' + 'version = "0.0.1"\n' + "update_changelog_on_bump = true\n" + "major_version_zero = true\n" ) EXPECTED_DICT_CONFIG = { "commitizen": { "name": "cz_conventional_commits", - "version": "0.0.1", "tag_format": "$version", + "version_type": "semver", + "version": "0.0.1", + "update_changelog_on_bump": True, + "major_version_zero": True, } } @@ -51,6 +60,8 @@ def test_init_without_setup_pre_commit_hook(tmpdir, mocker: MockFixture, config) side_effect=[ FakeQuestion("pyproject.toml"), FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), ], ) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) @@ -88,6 +99,7 @@ def test_init_without_choosing_tag(config, mocker: MockFixture, tmpdir): side_effect=[ FakeQuestion("pyproject.toml"), FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), FakeQuestion(""), ], ) @@ -109,7 +121,7 @@ def test_executed_pre_commit_command(config): def pre_commit_installed(mocker: MockFixture): # Assume the `pre-commit` is installed mocker.patch( - "commitizen.commands.init.Init._search_pre_commit", + "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", return_value=True, ) # And installation success (i.e. no exception raised) @@ -126,6 +138,8 @@ def default_choice(request, mocker: MockFixture): side_effect=[ FakeQuestion(request.param), FakeQuestion("cz_conventional_commits"), + FakeQuestion("commitizen"), + FakeQuestion("semver"), ], ) mocker.patch("questionary.confirm", return_value=FakeQuestion(True)) @@ -212,7 +226,7 @@ def test_pre_commit_not_installed( ): # Assume `pre-commit` is not installed mocker.patch( - "commitizen.commands.init.Init._search_pre_commit", + "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", return_value=False, ) with tmpdir.as_cwd(): @@ -224,7 +238,7 @@ def test_pre_commit_exec_failed( ): # Assume `pre-commit` is installed mocker.patch( - "commitizen.commands.init.Init._search_pre_commit", + "commitizen.commands.init.ProjectInfo.is_pre_commit_installed", return_value=True, ) # But pre-commit installation will fail diff --git a/tests/commands/test_version_command.py b/tests/commands/test_version_command.py index 7e6ec3c851..3f9de50d00 100644 --- a/tests/commands/test_version_command.py +++ b/tests/commands/test_version_command.py @@ -1,8 +1,12 @@ import platform import sys +import pytest +from pytest_mock import MockerFixture + from commitizen import commands from commitizen.__version__ import __version__ +from commitizen.config.base_config import BaseConfig def test_version_for_showing_project_version(config, capsys): @@ -70,3 +74,35 @@ def test_version_for_showing_commitizen_system_info(config, capsys): assert f"Commitizen Version: {__version__}" in captured.out assert f"Python Version: {sys.version}" in captured.out assert f"Operating System: {platform.system()}" in captured.out + + +@pytest.mark.parametrize("project", (True, False)) +@pytest.mark.usefixtures("tmp_git_project") +def test_version_use_version_provider( + mocker: MockerFixture, + config: BaseConfig, + capsys: pytest.CaptureFixture, + project: bool, +): + version = "0.0.0" + mock = mocker.MagicMock(name="provider") + mock.get_version.return_value = version + get_provider = mocker.patch( + "commitizen.commands.version.get_provider", return_value=mock + ) + + commands.Version( + config, + { + "report": False, + "project": project, + "commitizen": False, + "verbose": not project, + }, + )() + captured = capsys.readouterr() + + assert version in captured.out + get_provider.assert_called_once() + mock.get_version.assert_called_once() + mock.set_version.assert_not_called() diff --git a/tests/conftest.py b/tests/conftest.py index bb18531699..4daae7b80b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,11 +1,37 @@ import os import re import tempfile +from pathlib import Path +from typing import Optional import pytest from commitizen import cmd, defaults from commitizen.config import BaseConfig +from tests.utils import create_file_and_commit + +SIGNER = "GitHub Action" +SIGNER_MAIL = "action@github.com" + + +@pytest.fixture(autouse=True) +def git_sandbox(monkeypatch: pytest.MonkeyPatch, tmp_path: Path): + """Ensure git commands are executed without the current user settings""" + # Clear any GIT_ prefixed environment variable + for var in os.environ: + if var.startswith("GIT_"): + monkeypatch.delenv(var) + + # Define a dedicated temporary git config + gitconfig = tmp_path / ".git" / "config" + if not gitconfig.parent.exists(): + gitconfig.parent.mkdir() + monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(gitconfig)) + r = cmd.run(f"git config --file {gitconfig} user.name {SIGNER}") + assert r.return_code == 0, r.err + r = cmd.run(f"git config --file {gitconfig} user.email {SIGNER_MAIL}") + assert r.return_code == 0, r.err + cmd.run("git config --global init.defaultBranch master") @pytest.fixture(scope="function") @@ -18,11 +44,39 @@ def tmp_git_project(tmpdir): @pytest.fixture(scope="function") def tmp_commitizen_project(tmp_git_project): - with tmp_git_project.as_cwd(): - tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml") - tmp_commitizen_cfg_file.write("[tool.commitizen]\n" 'version="0.1.0"\n') + tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml") + tmp_commitizen_cfg_file.write("[tool.commitizen]\n" 'version="0.1.0"\n') - yield tmp_git_project + yield tmp_git_project + + +@pytest.fixture(scope="function") +def tmp_commitizen_project_initial(tmp_git_project): + def _initial( + config_extra: Optional[str] = None, + version="0.1.0", + initial_commit="feat: new user interface", + ): + with tmp_git_project.as_cwd(): + tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml") + tmp_commitizen_cfg_file.write( + f"[tool.commitizen]\n" f'version="{version}"\n' + ) + tmp_version_file = tmp_git_project.join("__version__.py") + tmp_version_file.write(version) + tmp_commitizen_cfg_file = tmp_git_project.join("pyproject.toml") + tmp_version_file_string = str(tmp_version_file).replace("\\", "/") + tmp_commitizen_cfg_file.write( + f"{tmp_commitizen_cfg_file.read()}\n" + f'version_files = ["{tmp_version_file_string}"]\n' + ) + if config_extra: + tmp_commitizen_cfg_file.write(config_extra, mode="a") + create_file_and_commit(initial_commit) + + return tmp_git_project + + yield _initial def _get_gpg_keyid(signer_mail): @@ -36,9 +90,6 @@ def _get_gpg_keyid(signer_mail): @pytest.fixture(scope="function") def tmp_commitizen_project_with_gpg(tmp_commitizen_project): - signer = "GitHub Action" - signer_mail = "action@github.com" - # create a temporary GPGHOME to store a temporary keyring. # Home path must be less than 104 characters gpg_home = tempfile.TemporaryDirectory(suffix="_cz") @@ -47,17 +98,15 @@ def tmp_commitizen_project_with_gpg(tmp_commitizen_project): # create a key (a keyring will be generated within GPUPGHOME) c = cmd.run( - f"gpg --batch --yes --debug-quick-random --passphrase '' --quick-gen-key '{signer} {signer_mail}'" + f"gpg --batch --yes --debug-quick-random --passphrase '' --quick-gen-key '{SIGNER} {SIGNER_MAIL}'" ) if c.return_code != 0: raise Exception(f"gpg keygen failed with err: '{c.err}'") - key_id = _get_gpg_keyid(signer_mail) + key_id = _get_gpg_keyid(SIGNER_MAIL) assert key_id - # configure git + # configure git to use gpg signing cmd.run("git config commit.gpgsign true") - cmd.run(f"git config user.name {signer}") - cmd.run(f"git config user.email {signer_mail}") cmd.run(f"git config user.signingkey {key_id}") yield tmp_commitizen_project diff --git a/tests/test_bump_create_commit_message.py b/tests/test_bump_create_commit_message.py index 6fc31778d7..0609e1a236 100644 --- a/tests/test_bump_create_commit_message.py +++ b/tests/test_bump_create_commit_message.py @@ -1,4 +1,3 @@ -import os import sys from pathlib import Path from textwrap import dedent @@ -29,9 +28,8 @@ def test_create_tag(test_input, expected): @pytest.mark.parametrize("retry", (True, False)) -def test_bump_pre_commit_changelog( - tmp_commitizen_project, mocker: MockFixture, freezer, retry -): +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_pre_commit_changelog(mocker: MockFixture, freezer, retry): freezer.move_to("2022-04-01") testargs = ["cz", "bump", "--changelog", "--yes"] if retry: @@ -39,10 +37,10 @@ def test_bump_pre_commit_changelog( else: pytest.xfail("it will fail because pre-commit will reformat CHANGELOG.md") mocker.patch.object(sys, "argv", testargs) - with tmp_commitizen_project.as_cwd(): - # Configure prettier as a pre-commit hook - Path(".pre-commit-config.yaml").write_text( - """ + # Configure prettier as a pre-commit hook + Path(".pre-commit-config.yaml").write_text( + dedent( + """\ repos: - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.6.2 @@ -51,44 +49,43 @@ def test_bump_pre_commit_changelog( stages: [commit] """ ) - # Prettier inherits editorconfig - Path(".editorconfig").write_text( - """ + ) + # Prettier inherits editorconfig + Path(".editorconfig").write_text( + dedent( + """\ [*] indent_size = 4 """ ) - cmd.run("git add -A") - if os.name == "nt": - cmd.run('git commit -m "fix: _test"') - else: - cmd.run("git commit -m 'fix: _test'") - cmd.run("pre-commit install") - cli.main() - # Pre-commit fixed last line adding extra indent and "\" char - assert Path("CHANGELOG.md").read_text() == dedent( - """\ - ## 0.1.1 (2022-04-01) + ) + cmd.run("git add -A") + cmd.run('git commit -m "fix: _test"') + cmd.run("pre-commit install") + cli.main() + # Pre-commit fixed last line adding extra indent and "\" char + assert Path("CHANGELOG.md").read_text() == dedent( + """\ + ## 0.1.1 (2022-04-01) - ### Fix + ### Fix - - \\_test - """ - ) + - \\_test + """ + ) @pytest.mark.parametrize("retry", (True, False)) -def test_bump_pre_commit_changelog_fails_always( - tmp_commitizen_project, mocker: MockFixture, freezer, retry -): +@pytest.mark.usefixtures("tmp_commitizen_project") +def test_bump_pre_commit_changelog_fails_always(mocker: MockFixture, freezer, retry): freezer.move_to("2022-04-01") testargs = ["cz", "bump", "--changelog", "--yes"] if retry: testargs.append("--retry") mocker.patch.object(sys, "argv", testargs) - with tmp_commitizen_project.as_cwd(): - Path(".pre-commit-config.yaml").write_text( - """ + Path(".pre-commit-config.yaml").write_text( + dedent( + """\ repos: - repo: local hooks: @@ -99,11 +96,9 @@ def test_bump_pre_commit_changelog_fails_always( files: CHANGELOG.md """ ) - cmd.run("git add -A") - if os.name == "nt": - cmd.run('git commit -m "feat: forbid changelogs"') - else: - cmd.run("git commit -m 'feat: forbid changelogs'") - cmd.run("pre-commit install") - with pytest.raises(exceptions.BumpCommitFailedError): - cli.main() + ) + cmd.run("git add -A") + cmd.run('git commit -m "feat: forbid changelogs"') + cmd.run("pre-commit install") + with pytest.raises(exceptions.BumpCommitFailedError): + cli.main() diff --git a/tests/test_bump_find_increment.py b/tests/test_bump_find_increment.py index 826490a3ba..32a978556c 100644 --- a/tests/test_bump_find_increment.py +++ b/tests/test_bump_find_increment.py @@ -5,7 +5,7 @@ import pytest from commitizen import bump -from commitizen.cz import ConventionalCommitsCz +from commitizen.cz.conventional_commits import ConventionalCommitsCz from commitizen.git import GitCommit NONE_INCREMENT_CC = ["docs(README): motivation", "ci: added travis"] diff --git a/tests/test_bump_find_version.py b/tests/test_bump_find_version.py index f12b87f1eb..394c8fd61a 100644 --- a/tests/test_bump_find_version.py +++ b/tests/test_bump_find_version.py @@ -91,16 +91,13 @@ def test_generate_version(test_input, expected): prerelease = test_input[2] prerelease_offset = test_input[3] devrelease = test_input[4] - assert ( - generate_version( - current_version, - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - ) - == Version(expected) - ) + assert generate_version( + current_version, + increment=increment, + prerelease=prerelease, + prerelease_offset=prerelease_offset, + devrelease=devrelease, + ) == Version(expected) @pytest.mark.parametrize( @@ -114,14 +111,11 @@ def test_generate_version_local(test_input, expected): prerelease_offset = test_input[3] devrelease = test_input[4] is_local_version = True - assert ( - generate_version( - current_version, - increment=increment, - prerelease=prerelease, - prerelease_offset=prerelease_offset, - devrelease=devrelease, - is_local_version=is_local_version, - ) - == Version(expected) - ) + assert generate_version( + current_version, + increment=increment, + prerelease=prerelease, + prerelease_offset=prerelease_offset, + devrelease=devrelease, + is_local_version=is_local_version, + ) == Version(expected) diff --git a/tests/test_bump_find_version_type_semver.py b/tests/test_bump_find_version_type_semver.py new file mode 100644 index 0000000000..633442f9ee --- /dev/null +++ b/tests/test_bump_find_version_type_semver.py @@ -0,0 +1,102 @@ +import itertools + +import pytest + +from commitizen.bump import generate_version +from commitizen.version_types import SemVerVersion + +simple_flow = [ + (("0.1.0", "PATCH", None, 0, None), "0.1.1"), + (("0.1.0", "PATCH", None, 0, 1), "0.1.1-dev1"), + (("0.1.1", "MINOR", None, 0, None), "0.2.0"), + (("0.2.0", "MINOR", None, 0, None), "0.3.0"), + (("0.2.0", "MINOR", None, 0, 1), "0.3.0-dev1"), + (("0.3.0", "PATCH", None, 0, None), "0.3.1"), + (("0.3.0", "PATCH", "alpha", 0, None), "0.3.1-a0"), + (("0.3.1a0", None, "alpha", 0, None), "0.3.1-a1"), + (("0.3.0", "PATCH", "alpha", 1, None), "0.3.1-a1"), + (("0.3.1a0", None, "alpha", 1, None), "0.3.1-a1"), + (("0.3.1a0", None, None, 0, None), "0.3.1"), + (("0.3.1", "PATCH", None, 0, None), "0.3.2"), + (("0.4.2", "MAJOR", "alpha", 0, None), "1.0.0-a0"), + (("1.0.0a0", None, "alpha", 0, None), "1.0.0-a1"), + (("1.0.0a1", None, "alpha", 0, None), "1.0.0-a2"), + (("1.0.0a1", None, "alpha", 0, 1), "1.0.0-a2-dev1"), + (("1.0.0a2.dev0", None, "alpha", 0, 1), "1.0.0-a3-dev1"), + (("1.0.0a2.dev0", None, "alpha", 0, 0), "1.0.0-a3-dev0"), + (("1.0.0a1", None, "beta", 0, None), "1.0.0-b0"), + (("1.0.0b0", None, "beta", 0, None), "1.0.0-b1"), + (("1.0.0b1", None, "rc", 0, None), "1.0.0-rc0"), + (("1.0.0rc0", None, "rc", 0, None), "1.0.0-rc1"), + (("1.0.0rc0", None, "rc", 0, 1), "1.0.0-rc1-dev1"), + (("1.0.0rc0", "PATCH", None, 0, None), "1.0.0"), + (("1.0.0a3.dev0", None, "beta", 0, None), "1.0.0-b0"), + (("1.0.0", "PATCH", None, 0, None), "1.0.1"), + (("1.0.1", "PATCH", None, 0, None), "1.0.2"), + (("1.0.2", "MINOR", None, 0, None), "1.1.0"), + (("1.1.0", "MINOR", None, 0, None), "1.2.0"), + (("1.2.0", "PATCH", None, 0, None), "1.2.1"), + (("1.2.1", "MAJOR", None, 0, None), "2.0.0"), +] + +# this cases should be handled gracefully +unexpected_cases = [ + (("0.1.1rc0", None, "alpha", 0, None), "0.1.1-a0"), + (("0.1.1b1", None, "alpha", 0, None), "0.1.1-a0"), +] + +weird_cases = [ + (("1.1", "PATCH", None, 0, None), "1.1.1"), + (("1", "MINOR", None, 0, None), "1.1.0"), + (("1", "MAJOR", None, 0, None), "2.0.0"), + (("1a0", None, "alpha", 0, None), "1.0.0-a1"), + (("1a0", None, "alpha", 1, None), "1.0.0-a1"), + (("1", None, "beta", 0, None), "1.0.0-b0"), + (("1", None, "beta", 1, None), "1.0.0-b1"), + (("1beta", None, "beta", 0, None), "1.0.0-b1"), + (("1.0.0alpha1", None, "alpha", 0, None), "1.0.0-a2"), + (("1", None, "rc", 0, None), "1.0.0-rc0"), + (("1.0.0rc1+e20d7b57f3eb", "PATCH", None, 0, None), "1.0.0"), +] + +# test driven development +tdd_cases = [ + (("0.1.1", "PATCH", None, 0, None), "0.1.2"), + (("0.1.1", "MINOR", None, 0, None), "0.2.0"), + (("2.1.1", "MAJOR", None, 0, None), "3.0.0"), + (("0.9.0", "PATCH", "alpha", 0, None), "0.9.1-a0"), + (("0.9.0", "MINOR", "alpha", 0, None), "0.10.0-a0"), + (("0.9.0", "MAJOR", "alpha", 0, None), "1.0.0-a0"), + (("0.9.0", "MAJOR", "alpha", 1, None), "1.0.0-a1"), + (("1.0.0a2", None, "beta", 0, None), "1.0.0-b0"), + (("1.0.0a2", None, "beta", 1, None), "1.0.0-b1"), + (("1.0.0beta1", None, "rc", 0, None), "1.0.0-rc0"), + (("1.0.0rc1", None, "rc", 0, None), "1.0.0-rc2"), + (("1.0.0-a0", None, "rc", 0, None), "1.0.0-rc0"), + (("1.0.0-alpha1", None, "alpha", 0, None), "1.0.0-a2"), +] + + +@pytest.mark.parametrize( + "test_input, expected", + itertools.chain(tdd_cases, weird_cases, simple_flow, unexpected_cases), +) +def test_generate_version_type(test_input, expected): + current_version = test_input[0] + increment = test_input[1] + prerelease = test_input[2] + prerelease_offset = test_input[3] + devrelease = test_input[4] + assert ( + str( + generate_version( + current_version, + increment=increment, + prerelease=prerelease, + prerelease_offset=prerelease_offset, + devrelease=devrelease, + version_type_cls=SemVerVersion, + ) + ) + == expected + ) diff --git a/tests/test_bump_update_version_in_files.py b/tests/test_bump_update_version_in_files.py index 0c37bf538c..b1b9c0b1d5 100644 --- a/tests/test_bump_update_version_in_files.py +++ b/tests/test_bump_update_version_in_files.py @@ -1,7 +1,7 @@ +from pathlib import Path from shutil import copyfile import pytest -from py._path.local import LocalPath from commitizen import bump from commitizen.exceptions import CurrentVersionNotFoundError @@ -13,9 +13,9 @@ def _copy_sample_file_to_tmpdir( - tmpdir: LocalPath, source_filename: str, dest_filename: str -) -> str: - tmp_file = str(tmpdir.join(dest_filename)).replace("\\", "/") + tmp_path: Path, source_filename: str, dest_filename: str +) -> Path: + tmp_file = tmp_path / dest_filename copyfile(f"{TESTING_FILE_PREFIX}/{source_filename}", tmp_file) return tmp_file diff --git a/tests/test_changelog.py b/tests/test_changelog.py index e68a3abdcf..0aa9fdfbff 100644 --- a/tests/test_changelog.py +++ b/tests/test_changelog.py @@ -513,6 +513,17 @@ def test_get_commit_tag_is_None(gitcommits, tags): assert current_key is None +@pytest.mark.parametrize("test_input", TAGS) +def test_valid_tag_included_in_changelog(test_input): + tag = git.GitTag(*test_input) + assert changelog.tag_included_in_changelog(tag, [], False) + + +def test_invalid_tag_included_in_changelog(): + tag = git.GitTag("not_a_version", "rev", "date") + assert not changelog.tag_included_in_changelog(tag, [], False) + + COMMITS_TREE = ( { "version": "v1.2.0", @@ -789,15 +800,287 @@ def test_get_commit_tag_is_None(gitcommits, tags): }, ) +COMMITS_TREE_AFTER_MERGED_PRERELEASES = ( + { + "version": "v1.2.0", + "date": "2019-04-19", + "changes": { + "feat": [ + { + "scope": None, + "breaking": None, + "message": "custom cz plugins now support bumping version", + } + ] + }, + }, + { + "version": "v1.1.1", + "date": "2019-04-18", + "changes": { + "refactor": [ + { + "scope": None, + "breaking": None, + "message": "changed stdout statements", + }, + { + "scope": "schema", + "breaking": None, + "message": "command logic removed from commitizen base", + }, + { + "scope": "info", + "breaking": None, + "message": "command logic removed from commitizen base", + }, + { + "scope": "example", + "breaking": None, + "message": "command logic removed from commitizen base", + }, + { + "scope": "commit", + "breaking": None, + "message": "moved most of the commit logic to the commit command", + }, + ], + "fix": [ + { + "scope": "bump", + "breaking": None, + "message": "commit message now fits better with semver", + }, + { + "scope": None, + "breaking": None, + "message": "conventional commit 'breaking change' in body instead of title", + }, + ], + }, + }, + { + "version": "v1.1.0", + "date": "2019-04-14", + "changes": { + "feat": [ + { + "scope": None, + "breaking": None, + "message": "new working bump command", + }, + {"scope": None, "breaking": None, "message": "create version tag"}, + { + "scope": None, + "breaking": None, + "message": "update given files with new version", + }, + { + "scope": "config", + "breaking": None, + "message": "new set key, used to set version to cfg", + }, + { + "scope": None, + "breaking": None, + "message": "support for pyproject.toml", + }, + { + "scope": None, + "breaking": None, + "message": "first semantic version bump implementation", + }, + ], + "fix": [ + { + "scope": None, + "breaking": None, + "message": "removed all from commit", + }, + { + "scope": None, + "breaking": None, + "message": "fix config file not working", + }, + ], + "refactor": [ + { + "scope": None, + "breaking": None, + "message": "added commands folder, better integration with decli", + } + ], + }, + }, + { + "version": "v1.0.0", + "date": "2019-03-01", + "changes": { + "refactor": [ + { + "scope": None, + "breaking": None, + "message": "removed delegator, added decli and many tests", + } + ], + "feat": [ + { + "scope": None, + "breaking": None, + "message": "py3 only, tests and conventional commits 1.0", + } + ], + "BREAKING CHANGE": [ + {"scope": None, "breaking": None, "message": "API is stable"} + ], + }, + }, + { + "version": "v0.9.11", + "date": "2018-12-17", + "changes": { + "fix": [ + { + "scope": "config", + "breaking": None, + "message": "load config reads in order without failing if there is no commitizen section", + } + ] + }, + }, + { + "version": "v0.9.10", + "date": "2018-09-22", + "changes": { + "fix": [ + { + "scope": None, + "breaking": None, + "message": "parse scope (this is my punishment for not having tests)", + } + ] + }, + }, + { + "version": "v0.9.9", + "date": "2018-09-22", + "changes": { + "fix": [{"scope": None, "breaking": None, "message": "parse scope empty"}] + }, + }, + { + "version": "v0.9.8", + "date": "2018-09-22", + "changes": { + "fix": [ + { + "scope": "scope", + "breaking": None, + "message": "parse correctly again", + } + ] + }, + }, + { + "version": "v0.9.7", + "date": "2018-09-22", + "changes": { + "fix": [{"scope": "scope", "breaking": None, "message": "parse correctly"}] + }, + }, + { + "version": "v0.9.6", + "date": "2018-09-19", + "changes": { + "refactor": [ + { + "scope": "conventionalCommit", + "breaking": None, + "message": "moved filters to questions instead of message", + } + ], + "fix": [ + { + "scope": "manifest", + "breaking": None, + "message": "included missing files", + } + ], + }, + }, + { + "version": "v0.9.5", + "date": "2018-08-24", + "changes": { + "fix": [ + { + "scope": "config", + "breaking": None, + "message": "home path for python versions between 3.0 and 3.5", + } + ] + }, + }, + { + "version": "v0.9.4", + "date": "2018-08-02", + "changes": { + "feat": [{"scope": "cli", "breaking": None, "message": "added version"}] + }, + }, + { + "version": "v0.9.3", + "date": "2018-07-28", + "changes": { + "feat": [ + { + "scope": "committer", + "breaking": None, + "message": "conventional commit is a bit more intelligent now", + } + ] + }, + }, + { + "version": "v0.9.2", + "date": "2017-11-11", + "changes": { + "refactor": [ + { + "scope": None, + "breaking": None, + "message": "renamed conventional_changelog to conventional_commits, not backward compatible", + } + ] + }, + }, + { + "version": "v0.9.1", + "date": "2017-11-11", + "changes": { + "fix": [ + { + "scope": "setup.py", + "breaking": None, + "message": "future is now required for every python version", + } + ] + }, + }, +) + -def test_generate_tree_from_commits(gitcommits, tags): +@pytest.mark.parametrize("merge_prereleases", (True, False)) +def test_generate_tree_from_commits(gitcommits, tags, merge_prereleases): parser = defaults.commit_parser changelog_pattern = defaults.bump_pattern tree = changelog.generate_tree_from_commits( - gitcommits, tags, parser, changelog_pattern + gitcommits, tags, parser, changelog_pattern, merge_prerelease=merge_prereleases ) - - assert tuple(tree) == COMMITS_TREE + if merge_prereleases: + assert tuple(tree) == COMMITS_TREE_AFTER_MERGED_PRERELEASES + else: + assert tuple(tree) == COMMITS_TREE @pytest.mark.parametrize( diff --git a/tests/test_conf.py b/tests/test_conf.py index d39de8a048..4226096371 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -44,6 +44,7 @@ _settings = { "name": "cz_jira", "version": "1.0.0", + "version_provider": "commitizen", "tag_format": None, "bump_message": None, "allow_abort": False, @@ -52,17 +53,20 @@ "changelog_file": "CHANGELOG.md", "changelog_incremental": False, "changelog_start_rev": None, + "changelog_merge_prerelease": False, "update_changelog_on_bump": False, "use_shortcuts": False, "major_version_zero": False, "pre_bump_hooks": ["scripts/generate_documentation.sh"], "post_bump_hooks": ["scripts/slack_notification.sh"], "prerelease_offset": 0, + "version_type": None, } _new_settings = { "name": "cz_jira", "version": "2.0.0", + "version_provider": "commitizen", "tag_format": None, "bump_message": None, "allow_abort": False, @@ -71,12 +75,14 @@ "changelog_file": "CHANGELOG.md", "changelog_incremental": False, "changelog_start_rev": None, + "changelog_merge_prerelease": False, "update_changelog_on_bump": False, "use_shortcuts": False, "major_version_zero": False, "pre_bump_hooks": ["scripts/generate_documentation.sh"], "post_bump_hooks": ["scripts/slack_notification.sh"], "prerelease_offset": 0, + "version_type": None, } _read_settings = { diff --git a/tests/test_factory.py b/tests/test_factory.py index a8e56f51d6..0ac8c0b275 100644 --- a/tests/test_factory.py +++ b/tests/test_factory.py @@ -1,13 +1,26 @@ import sys +from textwrap import dedent +import importlib_metadata as metadata import pytest from commitizen import BaseCommitizen, defaults, factory from commitizen.config import BaseConfig from commitizen.cz import discover_plugins +from commitizen.cz.conventional_commits import ConventionalCommitsCz +from commitizen.cz.customize import CustomizeCommitsCz +from commitizen.cz.jira import JiraSmartCz from commitizen.exceptions import NoCommitizenFoundException +class Plugin: + pass + + +class OtherPlugin: + pass + + def test_factory(): config = BaseConfig() config.settings.update({"name": defaults.DEFAULT_SETTINGS["name"]}) @@ -24,17 +37,19 @@ def test_factory_fails(): assert "The committer has not been found in the system." in str(excinfo) -@pytest.mark.parametrize( - "module_content, plugin_name, expected_plugins", - [ - ("", "cz_no_plugin", {}), - ], -) -def test_discover_plugins(module_content, plugin_name, expected_plugins, tmp_path): - no_plugin_folder = tmp_path / plugin_name - no_plugin_folder.mkdir() - init_file = no_plugin_folder / "__init__.py" - init_file.write_text(module_content) +def test_discover_plugins(tmp_path): + legacy_plugin_folder = tmp_path / "cz_legacy" + legacy_plugin_folder.mkdir() + init_file = legacy_plugin_folder / "__init__.py" + init_file.write_text( + dedent( + """\ + class Plugin: pass + + discover_this = Plugin + """ + ) + ) sys.path.append(tmp_path.as_posix()) with pytest.warns(UserWarning) as record: @@ -43,6 +58,36 @@ def test_discover_plugins(module_content, plugin_name, expected_plugins, tmp_pat assert ( record[0].message.args[0] - == f"module '{plugin_name}' has no attribute 'discover_this'" + == "Legacy plugin 'cz_legacy' has been ignored: please expose it the 'commitizen.plugin' entrypoint" + ) + assert "cz_legacy" not in discovered_plugins + + +def test_discover_external_plugin(mocker): + ep_plugin = metadata.EntryPoint( + "test", "tests.test_factory:Plugin", "commitizen.plugin" ) - assert expected_plugins == discovered_plugins + ep_other_plugin = metadata.EntryPoint( + "not-selected", "tests.test_factory::OtherPlugin", "commitizen.not_a_plugin" + ) + eps = [ep_plugin, ep_other_plugin] + + def mock_entrypoints(**kwargs): + group = kwargs.get("group") + return metadata.EntryPoints(ep for ep in eps if ep.group == group) + + mocker.patch.object(metadata, "entry_points", side_effect=mock_entrypoints) + + assert discover_plugins() == {"test": Plugin} + + +def test_discover_internal_plugins(): + expected = { + "cz_conventional_commits": ConventionalCommitsCz, + "cz_jira": JiraSmartCz, + "cz_customize": CustomizeCommitsCz, + } + + discovered = discover_plugins() + + assert set(expected.items()).issubset(set(discovered.items())) diff --git a/tests/test_version_providers.py b/tests/test_version_providers.py new file mode 100644 index 0000000000..1c48fc3603 --- /dev/null +++ b/tests/test_version_providers.py @@ -0,0 +1,213 @@ +from __future__ import annotations + +import os +from pathlib import Path +from textwrap import dedent +from typing import TYPE_CHECKING, Iterator, Optional, Type + +import pytest + +from commitizen.config.base_config import BaseConfig +from commitizen.exceptions import VersionProviderUnknown +from commitizen.providers import ( + CargoProvider, + CommitizenProvider, + ComposerProvider, + NpmProvider, + Pep621Provider, + PoetryProvider, + ScmProvider, + VersionProvider, + get_provider, +) +from tests.utils import create_file_and_commit, create_tag + +if TYPE_CHECKING: + from pytest_mock import MockerFixture + + +@pytest.fixture +def chdir(tmp_path: Path) -> Iterator[Path]: + cwd = Path() + os.chdir(tmp_path) + yield tmp_path + os.chdir(cwd) + + +def test_default_version_provider_is_commitizen_config(config: BaseConfig): + provider = get_provider(config) + + assert isinstance(provider, CommitizenProvider) + + +def test_raise_for_unknown_provider(config: BaseConfig): + config.settings["version_provider"] = "unknown" + with pytest.raises(VersionProviderUnknown): + get_provider(config) + + +def test_commitizen_provider(config: BaseConfig, mocker: MockerFixture): + config.settings["version"] = "42" + mock = mocker.patch.object(config, "set_key") + + provider = CommitizenProvider(config) + assert provider.get_version() == "42" + + provider.set_version("43.1") + mock.assert_called_once_with("version", "43.1") + + +FILE_PROVIDERS = dict( + pep621=( + "pyproject.toml", + Pep621Provider, + """\ + [project] + version = "0.1.0" + """, + """\ + [project] + version = "42.1" + """, + ), + poetry=( + "pyproject.toml", + PoetryProvider, + """\ + [tool.poetry] + version = "0.1.0" + """, + """\ + [tool.poetry] + version = "42.1" + """, + ), + cargo=( + "Cargo.toml", + CargoProvider, + """\ + [package] + version = "0.1.0" + """, + """\ + [package] + version = "42.1" + """, + ), + npm=( + "package.json", + NpmProvider, + """\ + { + "name": "whatever", + "version": "0.1.0" + } + """, + """\ + { + "name": "whatever", + "version": "42.1" + } + """, + ), + composer=( + "composer.json", + ComposerProvider, + """\ + { + "name": "whatever", + "version": "0.1.0" + } + """, + """\ + { + "name": "whatever", + "version": "42.1" + } + """, + ), +) + + +@pytest.mark.parametrize( + "id,filename,cls,content,expected", + (pytest.param(id, *FILE_PROVIDERS[id], id=id) for id in FILE_PROVIDERS), +) +def test_file_providers( + config: BaseConfig, + chdir: Path, + id: str, + filename: str, + cls: Type[VersionProvider], + content: str, + expected: str, +): + file = chdir / filename + file.write_text(dedent(content)) + config.settings["version_provider"] = id + + provider = get_provider(config) + assert isinstance(provider, cls) + assert provider.get_version() == "0.1.0" + + provider.set_version("42.1") + assert file.read_text() == dedent(expected) + + +@pytest.mark.parametrize( + "tag_format,tag,version", + ( + (None, "0.1.0", "0.1.0"), + (None, "v0.1.0", "0.1.0"), + ("v$version", "v0.1.0", "0.1.0"), + ("version-$version", "version-0.1.0", "0.1.0"), + ("version-$version", "version-0.1", "0.1"), + ("version-$version", "version-0.1.0rc1", "0.1.0rc1"), + ("v$minor.$major.$patch", "v1.0.0", "0.1.0"), + ("version-$major.$minor.$patch", "version-0.1.0", "0.1.0"), + ("v$major.$minor$prerelease$devrelease", "v1.0rc1", "1.0rc1"), + ("v$major.$minor.$patch$prerelease$devrelease", "v0.1.0", "0.1.0"), + ("v$major.$minor.$patch$prerelease$devrelease", "v0.1.0rc1", "0.1.0rc1"), + ("v$major.$minor.$patch$prerelease$devrelease", "v1.0.0.dev0", "1.0.0.dev0"), + ), +) +@pytest.mark.usefixtures("tmp_git_project") +def test_scm_provider( + config: BaseConfig, tag_format: Optional[str], tag: str, version: str +): + create_file_and_commit("test: fake commit") + create_tag(tag) + create_file_and_commit("test: fake commit") + create_tag("should-not-match") + + config.settings["version_provider"] = "scm" + config.settings["tag_format"] = tag_format + + provider = get_provider(config) + assert isinstance(provider, ScmProvider) + assert provider.get_version() == version + + # Should not fail on set_version() + provider.set_version("43.1") + + +@pytest.mark.usefixtures("tmp_git_project") +def test_scm_provider_default_without_matching_tag(config: BaseConfig): + create_file_and_commit("test: fake commit") + create_tag("should-not-match") + create_file_and_commit("test: fake commit") + + config.settings["version_provider"] = "scm" + + provider = get_provider(config) + assert isinstance(provider, ScmProvider) + assert provider.get_version() == "0.0.0" + + +@pytest.mark.usefixtures("tmp_git_project") +def test_scm_provider_default_without_commits_and_tags(config: BaseConfig): + config.settings["version_provider"] = "scm" + + provider = get_provider(config) + assert isinstance(provider, ScmProvider) + assert provider.get_version() == "0.0.0" diff --git a/tests/utils.py b/tests/utils.py index 5dcf6722e9..2efe13fa9e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -17,7 +17,7 @@ def create_file_and_commit(message: str, filename: Optional[str] = None): if not filename: filename = str(uuid.uuid4()) - Path(f"./{filename}").touch() + Path(filename).touch() c = cmd.run("git add .") if c.return_code != 0: raise exceptions.CommitError(c.err) @@ -26,6 +26,31 @@ def create_file_and_commit(message: str, filename: Optional[str] = None): raise exceptions.CommitError(c.err) +def create_branch(name: str): + c = cmd.run(f"git branch {name}") + if c.return_code != 0: + raise exceptions.GitCommandError(c.err) + + +def switch_branch(branch: str): + c = cmd.run(f"git switch {branch}") + if c.return_code != 0: + raise exceptions.GitCommandError(c.err) + + +def merge_branch(branch: str): + c = cmd.run(f"git merge {branch}") + if c.return_code != 0: + raise exceptions.GitCommandError(c.err) + + +def get_current_branch() -> str: + c = cmd.run("git rev-parse --abbrev-ref HEAD") + if c.return_code != 0: + raise exceptions.GitCommandError(c.err) + return c.out + + def create_tag(tag: str): c = git.tag(tag) if c.return_code != 0: