diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b04030..23efa49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -65,5 +65,12 @@ jobs: - name: Install dev dependencies run: python -m pip install --group dev - - name: Validate pre-commit hook - run: pre-commit try-repo . docstringify + - name: Check that the docstringify-edit pre-commit hook is available + run: pre-commit try-repo . docstringify-edit + + - name: Check that the docstringify-suggest pre-commit hook is available + run: pre-commit try-repo . docstringify-suggest + + # this is the only hook that works without an additional arg so we can test it like this + - name: Validate the docstringify-check pre-commit hook + run: pre-commit try-repo . docstringify-check --all-files diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d29deda..b65ac43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-toml - id: check-yaml @@ -26,20 +26,26 @@ repos: exclude: (tests|docs)/.* - repo: https://github.com/stefmolin/docstringify - rev: 1.0.0 + rev: 1decb1a66b512ab792fdc0d190a7ba761b3d70fd hooks: - - id: docstringify - args: [--make-changes-inplace=numpydoc] + - id: docstringify-edit + name: docstringify source code with numpydoc style + args: [--overwrite, --style=numpydoc] + files: src/.* + - id: docstringify-edit + name: docstringify tests with stubs + args: [--overwrite, --style=stub] + files: tests/.* - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.2 + rev: v0.14.2 hooks: - id: ruff-check args: [--fix, --exit-non-zero-on-fix, --show-fixes] - id: ruff-format - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.6.0 + rev: v2.7.0 hooks: - id: pyproject-fmt args: [--keep-full-version, --no-print-diff] diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 5a4b331..2935804 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,7 +1,35 @@ +- id: docstringify-edit + name: generate missing docstrings with docstringify + description: >- + Generate missing docstrings from signatures and type annotations, + injecting them into the source code. + entry: docstringify edit + language: python + types: [python] + +- id: docstringify-suggest + name: suggest docstrings with docstringify + description: >- + Generate suggestions for missing docstrings from signatures and + type annotations, printing them for inspection. + entry: docstringify suggest + language: python + types: [python] + +- id: docstringify-check + name: check for missing docstrings with docstringify + description: >- + Flag missing docstrings, failing if more than the threshold are missing. + entry: docstringify check + language: python + require_serial: true + types: [python] + - id: docstringify - name: docstringify - description: | - Flag missing docstrings and, optionally, generate them from signatures and type annotations. + name: docstringify (deprecated) + description: >- + Flag missing docstrings and, optionally, generate them from signatures + and type annotations. entry: docstringify language: python require_serial: true diff --git a/pyproject.toml b/pyproject.toml index 710687f..12905e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,13 +20,11 @@ license-files = [ "LICENSE" ] authors = [ { name = "Stefanie Molin", email = "docstringify@stefaniemolin.com" }, ] -requires-python = ">=3.9" +requires-python = ">=3.11" classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -85,6 +83,7 @@ lint.isort.known-first-party = [ ] [tool.codespell] +ignore-words-list = "docstringify" ignore-regex = 'https://([\w/\.])+' [tool.numpydoc_validation] diff --git a/src/docstringify/cli.py b/src/docstringify/cli.py index 0e98fc1..1fa101a 100644 --- a/src/docstringify/cli.py +++ b/src/docstringify/cli.py @@ -4,16 +4,13 @@ import argparse import sys +from enum import StrEnum, auto from functools import partial from typing import TYPE_CHECKING from . import __doc__ as pkg_description from . import __version__ -from .converters import ( - GoogleDocstringConverter, - NumpydocDocstringConverter, - StubDocstringConverter, -) +from .converters import CONVERTER_LOOKUP from .traversal import DocstringTransformer, DocstringVisitor if TYPE_CHECKING: @@ -21,108 +18,220 @@ PROG = __package__ -STYLES: dict[ - str, - type[GoogleDocstringConverter] - | type[NumpydocDocstringConverter] - | type[StubDocstringConverter], -] = { - 'google': GoogleDocstringConverter, - 'numpydoc': NumpydocDocstringConverter, - 'stub': StubDocstringConverter, -} +"""CLI name.""" + CLI_DEFAULTS = {'threshold': 1.0} +"""CLI default values.""" -def main(argv: Sequence[str] | None = None) -> int: +class DocstringifyRunModes(StrEnum): + """Run modes for Docstringify's CLI.""" + + CHECK = auto() + """Docstringify mode that checks for missing docstrings only.""" + + EDIT = auto() + """Docstringify mode that injects templates for missing docstrings into the + source code.""" + + SUGGEST = auto() + """Docstringify mode that suggests templates for missing docstrings without + editing the source code.""" + + +def _process_files( + mode: DocstringifyRunModes, args: argparse.Namespace +) -> tuple[int, int]: """ - Flag missing docstrings and, optionally, generate them from signatures and - type annotations. + Process the filenames passed in as command line arguments. Parameters ---------- - argv : Sequence[str] | None, default=None - The arguments passed on the command line. + mode : DocstringifyRunModes + The mode in which to run Docstringify. + args : argparse.Namespace + The command line arguments. Returns ------- - int - Exit code for the process, where non-zero values indicate errors, and ``1`` - indicates that more than the allowed percentage of docstrings were missing. + tuple[int, int] + A tuple containing the number of missing docstrings and the total number of + docstrings expected. """ - - parser = argparse.ArgumentParser(prog=PROG, description=pkg_description) - parser.add_argument('filenames', nargs='*', help='Filenames to process') - parser.add_argument( - '-v', '--verbose', action='store_true', help='Run in verbose mode' - ) - parser.add_argument( - '--version', action='version', version=f'%(prog)s {__version__}' - ) - - run_group = parser.add_argument_group('Run options') - handle_missing_docstring = run_group.add_mutually_exclusive_group(required=False) - handle_missing_docstring.add_argument( - '--make-changes', - choices=STYLES.keys(), - help='Whether to insert docstring templates for items missing docstrings', - ) - handle_missing_docstring.add_argument( - '--make-changes-inplace', - choices=STYLES.keys(), - help=( - 'Whether to insert docstring templates for items missing docstrings, ' - 'overwriting the original file' - ), - ) - handle_missing_docstring.add_argument( - '--suggest-changes', - choices=STYLES.keys(), - help='Whether to print out docstring templates for items missing docstrings', - ) - run_group.add_argument( - '--threshold', - type=float, - default=CLI_DEFAULTS['threshold'], - help='The percentage of docstrings that must be present to pass', - ) - args = parser.parse_args(argv) - get_docstring_processor = ( partial( DocstringTransformer, - converter=STYLES[style], - overwrite=bool(args.make_changes_inplace), + converter=CONVERTER_LOOKUP[args.style], + overwrite=bool(args.overwrite), verbose=args.verbose, ) - if (style := args.make_changes or args.make_changes_inplace) + if mode == DocstringifyRunModes.EDIT else partial( DocstringVisitor, - converter=STYLES.get(args.suggest_changes), + converter=None + if mode == DocstringifyRunModes.CHECK + else CONVERTER_LOOKUP[args.style], verbose=args.verbose, ) ) - docstrings_processed = missing_docstrings = 0 + docstrings_expected = missing_docstrings = 0 for file in args.filenames: processor = get_docstring_processor(file) processor.process_file() missing_docstrings += len(processor.missing_docstrings) - docstrings_processed += processor.docstrings_inspected - - if ( - docstrings_processed - and (missing_percentage := (missing_docstrings / docstrings_processed)) - > 1 - args.threshold - ): - print(f'Missing {missing_percentage:.0%} of docstrings', file=sys.stderr) - print( - f'Your settings require {args.threshold:.0%} of docstrings to be present', - file=sys.stderr, - ) + docstrings_expected += processor.docstrings_inspected + + return missing_docstrings, docstrings_expected + + +def _run(mode: DocstringifyRunModes, args: argparse.Namespace) -> int: + """ + Run Docstringify in the specified mode with the provided command line arguments. + + Parameters + ---------- + mode : DocstringifyRunModes + The run mode. + args : argparse.Namespace + The command line arguments. + + Returns + ------- + int + Exit code for the process, where non-zero values indicate errors. + """ + missing_docstrings, docstrings_expected = _process_files(mode, args) + + if mode == DocstringifyRunModes.CHECK: + if ( + docstrings_expected + and (missing_percentage := (missing_docstrings / docstrings_expected)) + > 1 - args.threshold + ): + print(f'Missing {missing_percentage:.0%} of docstrings', file=sys.stderr) + print( + f'Your settings require {args.threshold:.0%} of docstrings to be present', + file=sys.stderr, + ) + return 1 + return 0 + + # modes suggest and edit behave the same + if docstrings_expected and missing_docstrings: return 1 return 0 +def _populate_subparser( + subparsers: argparse._SubParsersAction[argparse.ArgumentParser], + subcommand: DocstringifyRunModes, + help_msg: str, +) -> argparse.ArgumentParser: + """ + Populate an argument subparser for handling subcommands from the main CLI. + + Parameters + ---------- + subparsers : argparse._SubParsersAction[argparse.ArgumentParser] + The result of calling the :meth:`argparse.ArgumentParser.add_subparsers` method + on the main argument parser. + subcommand : DocstringifyRunModes + The run mode for which to populate the parser. + help_msg : str + The help message to include for the subcommand. + + Returns + ------- + argparse.ArgumentParser + The populated subparser. + """ + parser = subparsers.add_parser(subcommand, help=help_msg) + + parser.add_argument( + 'filenames', nargs='+', metavar='filename', help='filename(s) to process' + ) + + if subcommand == DocstringifyRunModes.CHECK: + parser.add_argument( + '--threshold', + type=float, + default=CLI_DEFAULTS['threshold'], + help='the percentage of docstrings that must be present to pass', + ) + else: + if subcommand == DocstringifyRunModes.EDIT: + parser.add_argument( + '--overwrite', + action='store_true', + help='whether to overwrite the existing file with the changes', + ) + parser.add_argument( + '--style', + choices=CONVERTER_LOOKUP.keys(), + required=True, + help='docstring style to use', + ) + + parser.add_argument('--verbose', action='store_true', help='run in verbose mode') + parser.set_defaults(run=partial(_run, subcommand)) + + return parser + + +def _create_parser() -> argparse.ArgumentParser: + """ + Create an argument parser for the CLI. + + Returns + ------- + argparse.ArgumentParser + The argument parser for the CLI. + """ + parser = argparse.ArgumentParser(prog=PROG, description=pkg_description) + parser.add_argument( + '--version', action='version', version=f'%(prog)s {__version__}' + ) + + subparsers = parser.add_subparsers(title='modes') + for mode, help_msg in [ + ( + DocstringifyRunModes.CHECK, + 'use Docstringify to check whether files have the required docstrings', + ), + ( + DocstringifyRunModes.EDIT, + 'use Docstringify to edit files to inject docstring templates for you', + ), + ( + DocstringifyRunModes.SUGGEST, + 'use Docstringify to review files and suggest templates for missing docstrings', + ), + ]: + _populate_subparser(subparsers, mode, help_msg) + + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + """ + Flag missing docstrings and, optionally, generate them from signatures and + type annotations. + + Parameters + ---------- + argv : Sequence[str] | None, default=None + The arguments passed on the command line. + + Returns + ------- + int + Exit code for the process, where non-zero values indicate errors. + """ + parser = _create_parser() + args = parser.parse_args(argv) + return args.run(args) + + if __name__ == '__main__': raise SystemExit(main()) diff --git a/src/docstringify/converters/__init__.py b/src/docstringify/converters/__init__.py index 0c92ad6..892b767 100644 --- a/src/docstringify/converters/__init__.py +++ b/src/docstringify/converters/__init__.py @@ -5,7 +5,20 @@ from .numpydoc import NumpydocDocstringConverter from .stub import StubDocstringConverter +CONVERTER_LOOKUP: dict[ + str, + type[GoogleDocstringConverter] + | type[NumpydocDocstringConverter] + | type[StubDocstringConverter], +] = { + 'google': GoogleDocstringConverter, + 'numpydoc': NumpydocDocstringConverter, + 'stub': StubDocstringConverter, +} +"""Mapping of docstring style name to the converter class (:mod:`.converters`).""" + __all__ = [ + 'CONVERTER_LOOKUP', 'DocstringConverter', 'GoogleDocstringConverter', 'NumpydocDocstringConverter', diff --git a/src/docstringify/nodes/base.py b/src/docstringify/nodes/base.py index ab22a92..c08c6ee 100644 --- a/src/docstringify/nodes/base.py +++ b/src/docstringify/nodes/base.py @@ -4,7 +4,10 @@ import ast from functools import partial -from typing import Callable, overload +from typing import TYPE_CHECKING, overload + +if TYPE_CHECKING: + from collections.abc import Callable class DocstringNode: diff --git a/src/docstringify/nodes/function.py b/src/docstringify/nodes/function.py index e860f66..9532c44 100644 --- a/src/docstringify/nodes/function.py +++ b/src/docstringify/nodes/function.py @@ -234,6 +234,7 @@ def _extract_positional_args(self) -> list[Parameter]: zip(self.arguments.args, itertools.repeat(None)), ), positional_defaults, + strict=False, ) ] @@ -255,7 +256,7 @@ def _extract_keyword_args(self) -> list[Parameter]: default=self._extract_default_values(default, True), ) for arg, default in zip( - self.arguments.kwonlyargs, self.arguments.kw_defaults + self.arguments.kwonlyargs, self.arguments.kw_defaults, strict=False ) ] diff --git a/src/docstringify/traversal/visitor.py b/src/docstringify/traversal/visitor.py index 7852a91..6552a56 100644 --- a/src/docstringify/traversal/visitor.py +++ b/src/docstringify/traversal/visitor.py @@ -195,7 +195,7 @@ def visit_docstring( self.stack.pop() return docstring_node.ast_node - def visit_Module(self, node: ast.Module) -> ast.Module: # noqa: N802 + def visit_Module(self, node: ast.Module) -> ast.Module: """ Visit an :class:`ast.Module` node. @@ -211,7 +211,7 @@ def visit_Module(self, node: ast.Module) -> ast.Module: # noqa: N802 """ return self.visit_docstring(node, DocstringNode) - def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef: # noqa: N802 + def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef: """ Visit an :class:`ast.ClassDef` node. @@ -227,7 +227,7 @@ def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef: # noqa: N802 """ return self.visit_docstring(node, DocstringNode) - def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef: # noqa: N802 + def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef: """ Visit an :class:`ast.FunctionDef` node. @@ -243,7 +243,7 @@ def visit_FunctionDef(self, node: ast.FunctionDef) -> ast.FunctionDef: # noqa: """ return self.visit_docstring(node, FunctionDocstringNode) - def visit_AsyncFunctionDef( # noqa: N802 + def visit_AsyncFunctionDef( self, node: ast.AsyncFunctionDef ) -> ast.AsyncFunctionDef: """ @@ -261,7 +261,7 @@ def visit_AsyncFunctionDef( # noqa: N802 """ return self.visit_docstring(node, FunctionDocstringNode) - def visit_Return(self, node: ast.Return) -> ast.Return: # noqa: N802 + def visit_Return(self, node: ast.Return) -> ast.Return: """ Visit an :class:`ast.Return` node. diff --git a/uv.lock b/uv.lock index 3420500..8e28a03 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 2 -requires-python = ">=3.9" +requires-python = ">=3.11" [[package]] name = "cfgv" @@ -26,16 +26,6 @@ version = "7.9.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/0d/5c2114fd776c207bd55068ae8dc1bef63ecd1b767b3389984a8e58f2b926/coverage-7.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:66283a192a14a3854b2e7f3418d7db05cdf411012ab7ff5db98ff3b181e1f912", size = 212039, upload-time = "2025-07-03T10:52:38.955Z" }, - { url = "https://files.pythonhosted.org/packages/cf/ad/dc51f40492dc2d5fcd31bb44577bc0cc8920757d6bc5d3e4293146524ef9/coverage-7.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4e01d138540ef34fcf35c1aa24d06c3de2a4cffa349e29a10056544f35cca15f", size = 212428, upload-time = "2025-07-03T10:52:41.36Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a3/55cb3ff1b36f00df04439c3993d8529193cdf165a2467bf1402539070f16/coverage-7.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f22627c1fe2745ee98d3ab87679ca73a97e75ca75eb5faee48660d060875465f", size = 241534, upload-time = "2025-07-03T10:52:42.956Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c9/a8410b91b6be4f6e9c2e9f0dce93749b6b40b751d7065b4410bf89cb654b/coverage-7.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b1c2d8363247b46bd51f393f86c94096e64a1cf6906803fa8d5a9d03784bdbf", size = 239408, upload-time = "2025-07-03T10:52:44.199Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c4/6f3e56d467c612b9070ae71d5d3b114c0b899b5788e1ca3c93068ccb7018/coverage-7.9.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c10c882b114faf82dbd33e876d0cbd5e1d1ebc0d2a74ceef642c6152f3f4d547", size = 240552, upload-time = "2025-07-03T10:52:45.477Z" }, - { url = "https://files.pythonhosted.org/packages/fd/20/04eda789d15af1ce79bce5cc5fd64057c3a0ac08fd0576377a3096c24663/coverage-7.9.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:de3c0378bdf7066c3988d66cd5232d161e933b87103b014ab1b0b4676098fa45", size = 240464, upload-time = "2025-07-03T10:52:46.809Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5a/217b32c94cc1a0b90f253514815332d08ec0812194a1ce9cca97dda1cd20/coverage-7.9.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1e2f097eae0e5991e7623958a24ced3282676c93c013dde41399ff63e230fcf2", size = 239134, upload-time = "2025-07-03T10:52:48.149Z" }, - { url = "https://files.pythonhosted.org/packages/34/73/1d019c48f413465eb5d3b6898b6279e87141c80049f7dbf73fd020138549/coverage-7.9.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28dc1f67e83a14e7079b6cea4d314bc8b24d1aed42d3582ff89c0295f09b181e", size = 239405, upload-time = "2025-07-03T10:52:49.687Z" }, - { url = "https://files.pythonhosted.org/packages/49/6c/a2beca7aa2595dad0c0d3f350382c381c92400efe5261e2631f734a0e3fe/coverage-7.9.2-cp310-cp310-win32.whl", hash = "sha256:bf7d773da6af9e10dbddacbf4e5cab13d06d0ed93561d44dae0188a42c65be7e", size = 214519, upload-time = "2025-07-03T10:52:51.036Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c8/91e5e4a21f9a51e2c7cdd86e587ae01a4fcff06fc3fa8cde4d6f7cf68df4/coverage-7.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:0c0378ba787681ab1897f7c89b415bd56b0b2d9a47e5a3d8dc0ea55aac118d6c", size = 215400, upload-time = "2025-07-03T10:52:52.313Z" }, { url = "https://files.pythonhosted.org/packages/39/40/916786453bcfafa4c788abee4ccd6f592b5b5eca0cd61a32a4e5a7ef6e02/coverage-7.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a7a56a2964a9687b6aba5b5ced6971af308ef6f79a91043c05dd4ee3ebc3e9ba", size = 212152, upload-time = "2025-07-03T10:52:53.562Z" }, { url = "https://files.pythonhosted.org/packages/9f/66/cc13bae303284b546a030762957322bbbff1ee6b6cb8dc70a40f8a78512f/coverage-7.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123d589f32c11d9be7fe2e66d823a236fe759b0096f5db3fb1b75b2fa414a4fa", size = 212540, upload-time = "2025-07-03T10:52:55.196Z" }, { url = "https://files.pythonhosted.org/packages/0f/3c/d56a764b2e5a3d43257c36af4a62c379df44636817bb5f89265de4bf8bd7/coverage-7.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:333b2e0ca576a7dbd66e85ab402e35c03b0b22f525eed82681c4b866e2e2653a", size = 245097, upload-time = "2025-07-03T10:52:56.509Z" }, @@ -80,16 +70,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" }, { url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" }, { url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" }, - { url = "https://files.pythonhosted.org/packages/62/ab/b4b06662ccaa00ca7bbee967b7035a33a58b41efb92d8c89a6c523a2ccd5/coverage-7.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ddc39510ac922a5c4c27849b739f875d3e1d9e590d1e7b64c98dadf037a16cce", size = 212037, upload-time = "2025-07-03T10:53:58.055Z" }, - { url = "https://files.pythonhosted.org/packages/bb/5e/04619995657acc898d15bfad42b510344b3a74d4d5bc34f2e279d46c781c/coverage-7.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a535c0c7364acd55229749c2b3e5eebf141865de3a8f697076a3291985f02d30", size = 212412, upload-time = "2025-07-03T10:53:59.451Z" }, - { url = "https://files.pythonhosted.org/packages/14/e7/1465710224dc6d31c534e7714cbd907210622a044adc81c810e72eea873f/coverage-7.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df0f9ef28e0f20c767ccdccfc5ae5f83a6f4a2fbdfbcbcc8487a8a78771168c8", size = 241164, upload-time = "2025-07-03T10:54:00.852Z" }, - { url = "https://files.pythonhosted.org/packages/ab/f2/44c6fbd2794afeb9ab6c0a14d3c088ab1dae3dff3df2624609981237bbb4/coverage-7.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f3da12e0ccbcb348969221d29441ac714bbddc4d74e13923d3d5a7a0bebef7a", size = 239032, upload-time = "2025-07-03T10:54:02.25Z" }, - { url = "https://files.pythonhosted.org/packages/6a/d2/7a79845429c0aa2e6788bc45c26a2e3052fa91082c9ea1dea56fb531952c/coverage-7.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a17eaf46f56ae0f870f14a3cbc2e4632fe3771eab7f687eda1ee59b73d09fe4", size = 240148, upload-time = "2025-07-03T10:54:03.618Z" }, - { url = "https://files.pythonhosted.org/packages/9c/7d/2731d1b4c9c672d82d30d218224dfc62939cf3800bc8aba0258fefb191f5/coverage-7.9.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:669135a9d25df55d1ed56a11bf555f37c922cf08d80799d4f65d77d7d6123fcf", size = 239875, upload-time = "2025-07-03T10:54:05.022Z" }, - { url = "https://files.pythonhosted.org/packages/1b/83/685958715429a9da09cf172c15750ca5c795dd7259466f2645403696557b/coverage-7.9.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9d3a700304d01a627df9db4322dc082a0ce1e8fc74ac238e2af39ced4c083193", size = 238127, upload-time = "2025-07-03T10:54:06.366Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/161a4313308b3783126790adfae1970adbe4886fda8788792e435249910a/coverage-7.9.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:71ae8b53855644a0b1579d4041304ddc9995c7b21c8a1f16753c4d8903b4dfed", size = 239064, upload-time = "2025-07-03T10:54:07.878Z" }, - { url = "https://files.pythonhosted.org/packages/17/14/fe33f41b2e80811021de059621f44c01ebe4d6b08bdb82d54a514488e933/coverage-7.9.2-cp39-cp39-win32.whl", hash = "sha256:dd7a57b33b5cf27acb491e890720af45db05589a80c1ffc798462a765be6d4d7", size = 214522, upload-time = "2025-07-03T10:54:09.331Z" }, - { url = "https://files.pythonhosted.org/packages/6e/30/63d850ec31b5c6f6a7b4e853016375b846258300320eda29376e2786ceeb/coverage-7.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f65bb452e579d5540c8b37ec105dd54d8b9307b07bcaa186818c104ffda22441", size = 215419, upload-time = "2025-07-03T10:54:10.681Z" }, { url = "https://files.pythonhosted.org/packages/d7/85/f8bbefac27d286386961c25515431482a425967e23d3698b75a250872924/coverage-7.9.2-pp39.pp310.pp311-none-any.whl", hash = "sha256:8a1166db2fb62473285bcb092f586e081e92656c7dfa8e9f62b4d39d7e6b5050", size = 204013, upload-time = "2025-07-03T10:54:12.084Z" }, { url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, ] @@ -141,18 +121,6 @@ test = [ { name = "pytest-randomly", specifier = ">=3.12.0" }, ] -[[package]] -name = "exceptiongroup" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, -] - [[package]] name = "filelock" version = "3.18.0" @@ -171,18 +139,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/cd/18f8da995b658420625f7ef13f037be53ae04ec5ad33f9b718240dcfd48c/identify-2.6.12-py2.py3-none-any.whl", hash = "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", size = 99145, upload-time = "2025-05-23T20:37:51.495Z" }, ] -[[package]] -name = "importlib-metadata" -version = "8.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "zipp" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, -] - [[package]] name = "iniconfig" version = "2.1.0" @@ -259,12 +215,10 @@ version = "8.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "iniconfig" }, { name = "packaging" }, { name = "pluggy" }, { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } wheels = [ @@ -290,7 +244,6 @@ name = "pytest-randomly" version = "3.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, { name = "pytest" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c0/68/d221ed7f4a2a49a664da721b8e87b52af6dd317af2a6cb51549cf17ac4b8/pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26", size = 13367, upload-time = "2024-10-25T15:45:34.274Z" } @@ -304,15 +257,6 @@ version = "6.0.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631, upload-time = "2024-08-06T20:33:50.674Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199, upload-time = "2024-08-06T20:31:40.178Z" }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758, upload-time = "2024-08-06T20:31:42.173Z" }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463, upload-time = "2024-08-06T20:31:44.263Z" }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280, upload-time = "2024-08-06T20:31:50.199Z" }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239, upload-time = "2024-08-06T20:31:52.292Z" }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802, upload-time = "2024-08-06T20:31:53.836Z" }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527, upload-time = "2024-08-06T20:31:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052, upload-time = "2024-08-06T20:31:56.914Z" }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774, upload-time = "2024-08-06T20:31:58.304Z" }, { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612, upload-time = "2024-08-06T20:32:03.408Z" }, { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040, upload-time = "2024-08-06T20:32:04.926Z" }, { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829, upload-time = "2024-08-06T20:32:06.459Z" }, @@ -340,15 +284,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597, upload-time = "2024-08-06T20:32:56.985Z" }, { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527, upload-time = "2024-08-06T20:33:03.001Z" }, { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446, upload-time = "2024-08-06T20:33:04.33Z" }, - { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777, upload-time = "2024-08-06T20:33:25.896Z" }, - { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318, upload-time = "2024-08-06T20:33:27.212Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891, upload-time = "2024-08-06T20:33:28.974Z" }, - { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614, upload-time = "2024-08-06T20:33:34.157Z" }, - { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360, upload-time = "2024-08-06T20:33:35.84Z" }, - { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006, upload-time = "2024-08-06T20:33:37.501Z" }, - { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577, upload-time = "2024-08-06T20:33:39.389Z" }, - { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593, upload-time = "2024-08-06T20:33:46.63Z" }, - { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312, upload-time = "2024-08-06T20:33:49.073Z" }, ] [[package]] @@ -390,15 +325,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, ] -[[package]] -name = "typing-extensions" -version = "4.14.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09109dc2e1abd358d5684b1425678151cdaed4731c822/typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36", size = 107673, upload-time = "2025-07-04T13:28:34.16Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload-time = "2025-07-04T13:28:32.743Z" }, -] - [[package]] name = "virtualenv" version = "20.31.2" @@ -412,12 +338,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c wheels = [ { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, ] - -[[package]] -name = "zipp" -version = "3.23.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, -]