From 5734629ce2ef39dba89a8ce4d8600084e7c3b386 Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 00:59:59 +0200 Subject: [PATCH 1/6] Setup project --- .github/workflows/pypi.yml | 27 + .github/workflows/tests.yml | 34 + .gitignore | 4 +- Justfile | 20 + LICENSE | 21 + README.md | 86 ++- def_form/__init__.py | 0 def_form/cli/__init__.py | 0 def_form/cli/cli.py | 78 +++ def_form/cli/main.py | 16 + def_form/exceptions/__init__.py | 0 def_form/exceptions/base.py | 8 + def_form/exceptions/def_formatter.py | 31 + def_form/formatters/__init__.py | 5 + def_form/formatters/def_formatter/__init__.py | 5 + def_form/formatters/def_formatter/base.py | 215 ++++++ def_form/formatters/def_formatter/checker.py | 25 + .../formatters/def_formatter/formatter.py | 137 ++++ def_form/formatters/def_formatter/manager.py | 244 +++++++ def_form/formatters/def_formatter/models.py | 24 + def_form/utils/__init__.py | 0 def_form/utils/find_pyproject.py | 10 + pyproject.toml | 158 +++++ tests/__init__.py | 0 tests/conftest.py | 45 ++ tests/constants.py | 11 + tests/mock_data/__init__.py | 75 +++ tests/mock_data/example.py | 126 ++++ tests/mock_data/expected.py | 213 ++++++ tests/test_checker/__init__.py | 0 tests/test_checker/test_checker.py | 18 + tests/test_formatter/__init__.py | 0 tests/test_formatter/test_formatter.py | 21 + uv.lock | 624 ++++++++++++++++++ 34 files changed, 2278 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/pypi.yml create mode 100644 .github/workflows/tests.yml create mode 100755 Justfile create mode 100644 LICENSE create mode 100644 def_form/__init__.py create mode 100644 def_form/cli/__init__.py create mode 100644 def_form/cli/cli.py create mode 100644 def_form/cli/main.py create mode 100644 def_form/exceptions/__init__.py create mode 100644 def_form/exceptions/base.py create mode 100644 def_form/exceptions/def_formatter.py create mode 100644 def_form/formatters/__init__.py create mode 100644 def_form/formatters/def_formatter/__init__.py create mode 100644 def_form/formatters/def_formatter/base.py create mode 100644 def_form/formatters/def_formatter/checker.py create mode 100644 def_form/formatters/def_formatter/formatter.py create mode 100644 def_form/formatters/def_formatter/manager.py create mode 100644 def_form/formatters/def_formatter/models.py create mode 100644 def_form/utils/__init__.py create mode 100644 def_form/utils/find_pyproject.py create mode 100644 pyproject.toml create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/constants.py create mode 100644 tests/mock_data/__init__.py create mode 100644 tests/mock_data/example.py create mode 100644 tests/mock_data/expected.py create mode 100644 tests/test_checker/__init__.py create mode 100644 tests/test_checker/test_checker.py create mode 100644 tests/test_formatter/__init__.py create mode 100644 tests/test_formatter/test_formatter.py create mode 100644 uv.lock diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..0ad2f97 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,27 @@ +name: publish to PyPI + +on: + push: + tags: + - "v*.*.*" + +permissions: + id-token: write + contents: read + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: astral-sh/setup-uv@v4 + - run: uv python install 3.12 + - run: uv sync --all-extras --dev + + - name: Build package + run: uv build + + - name: Publish to PyPI + run: uv publish diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..9e10fbd --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,34 @@ +name: tests + +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] + +jobs: + linters: + runs-on: ubuntu-latest + strategy: + matrix: + just-trigger: [ format, lint, mypy ] + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: astral-sh/setup-uv@v4 + - run: uv python install 3.12 + - run: uv sync --all-extras --dev + - run: just ${{ matrix.just-trigger }} + + tests: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.10", "3.11", "3.12", "3.13" ] + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: astral-sh/setup-uv@v4 + - run: uv python install ${{ matrix.python-version }} + - run: uv sync --all-extras --dev + - run: just tests diff --git a/.gitignore b/.gitignore index b7faf40..a9031e8 100644 --- a/.gitignore +++ b/.gitignore @@ -85,7 +85,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -173,7 +173,7 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ # Abstra # Abstra is an AI-powered process automation framework. diff --git a/Justfile b/Justfile new file mode 100755 index 0000000..92f54cc --- /dev/null +++ b/Justfile @@ -0,0 +1,20 @@ +SOURCE_PATH := "def_form" +TESTS_PATH := "tests" + +upgrade: + uv lock --upgrade + +format: + uv run ruff format {{ SOURCE_PATH }} + +lint: + uv run ruff check {{ SOURCE_PATH }} + +mypy: + uv run python -m mypy --pretty {{ SOURCE_PATH }} + +fix: + uv run ruff check --fix --unsafe-fixes {{ SOURCE_PATH }} + +tests: + uv run pytest --cov=probirka --cov-report lcov:tests.lcov tests/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c395fde --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 TopNik073 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 48ad210..9bf9995 100644 --- a/README.md +++ b/README.md @@ -1 +1,85 @@ -# def-form \ No newline at end of file +# def-form + +Python function definition formatter + +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![PyPI](https://img.shields.io/pypi/v/def-form.svg)](https://pypi.python.org/pypi/def-form) +[![PyPI](https://img.shields.io/pypi/dm/def-form.svg)](https://pypi.python.org/pypi/def-form) +[![Coverage Status](https://coveralls.io/repos/github/TopNik073/def-form/badge.svg?branch=polish-docs)](https://coveralls.io/github/TopNik073/def-form?branch=polish-docs) + +## Overview + +`def-form` is a code formatting tool that focuses specifically on Python function definitions. It helps maintain consistent formatting of function signatures by automatically organizing arguments vertically when they exceed specified thresholds. + +## Features + +- **Automatic argument formatting**: Converts inline function arguments to vertical format based on configurable rules +- **CI/CD integration**: Provides both `format` and `check` commands for use in development pipelines +- **Configuration file support**: Uses `pyproject.toml` for project-specific settings +- **Customizable thresholds**: Control when arguments should be formatted vertically + +## Installation + +```bash +pip install def-form +``` + +## Usage + +### Format code + +* Format all Python files in a directory: + +```bash +def-form format src/ +``` + +* Format a specific file: + +```bash +def-form format my_module.py +``` + +### Check code without formatting + +* Check if code follows formatting rules: + +```bash +def-form check src/ +``` + +### Command line options + +```text +def-form format [OPTIONS] [PATH] + +Options: + --max-def-length INTEGER Maximum length of function definition + --max-inline-args INTEGER Maximum number of inline arguments + --indent-size INTEGER indent size in spaces (default: 4) + --exclude TEXT Paths or files to exclude from checking/formatting + --show-skipped Show skipped files/directories + --config TEXT Path to pyproject.toml configuration file +``` + +### Configuration + +Create a pyproject.toml file in your project root: + +```toml +[tool.def-form] +max_def_length = 100 +max_inline_args = 2 +indent_size = 4 +exclude = [ + '.venv', + 'migrations' +] +``` + +### Configuration options + +* max_def_length: Maximum allowed characters in a single-line function definition +* max_inline_args: Maximum number of arguments allowed in inline format +* indent_size: Indent for arguments in spaces +* exclude: Files or directories you want to exclude \ No newline at end of file diff --git a/def_form/__init__.py b/def_form/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/def_form/cli/__init__.py b/def_form/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/def_form/cli/cli.py b/def_form/cli/cli.py new file mode 100644 index 0000000..779152d --- /dev/null +++ b/def_form/cli/cli.py @@ -0,0 +1,78 @@ +import sys + +import click + +from def_form.exceptions.base import BaseDefFormException +from def_form.formatters import DefManager + + +@click.command() +@click.argument('path', type=str, default='src') +@click.option('--max-def-length', type=int, default=None, help='max length of your function definition') +@click.option('--max-inline-args', type=int, default=None, help='max number of inline arguments') +@click.option('--indent-size', type=int, default=None, help='indent size in spaces (default: 4)') +@click.option('--config', type=str, default=None, help='path to pyproject.toml') +@click.option('--exclude', multiple=True, help='paths to exclude from formatting') +@click.option('--show-skipped', is_flag=True, help='show skipped files/directories') +def format( + path: str, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None, + config: str | None, + exclude: tuple[str, ...], + show_skipped: bool, +) -> None: + click.echo('Start formatting your code') + try: + DefManager( + path=path, + excluded=exclude, + max_def_length=max_def_length, + max_inline_args=max_inline_args, + indent_size=indent_size, + config=config, + show_skipped=show_skipped, + ).format() + except Exception as e: + click.echo(f'Something went wrong: {e}', err=True) + sys.exit(1) + else: + click.echo('Formatted!') + + +@click.command() +@click.argument('path', type=str, default='src') +@click.option('--max-def-length', type=int, default=None, help='max length of your function definition') +@click.option('--max-inline-args', type=int, default=None, help='max number of inline arguments') +@click.option('--indent-size', type=int, default=None, help='indent size in spaces (default: 4)') +@click.option('--config', type=str, default=None, help='path to pyproject.toml') +@click.option('--exclude', multiple=True, help='paths to exclude from checking') +@click.option('--show-skipped', is_flag=True, help='show skipped files/directories') +def check( + path: str, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None, + config: str | None, + exclude: tuple[str, ...], + show_skipped: bool, +) -> None: + click.echo('Start checking your code') + try: + DefManager( + path=path, + excluded=exclude, + max_def_length=max_def_length, + max_inline_args=max_inline_args, + indent_size=indent_size, + config=config, + show_skipped=show_skipped, + ).check() + except BaseDefFormException: + sys.exit(1) + except Exception as e: + click.echo(f'Something went wrong: {e}', err=True) + sys.exit(1) + else: + click.echo('All checks passed!') diff --git a/def_form/cli/main.py b/def_form/cli/main.py new file mode 100644 index 0000000..07929b8 --- /dev/null +++ b/def_form/cli/main.py @@ -0,0 +1,16 @@ +import click + +from def_form.cli.cli import check +from def_form.cli.cli import format + + +@click.group(name='def-form') +def main(): + click.help_option() + + +main.add_command(format) +main.add_command(check) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/def_form/exceptions/__init__.py b/def_form/exceptions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/def_form/exceptions/base.py b/def_form/exceptions/base.py new file mode 100644 index 0000000..86c4143 --- /dev/null +++ b/def_form/exceptions/base.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass + + +@dataclass +class BaseDefFormException(Exception): + path: str | None = None + message: str | None = None + description: str | None = None diff --git a/def_form/exceptions/def_formatter.py b/def_form/exceptions/def_formatter.py new file mode 100644 index 0000000..7ff4120 --- /dev/null +++ b/def_form/exceptions/def_formatter.py @@ -0,0 +1,31 @@ +from dataclasses import dataclass + +from .base import BaseDefFormException + + +@dataclass +class BaseDefFormatterException(BaseDefFormException): + path: str + message: str + description: str | None = None + + +@dataclass +class DefStringTooLongException(BaseDefFormException): + path: str + message: str = 'String is too long' + description: str | None = None + + +@dataclass +class TooManyInlineArgumentsException(BaseDefFormException): + path: str + message: str = 'Too many inline arguments' + description: str | None = None + + +@dataclass +class InvalidMultilineParamsIndentException(BaseDefFormException): + path: str + message: str = 'Invalid multiline params indentation' + description: str | None = None diff --git a/def_form/formatters/__init__.py b/def_form/formatters/__init__.py new file mode 100644 index 0000000..b349561 --- /dev/null +++ b/def_form/formatters/__init__.py @@ -0,0 +1,5 @@ +from .def_formatter import DefManager + +__all__ = [ + 'DefManager', +] diff --git a/def_form/formatters/def_formatter/__init__.py b/def_form/formatters/def_formatter/__init__.py new file mode 100644 index 0000000..994475e --- /dev/null +++ b/def_form/formatters/def_formatter/__init__.py @@ -0,0 +1,5 @@ +from .manager import DefManager + +__all__ = [ + 'DefManager', +] diff --git a/def_form/formatters/def_formatter/base.py b/def_form/formatters/def_formatter/base.py new file mode 100644 index 0000000..a43892f --- /dev/null +++ b/def_form/formatters/def_formatter/base.py @@ -0,0 +1,215 @@ +import re + +from libcst import FunctionDef +from libcst import MetadataDependent +from libcst import Module +from libcst import Param +from libcst import ParenthesizedWhitespace +from libcst import SimpleWhitespace +from libcst.metadata import PositionProvider + +from def_form.exceptions.base import BaseDefFormException +from def_form.exceptions.def_formatter import DefStringTooLongException +from def_form.exceptions.def_formatter import InvalidMultilineParamsIndentException +from def_form.exceptions.def_formatter import TooManyInlineArgumentsException +from def_form.formatters.def_formatter.models import FunctionAnalysis + + +class DefBase(MetadataDependent): + METADATA_DEPENDENCIES = (PositionProvider,) + + def __init__(self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int = 4): + super().__init__() + self.filepath = filepath + self.max_def_length = max_def_length + self.max_inline_args = max_inline_args + self.indent_size = indent_size + self.issues: list[BaseDefFormException] = [] + + def _check_issues(self, line_length: int, line_no: int, arg_count: int) -> list[BaseDefFormException]: + issues = [] + + if self.max_def_length and line_length > self.max_def_length: + issues.append( + DefStringTooLongException( + path=f'{self.filepath}:{line_no}', + message=f'Function definition too long ({line_length} > {self.max_def_length})', + ) + ) + + if self.max_inline_args and arg_count > self.max_inline_args: + issues.append( + TooManyInlineArgumentsException( + path=f'{self.filepath}:{line_no}', + message=f'Too many inline args ({arg_count} > {self.max_inline_args})', + ) + ) + + return issues + + def is_single_line_function(self, node: FunctionDef) -> bool: + func_code = Module([]).code_for_node(node).strip() + lines = func_code.split('\n') + + for line in lines: + if line.strip().startswith(('def ', 'async def ')): + clean_line = re.sub(r'#.*', '', line) + return ')' in clean_line and ':' in clean_line + + return False + + def has_skip_comment(self, node: FunctionDef) -> bool: + pos = self.get_metadata(PositionProvider, node) + + try: + with open(self.filepath, encoding='utf-8') as f: + lines = f.readlines() + except (OSError, UnicodeDecodeError): + return False + + def_line = lines[pos.start.line - 1] + if '# def-form: skip' in def_line.lower(): + return True + + if pos.start.line > 1: + prev_line = lines[pos.start.line - 2] + if '# def-form: skip' in prev_line.lower(): + return True + + return False + + def _get_params_list(self, node: FunctionDef) -> list[Param]: + params = [] + params.extend(node.params.params) + + if isinstance(node.params.star_arg, Param): + params.append(node.params.star_arg) + + params.extend(node.params.kwonly_params) + + if isinstance(node.params.star_kwarg, Param): + params.append(node.params.star_kwarg) + + return params + + def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: + ws = node.whitespace_before_params + if not isinstance(ws, ParenthesizedWhitespace): + return False + + if not ws.indent: + return False + + if not isinstance(ws.last_line, SimpleWhitespace): + return False + + expected_indent = ' ' * self.indent_size + if ws.last_line.value != expected_indent: + return False + + all_params = self._get_params_list(node) + + if not all_params: + return True + + for param in all_params[:-1]: + comma = param.comma + if comma is None: + return False + + ws_after = comma.whitespace_after + if not isinstance(ws_after, ParenthesizedWhitespace): + return False + + if not ws_after.indent: + return False + + if not isinstance(ws_after.last_line, SimpleWhitespace): + return False + + if ws_after.last_line.value != expected_indent: + return False + + last_param = all_params[-1] + comma = last_param.comma + if comma is None: + return False + + ws_after = comma.whitespace_after + if not isinstance(ws_after, ParenthesizedWhitespace): + return False + + if not ws_after.indent: + return False + + if not isinstance(ws_after.last_line, SimpleWhitespace): + return False + + if ws_after.last_line.value != '': + return False + + return True + + def _count_arguments(self, node: FunctionDef) -> int: + count = len(node.params.params) + + if isinstance(node.params.star_arg, Param): + count += 1 + + count += len(node.params.kwonly_params) + + if isinstance(node.params.star_kwarg, Param): + count += 1 + + return count + + def analyze_function(self, node: FunctionDef) -> FunctionAnalysis: + if self.has_skip_comment(node): + return FunctionAnalysis( + should_process=False, + reason='skip_comment', + node=node, + ) + + func_code = Module([]).code_for_node(node).strip() + first_line = func_code.split('\n')[0] + line_length = len(first_line) + + arg_count = self._count_arguments(node) + + if arg_count == 0: + return FunctionAnalysis( + should_process=False, + reason='no_args', + node=node, + line_length=line_length, + arg_count=arg_count, + ) + + pos = self.get_metadata(PositionProvider, node) + line_no = pos.start.line + + issues: list[BaseDefFormException] = [] + + if self.is_single_line_function(node): + issues = self._check_issues(line_length, line_no, arg_count) + else: + if not self.has_correct_multiline_params_format(node): + issues.append( + InvalidMultilineParamsIndentException( + path=f'{self.filepath}:{line_no}', + message=f'Invalid multiline function parameters indentation (expected {self.indent_size} spaces)', + ) + ) + + has_issues = bool(issues) + + return FunctionAnalysis( + should_process=has_issues, + line_length=line_length, + arg_count=arg_count, + line_no=line_no, + pos=pos, + node=node, + issues=issues, + ) diff --git a/def_form/formatters/def_formatter/checker.py b/def_form/formatters/def_formatter/checker.py new file mode 100644 index 0000000..ded7b97 --- /dev/null +++ b/def_form/formatters/def_formatter/checker.py @@ -0,0 +1,25 @@ +from libcst import CSTVisitor +from libcst import FunctionDef + +from def_form.formatters.def_formatter.base import DefBase + + +class DefChecker(DefBase, CSTVisitor): + def __init__( + self, + filepath: str, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None, + ): + super().__init__( + filepath=filepath, max_def_length=max_def_length, max_inline_args=max_inline_args, indent_size=indent_size + ) + + def leave_FunctionDef(self, original_node: FunctionDef) -> FunctionDef: + analysis = self.analyze_function(original_node) + + if analysis.issues: + self.issues.extend(analysis.issues) + + return original_node diff --git a/def_form/formatters/def_formatter/formatter.py b/def_form/formatters/def_formatter/formatter.py new file mode 100644 index 0000000..462f3ee --- /dev/null +++ b/def_form/formatters/def_formatter/formatter.py @@ -0,0 +1,137 @@ +from libcst import Comma +from libcst import Comment +from libcst import CSTTransformer +from libcst import FunctionDef +from libcst import Param +from libcst import Parameters +from libcst import ParenthesizedWhitespace +from libcst import SimpleWhitespace +from libcst import TrailingWhitespace + +from def_form.formatters.def_formatter.base import DefBase + + +class DefFormatter(DefBase, CSTTransformer): + def __init__(self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int = 4): + super().__init__(filepath, max_def_length, max_inline_args) + self.indent_size = indent_size + + def _is_valid_param(self, param) -> bool: + return isinstance(param, Param) + + def _extract_comment_from_whitespace(self, ws) -> TrailingWhitespace | None: + if isinstance(ws, TrailingWhitespace): + return ws + if isinstance(ws, ParenthesizedWhitespace) and isinstance(ws.first_line, TrailingWhitespace): + return ws.first_line + if isinstance(ws, SimpleWhitespace) and '#' in ws.value: + parts = ws.value.split('#', 1) + spaces = parts[0].rstrip() + comment_text = '#' + parts[1].rstrip() + return TrailingWhitespace( + whitespace=SimpleWhitespace(spaces + ' ' if spaces else ''), comment=Comment(comment_text) + ) + return None + + def _extract_comment_from_param(self, param: Param) -> TrailingWhitespace | None: + if isinstance(param.comma, Comma) and param.comma.whitespace_after: + comment = self._extract_comment_from_whitespace(param.comma.whitespace_after) + if comment: + return comment + if param.whitespace_after_param: + return self._extract_comment_from_whitespace(param.whitespace_after_param) + return None + + def _create_whitespace( + self, comment: TrailingWhitespace | None, last_line: SimpleWhitespace + ) -> ParenthesizedWhitespace: + if comment: + return ParenthesizedWhitespace(first_line=comment, empty_lines=[], indent=True, last_line=last_line) + return ParenthesizedWhitespace(last_line=last_line, indent=True) + + def _create_formatted_param_for_single_line(self, param: Param, is_last: bool = False) -> Param: + indent = ' ' * self.indent_size + comment = self._extract_comment_from_param(param) + last_line = SimpleWhitespace('' if is_last else indent) + new_ws = self._create_whitespace(comment, last_line) + need_comma = param.comma is not None if is_last else True + new_comma = Comma(whitespace_after=new_ws) if need_comma else None + return param.with_changes(comma=new_comma, whitespace_after_param=SimpleWhitespace('')) + + def _create_formatted_param_for_multi_line(self, param: Param, is_last: bool = False) -> Param: + indent = ' ' * self.indent_size + last_line = SimpleWhitespace('' if is_last else indent) + original_ws = param.comma.whitespace_after if isinstance(param.comma, Comma) else None + + if isinstance(original_ws, ParenthesizedWhitespace): + comment = original_ws.first_line if isinstance(original_ws.first_line, TrailingWhitespace) else None + new_ws = self._create_whitespace(comment, last_line) + elif isinstance(original_ws, TrailingWhitespace): + new_ws = self._create_whitespace(original_ws, last_line) + else: + new_ws = self._create_whitespace(None, last_line) + + need_comma = param.comma is not None if is_last else True + new_comma = Comma(whitespace_after=new_ws) if need_comma else None + return param.with_changes(comma=new_comma, whitespace_after_param=SimpleWhitespace('')) + + def _collect_all_params(self, node: FunctionDef) -> list[Param]: + all_params = list(node.params.params) + if self._is_valid_param(node.params.star_arg): + all_params.append(node.params.star_arg) + all_params.extend(node.params.kwonly_params) + if self._is_valid_param(node.params.star_kwarg): + all_params.append(node.params.star_kwarg) + return all_params + + def _restore_param_groups(self, formatted_params: list[Param], node: FunctionDef) -> tuple: + param_count = len(node.params.params) + kwonly_count = len(node.params.kwonly_params) + new_params = formatted_params[:param_count] + remaining = formatted_params[param_count:] + + new_star_arg = None + if self._is_valid_param(node.params.star_arg) and remaining: + new_star_arg = remaining[0] + remaining = remaining[1:] + + new_kwonly_params = remaining[:kwonly_count] if kwonly_count else [] + remaining = remaining[kwonly_count:] + + new_star_kwarg = None + if self._is_valid_param(node.params.star_kwarg) and remaining: + new_star_kwarg = remaining[0] + + return new_params, new_kwonly_params, new_star_arg, new_star_kwarg + + def _process_parameters(self, node: FunctionDef, is_single_line: bool) -> FunctionDef: + all_params = self._collect_all_params(node) + total_params = len(all_params) + format_func = ( + self._create_formatted_param_for_single_line + if is_single_line + else self._create_formatted_param_for_multi_line + ) + formatted_params = [format_func(param, i == total_params - 1) for i, param in enumerate(all_params)] + new_params, new_kwonly_params, new_star_arg, new_star_kwarg = self._restore_param_groups(formatted_params, node) + + return node.with_changes( + params=Parameters( + params=new_params, + posonly_params=node.params.posonly_params, + kwonly_params=new_kwonly_params, + star_arg=new_star_arg, + star_kwarg=new_star_kwarg, + ), + whitespace_before_params=ParenthesizedWhitespace( + last_line=SimpleWhitespace(' ' * self.indent_size), indent=True + ), + ) + + def leave_FunctionDef(self, original_node: FunctionDef, updated_node: FunctionDef) -> FunctionDef: + analysis = self.analyze_function(original_node) + if not analysis.should_process: + return updated_node + if analysis.issues: + self.issues.extend(analysis.issues) + return self._process_parameters(updated_node, self.is_single_line_function(original_node)) diff --git a/def_form/formatters/def_formatter/manager.py b/def_form/formatters/def_formatter/manager.py new file mode 100644 index 0000000..b8548fd --- /dev/null +++ b/def_form/formatters/def_formatter/manager.py @@ -0,0 +1,244 @@ +import os +import tomllib +from collections.abc import Generator +from pathlib import Path + +import click +import libcst as cst + +from def_form.exceptions.base import BaseDefFormException +from def_form.formatters.def_formatter.checker import DefChecker +from def_form.formatters.def_formatter.formatter import DefFormatter +from def_form.utils.find_pyproject import find_pyproject_toml + + +class DefManager: + def __init__( + self, + path: str, + excluded: tuple[str, ...] | None = None, + formatter: type[DefFormatter] = DefFormatter, + checker: type[DefChecker] = DefChecker, + max_def_length: int | None = None, + max_inline_args: int | None = None, + indent_size: int | None = None, + config: str | None = None, + show_skipped: bool = False, + ): + self.path = Path(path).resolve() + self.show_skipped = show_skipped + self.issues: list[BaseDefFormException] = [] + + self._init_config( + config=config, + max_def_length=max_def_length, + max_inline_args=max_inline_args, + indent_size=indent_size, + ) + + self._init_exclusions(excluded or ()) + + self.formatter_class = formatter + self.checker_class = checker + + def _init_config( + self, + config: str | None, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None, + ) -> None: + self.max_def_length = max_def_length + self.max_inline_args = max_inline_args + self.indent_size = indent_size + self.is_config_found = bool(config) + + if not config: + config = find_pyproject_toml() + self.is_config_found = bool(config) + + config_excluded: list[str] = [] + + if self.is_config_found and config: + try: + with open(config, 'rb') as f: + click.secho(f'Using config: {config}', fg='yellow') + config_data = tomllib.load(f) + + config_def = config_data.get('tool', {}).get('def-form', {}) + + self.max_def_length = config_def.get( + 'max_def_length', + self.max_def_length, + ) + self.max_inline_args = config_def.get( + 'max_inline_args', + self.max_inline_args, + ) + + self.indent_size = config_def.get( + 'indent_size', + self.indent_size, + ) + + config_excluded = config_def.get('exclude', []) + except (FileNotFoundError, tomllib.TOMLDecodeError) as e: + click.secho(f'Error loading config {config}: {e}', fg='red') + self.is_config_found = False + + self._config_excluded = config_excluded + + def _init_exclusions(self, cli_excluded: tuple[str, ...]) -> None: + self.excluded: set[Path] = set() + + for p in (*cli_excluded, *self._config_excluded): + try: + excluded_path = Path(p).resolve() + self.excluded.add(excluded_path) + except Exception: + click.secho(f'Warning: invalid excluded path: {p}', fg='yellow') + + def _iter_py_files(self) -> Generator[str, None, None]: + if self.path.is_file(): + if self.path.suffix != '.py': + return + + if self._is_excluded(self.path): + if self.show_skipped: + click.secho(f'SKIPPED {self.path}', fg='yellow') + return + + click.secho(f'Processing: {self.path}', fg='green') + yield str(self.path) + return + + for root, dirs, files in os.walk(self.path): + root_path = Path(root) + + dirs[:] = [d for d in dirs if not self._is_excluded(root_path / d)] + + for filename in files: + if not filename.endswith('.py'): + continue + + file_path = root_path / filename + + if self._is_excluded(file_path): + if self.show_skipped: + click.secho(f'SKIPPED {file_path}', fg='yellow') + continue + + click.secho(f'Processing: {file_path}', fg='green') + yield str(file_path) + + def _is_excluded(self, path: Path) -> bool: + for excluded in self.excluded: + try: + path.relative_to(excluded) + return True + except ValueError: + pass + + if excluded.name in path.parts: + return True + + return False + + def _create_processor( + self, processor_class: type[DefFormatter] | type[DefChecker], filepath: str + ) -> DefFormatter | DefChecker: + return processor_class( + filepath=filepath, + max_def_length=self.max_def_length, + max_inline_args=self.max_inline_args, + indent_size=self.indent_size, + ) + + def _process_file( + self, + filepath: str, + processor_class: type[DefFormatter] | type[DefChecker], + ) -> tuple[cst.Module | None, list[BaseDefFormException]]: + try: + with open(filepath, encoding='utf-8') as f: + code = f.read() + except (OSError, UnicodeDecodeError) as e: + click.secho(f'Error reading {filepath}: {e}', fg='red') + return None, [] + + try: + tree = cst.parse_module(code) + wrapper = cst.metadata.MetadataWrapper(tree) + processor = self._create_processor(processor_class, filepath) + + if issubclass(processor_class, DefFormatter): + new_tree = wrapper.visit(processor) + return new_tree, processor.issues + else: + wrapper.visit(processor) + return None, processor.issues + + except cst.ParserSyntaxError as e: + click.secho(f'Syntax error in {filepath}: {e}', fg='red') + return None, [] + except Exception as e: + click.secho(f'Unexpected error processing {filepath}: {e}', fg='red') + return None, [] + + def format(self, write_to: str | None = None) -> None: + """Форматирует файлы согласно правилам.""" + processed_count = 0 + self.issues.clear() + + for filepath in self._iter_py_files(): + processed_count += 1 + new_tree, file_issues = self._process_file(filepath, self.formatter_class) + + self.issues.extend(file_issues) + + if new_tree is not None: + try: + with open(write_to or filepath, 'w', encoding='utf-8') as f: + f.write(new_tree.code) + except OSError as e: + click.secho(f'Error writing {filepath}: {e}', fg='red') + + self._echo_summary('format', processed_count) + + def check(self) -> None: + processed_count = 0 + self.issues.clear() + + for filepath in self._iter_py_files(): + processed_count += 1 + _, file_issues = self._process_file(filepath, self.checker_class) + self.issues.extend(file_issues) + + self._echo_summary('check', processed_count) + + if self.issues: + raise BaseDefFormException + + def _echo_summary(self, mode: str, processed_count: int) -> None: + click.echo('\n') + + if self.issues: + click.secho('Issues:', fg='yellow', bold=True) + for i, issue in enumerate(self.issues, 1): + click.secho(f'{issue.path}', color=True) + click.secho(f' {issue.message}', fg='white') + if i != len(self.issues): + click.echo('') + + click.echo('') + click.secho(f'{mode.capitalize()} Summary:', fg='cyan', bold=True) + click.echo(f'Processed files: {processed_count}') + click.echo(f'Issues found: {len(self.issues)}') + + +if __name__ == '__main__': + DefManager( + path='../../../../test_file.py', + max_def_length=100, + max_inline_args=2, + ).format() diff --git a/def_form/formatters/def_formatter/models.py b/def_form/formatters/def_formatter/models.py new file mode 100644 index 0000000..5b40bc3 --- /dev/null +++ b/def_form/formatters/def_formatter/models.py @@ -0,0 +1,24 @@ +from dataclasses import dataclass + +from libcst import FunctionDef +from libcst._position import CodeRange + +from def_form.exceptions.base import BaseDefFormException + + +@dataclass +class FunctionAnalysis: + """Результат анализа функции.""" + + should_process: bool + reason: str | None = None + line_length: int | None = None + arg_count: int | None = None + line_no: int | None = None + pos: CodeRange | None = None + node: FunctionDef | None = None + issues: list[BaseDefFormException] = None + + def __post_init__(self): + if self.issues is None: + self.issues = [] diff --git a/def_form/utils/__init__.py b/def_form/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/def_form/utils/find_pyproject.py b/def_form/utils/find_pyproject.py new file mode 100644 index 0000000..1426368 --- /dev/null +++ b/def_form/utils/find_pyproject.py @@ -0,0 +1,10 @@ +from pathlib import Path + + +def find_pyproject_toml() -> Path | None: + current = Path.cwd() + for parent in [current] + list(current.parents): + candidate = parent / 'pyproject.toml' + if candidate.is_file(): + return candidate + return None diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5b6a54e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,158 @@ +[project] +name = "def-form" +version = "0.1.0" +description = "Formatter for functions" +readme = "README.md" +requires-python = ">=3.10" +license = "MIT" +authors = [ + { name = "TopNik073" }, +] +keywords = [ + "code-formatting", + "docstrings", + "formatter", + "functions", + "python", +] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Topic :: Software Development :: Code Generators", + "Topic :: Software Development :: Quality Assurance", + "Topic :: Software Development :: Libraries :: Python Modules", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] + + +dependencies = [ + "click>=8.2.1", + "libcst>=1.8.2", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project.scripts] +def-form = "def_form.cli.main:main" + +[project.urls] +Homepage = "https://github.com/TopNik073/def-form" +Repository = "https://github.com/TopNik073/def-form" +Issues = "https://github.com/TopNik073/def-form/issues" + +[dependency-groups] +dev = [ + "pytest>=8.3.5", + "pytest-cov>=7.0.0", + "ruff>=0.11.4,<0.12.0", + "mypy>=1.19.1", +] + +[tool.def-form] +max_def_length = 100 +max_inline_args = 2 +indent_size = 4 +exclude = [ + ".venv/", + "build/", +] + +[tool.mypy] +python_version = "3.10" +ignore_missing_imports = true +follow_imports = "silent" +disallow_untyped_defs = true +disallow_incomplete_defs = true +check_untyped_defs = true +strict_optional = true +warn_redundant_casts = true +warn_unused_ignores = true +no_implicit_reexport = true +exclude = [ + ".venv/", + "tests/mock_data/example.py", + "tests/mock_data/expected.py", +] + +[tool.ruff] +target-version = "py310" +line-length = 120 +exclude = [ + ".venv/", + "tests/mock_data/example.py", + "tests/mock_data/expected.py", +] +lint.flake8-tidy-imports.ban-relative-imports = "all" +lint.mccabe.max-complexity = 20 +lint.select = [ + "F", # Pyflakes + # "E/W", # pycodestyle + "C90", # mccabe + # "I", # isort + # "N", # pep8-naming + # "D", # pydocstyle + "UP", # pyupgrade + "YTT", # flake8-2020 + # "ANN", # flake8-annotations + # "ASYNC", # flake8-async + # "TRIO", # flake8-trio + # "S", # flake8-bandit + # "BLE", # flake8-blind-except + # "FBT", # flake8-boolean-trap + "B", # flake8-bugbear + # "A", # flake8-builtins + # "COM", # flake8-commas + # "CPY", # flake8-copyright + "C4", # flake8-comprehensions + # "DTZ", # flake8-datetimez + "T10", # flake8-debugger + # "DJ", # flake8-django + # "EM", # flake8-errmsg + # "EXE", # flake8-executable + # "FA", # flake8-future-annotations + # "ISC", # flake8-implicit-str-concat + # "ICN", # flake8-import-conventions + "G", # flake8-logging-format + "INP", # flake8-no-pep420 + # "PIE", # flake8-pie + "T20", # flake8-print + # "PYI", # flake8-pyi + # "PT", # flake8-pytest-style + # "Q", # flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + "SLF", # flake8-self + # "SLOT", # flake8-slots + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + # "TCH", # flake8-type-checking + # "INT", # flake8-gettext + # "ARG", # flake8-unused-arguments + "PTH", # flake8-use-pathlib + # "TD", # flake8-todos + # "FIX", # flake8-fixme + "ERA", # eradicate + # "PD", # pandas-vet + # "PGH", # pygrep-hooks + "PL", # Pylint + # "TRY", # tryceratops + # "FLY", # flynt + # "NPY", # NumPy-specific rules + # "AIR", # Airflow + # "PERF", # Perflint + # "FURB", # refurb + # "LOG", # flake8-logging + "RUF", # Ruff-specific rules +] + +[tool.ruff.format] +quote-style = "single" +docstring-code-format = true \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..468cacb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,45 @@ +from collections.abc import Callable + +import pytest + +from def_form.formatters.def_formatter import DefManager +from tests.constants import EXAMPLE_PATH +from tests.constants import EXPECTED_PATH +from tests.constants import FORMATTED_PATH +from tests.constants import MAX_DEF_LENGTH +from tests.constants import MAX_INLINE_ARGS + + +@pytest.fixture +def get_def_manager() -> DefManager: + return DefManager( + excluded=(), + max_def_length=MAX_DEF_LENGTH, + max_inline_args=MAX_INLINE_ARGS, + path=EXAMPLE_PATH, + ) + + +@pytest.fixture +def get_correct_def_manager() -> DefManager: + return DefManager( + excluded=(), + max_def_length=MAX_DEF_LENGTH, + max_inline_args=MAX_INLINE_ARGS, + path=EXPECTED_PATH, + ) + + +@pytest.fixture +def get_expected() -> str: + with open(EXPECTED_PATH, encoding='utf-8') as f: + return f.read() + + +@pytest.fixture +def get_formatted() -> Callable[[], str]: + def read() -> str: + with open(FORMATTED_PATH, encoding='utf-8') as f: + return f.read() + + return read diff --git a/tests/constants.py b/tests/constants.py new file mode 100644 index 0000000..e4a9b94 --- /dev/null +++ b/tests/constants.py @@ -0,0 +1,11 @@ +from pathlib import Path + +MAX_DEF_LENGTH = 100 +MAX_INLINE_ARGS = 2 +EXPECTED_TOTAL_ISSUES = 23 + +src_path = Path(__file__).resolve().parent + +EXAMPLE_PATH: str = str(src_path / 'mock_data/example.py') +EXPECTED_PATH: str = str(src_path / 'mock_data/expected.py') +FORMATTED_PATH: str = str(src_path / 'mock_data/formatted.py') diff --git a/tests/mock_data/__init__.py b/tests/mock_data/__init__.py new file mode 100644 index 0000000..6b3c73e --- /dev/null +++ b/tests/mock_data/__init__.py @@ -0,0 +1,75 @@ +from def_form.exceptions.def_formatter import DefStringTooLongException +from def_form.exceptions.def_formatter import TooManyInlineArgumentsException +from tests.constants import EXAMPLE_PATH + +EXPECTED_ISSUES: list[DefStringTooLongException | TooManyInlineArgumentsException] = [ + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:19', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:25', message='Too many inline args (4 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:28', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:31', message='Too many inline args (4 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:34', message='Too many inline args (3 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:42', message='Too many inline args (4 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:58', message='Too many inline args (4 > 2)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:61', message='Function definition too long (165 > 100)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:64', message='Function definition too long (163 > 100)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:67', message='Function definition too long (126 > 100)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:75', message='Too many inline args (4 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:78', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:84', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:88', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:103', message='Too many inline args (5 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:110', message='Too many inline args (6 > 2)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:113', message='Too many inline args (3 > 2)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:116', message='Function definition too long (174 > 100)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:119', message='Function definition too long (199 > 100)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:119', message='Too many inline args (5 > 2)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:122', message='Function definition too long (127 > 100)', description=None + ), + DefStringTooLongException( + path=f'{EXAMPLE_PATH}:125', message='Function definition too long (138 > 100)', description=None + ), + TooManyInlineArgumentsException( + path=f'{EXAMPLE_PATH}:125', message='Too many inline args (3 > 2)', description=None + ), +] diff --git a/tests/mock_data/example.py b/tests/mock_data/example.py new file mode 100644 index 0000000..b376196 --- /dev/null +++ b/tests/mock_data/example.py @@ -0,0 +1,126 @@ +from typing import Optional, Union, Callable + + +def example_of_decorator(f: Callable) -> Callable: + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + return wrapper + +def clear_def(): + return + +def formatted_def( + a, + b: Optional[Union[int, str]] = None, + c: int | str | None = None, +): + return + +def to_much_inline_args(to, much, inline, arguments): + return + +async def async_def(): + return + +async def async_def_with_args(a, b, c): + return + +def to_much_inline_args_with_typehints(to: str, much, inline: int, arguments): + return + +def kw_args(def_, with_, *args, **kwargs): + return + +def def_kw_args_first(a, *args, b, **kwargs): + return + +@example_of_decorator +def def_with_decorator(): + return + +@example_of_decorator +def def_with_decorator_and_args(a, b, c: Optional[str]): + return + +def skipped_with_right_comment(a, b, c): # def-form: skip + return + +# def-form: skip +def skipped_with_up_comment(a, b, c): + return + +@example_of_decorator +def skipped_with_right_comment_and_decorator(a, b, c): # def-form: skip + return + +# def-form: skip +@example_of_decorator +def skipped_with_up_comment_and_decorator(a, b, c): + return + +def long_typehint_without_args() -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + +def long_typehint_with_args(a) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + +def loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_def_name(a): + return + + +class ClassWithFunctions: + def class_method(self): + return + + def class_method_with_args(self, a, b): + return + + def class_method_with_args_with_typehints(self, a: Optional[int], b: int, c): + return + + async def async_def(self): + return + + async def async_def_with_args(self, a, b, c): + return + + @example_of_decorator + async def async_def_with_args_and_decorator(self, a, b, c): + return + + def skipped_with_right_comment(self, a, b, c): # def-form: skip + return + + # def-form: skip + def skipped_with_up_comment(self, a, b, c): + return + + @example_of_decorator + def def_with_decorator(self): + return + + @example_of_decorator + def def_with_decorator_and_args(self, a, b, c): + return + + @staticmethod + def static_method(): + return + + def def_with_kw_args(self, a, b, c, *args, **kwargs): + return + + def def_with_kw_args_first(self, *args, a, b, c, **kwargs): + return + + def class_method_with_long_typehint(self) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + + def class_method_with_long_typehint_with_args(self, a, b: int, c) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + + def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name(self): + return + + def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name_and_arg(self, a): + return diff --git a/tests/mock_data/expected.py b/tests/mock_data/expected.py new file mode 100644 index 0000000..1037a62 --- /dev/null +++ b/tests/mock_data/expected.py @@ -0,0 +1,213 @@ +from typing import Optional, Union, Callable + + +def example_of_decorator(f: Callable) -> Callable: + def wrapper(*args, **kwargs): + return f(*args, **kwargs) + return wrapper + +def clear_def(): + return + +def formatted_def( + a, + b: Optional[Union[int, str]] = None, + c: int | str | None = None, +): + return + +def to_much_inline_args( + to, + much, + inline, + arguments, +): + return + +async def async_def(): + return + +async def async_def_with_args( + a, + b, + c, +): + return + +def to_much_inline_args_with_typehints( + to: str, + much, + inline: int, + arguments, +): + return + +def kw_args( + def_, + with_, + *args, + **kwargs, +): + return + +def def_kw_args_first( + a, + *args, + b, + **kwargs, +): + return + +@example_of_decorator +def def_with_decorator(): + return + +@example_of_decorator +def def_with_decorator_and_args( + a, + b, + c: Optional[str], +): + return + +def skipped_with_right_comment(a, b, c): # def-form: skip + return + +# def-form: skip +def skipped_with_up_comment(a, b, c): + return + +@example_of_decorator +def skipped_with_right_comment_and_decorator(a, b, c): # def-form: skip + return + +# def-form: skip +@example_of_decorator +def skipped_with_up_comment_and_decorator( + a, + b, + c, +): + return + +def long_typehint_without_args( + ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + +def long_typehint_with_args( + a, +) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + +def loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_def_name( + a, +): + return + + +class ClassWithFunctions: + def class_method(self): + return + + def class_method_with_args( + self, + a, + b, + ): + return + + def class_method_with_args_with_typehints( + self, + a: Optional[int], + b: int, + c, + ): + return + + async def async_def(self): + return + + async def async_def_with_args( + self, + a, + b, + c, + ): + return + + @example_of_decorator + async def async_def_with_args_and_decorator( + self, + a, + b, + c, + ): + return + + def skipped_with_right_comment(self, a, b, c): # def-form: skip + return + + # def-form: skip + def skipped_with_up_comment(self, a, b, c): + return + + @example_of_decorator + def def_with_decorator(self): + return + + @example_of_decorator + def def_with_decorator_and_args( + self, + a, + b, + c, + ): + return + + @staticmethod + def static_method(): + return + + def def_with_kw_args( + self, + a, + b, + c, + *args, + **kwargs, + ): + return + + def def_with_kw_args_first( + self, + *args, + a, + b, + c, + **kwargs, + ): + return + + def class_method_with_long_typehint( + self, + ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + + def class_method_with_long_typehint_with_args( + self, + a, + b: int, + c, + ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: + return + + def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name( + self, + ): + return + + def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name_and_arg( + self, + a, + ): + return diff --git a/tests/test_checker/__init__.py b/tests/test_checker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_checker/test_checker.py b/tests/test_checker/test_checker.py new file mode 100644 index 0000000..1812141 --- /dev/null +++ b/tests/test_checker/test_checker.py @@ -0,0 +1,18 @@ +from def_form.exceptions.base import BaseDefFormException +from def_form.formatters.def_formatter import DefManager +from tests.constants import EXPECTED_TOTAL_ISSUES +from tests.mock_data import EXPECTED_ISSUES + + +def test_failed_check(get_def_manager: DefManager): + try: + get_def_manager.check() + except BaseDefFormException as e: + assert isinstance(e, BaseDefFormException) + assert get_def_manager.issues == EXPECTED_ISSUES + assert len(get_def_manager.issues) == EXPECTED_TOTAL_ISSUES + + +def test_successful_check(get_correct_def_manager: DefManager): + get_correct_def_manager.check() + assert len(get_correct_def_manager.issues) == 0 diff --git a/tests/test_formatter/__init__.py b/tests/test_formatter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_formatter/test_formatter.py b/tests/test_formatter/test_formatter.py new file mode 100644 index 0000000..150dc06 --- /dev/null +++ b/tests/test_formatter/test_formatter.py @@ -0,0 +1,21 @@ +import os +from collections.abc import Callable + +from def_form.formatters.def_formatter import DefManager +from tests.constants import EXPECTED_TOTAL_ISSUES +from tests.constants import FORMATTED_PATH +from tests.mock_data import EXPECTED_ISSUES + + +def test_successful_format(get_def_manager: DefManager, get_expected: str, get_formatted: Callable[[], str]): + get_def_manager.format(write_to=FORMATTED_PATH) + assert len(get_def_manager.issues) == EXPECTED_TOTAL_ISSUES + assert get_def_manager.issues == EXPECTED_ISSUES + assert get_expected == get_formatted() + + os.remove(FORMATTED_PATH) + + +def test_no_need_format(get_correct_def_manager: DefManager): + get_correct_def_manager.format() + assert len(get_correct_def_manager.issues) == 0 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..e5815bf --- /dev/null +++ b/uv.lock @@ -0,0 +1,624 @@ +version = 1 +revision = 2 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version == '3.13.*'", + "python_full_version < '3.13'", +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/49/349848445b0e53660e258acbcc9b0d014895b6739237920886672240f84b/coverage-7.13.2.tar.gz", hash = "sha256:044c6951ec37146b72a50cc81ef02217d27d4c3640efd2640311393cbbf143d3", size = 826523, upload-time = "2026-01-25T13:00:04.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/2d/63e37369c8e81a643afe54f76073b020f7b97ddbe698c5c944b51b0a2bc5/coverage-7.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4af3b01763909f477ea17c962e2cca8f39b350a4e46e3a30838b2c12e31b81b", size = 218842, upload-time = "2026-01-25T12:57:15.3Z" }, + { url = "https://files.pythonhosted.org/packages/57/06/86ce882a8d58cbcb3030e298788988e618da35420d16a8c66dac34f138d0/coverage-7.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36393bd2841fa0b59498f75466ee9bdec4f770d3254f031f23e8fd8e140ffdd2", size = 219360, upload-time = "2026-01-25T12:57:17.572Z" }, + { url = "https://files.pythonhosted.org/packages/cd/84/70b0eb1ee19ca4ef559c559054c59e5b2ae4ec9af61398670189e5d276e9/coverage-7.13.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cc7573518b7e2186bd229b1a0fe24a807273798832c27032c4510f47ffdb896", size = 246123, upload-time = "2026-01-25T12:57:19.087Z" }, + { url = "https://files.pythonhosted.org/packages/35/fb/05b9830c2e8275ebc031e0019387cda99113e62bb500ab328bb72578183b/coverage-7.13.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ca9566769b69a5e216a4e176d54b9df88f29d750c5b78dbb899e379b4e14b30c", size = 247930, upload-time = "2026-01-25T12:57:20.929Z" }, + { url = "https://files.pythonhosted.org/packages/81/aa/3f37858ca2eed4f09b10ca3c6ddc9041be0a475626cd7fd2712f4a2d526f/coverage-7.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c9bdea644e94fd66d75a6f7e9a97bb822371e1fe7eadae2cacd50fcbc28e4dc", size = 249804, upload-time = "2026-01-25T12:57:22.904Z" }, + { url = "https://files.pythonhosted.org/packages/b6/b3/c904f40c56e60a2d9678a5ee8df3d906d297d15fb8bec5756c3b0a67e2df/coverage-7.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5bd447332ec4f45838c1ad42268ce21ca87c40deb86eabd59888859b66be22a5", size = 246815, upload-time = "2026-01-25T12:57:24.314Z" }, + { url = "https://files.pythonhosted.org/packages/41/91/ddc1c5394ca7fd086342486440bfdd6b9e9bda512bf774599c7c7a0081e0/coverage-7.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7c79ad5c28a16a1277e1187cf83ea8dafdcc689a784228a7d390f19776db7c31", size = 247843, upload-time = "2026-01-25T12:57:26.544Z" }, + { url = "https://files.pythonhosted.org/packages/87/d2/cdff8f4cd33697883c224ea8e003e9c77c0f1a837dc41d95a94dd26aad67/coverage-7.13.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:76e06ccacd1fb6ada5d076ed98a8c6f66e2e6acd3df02819e2ee29fd637b76ad", size = 245850, upload-time = "2026-01-25T12:57:28.507Z" }, + { url = "https://files.pythonhosted.org/packages/f5/42/e837febb7866bf2553ab53dd62ed52f9bb36d60c7e017c55376ad21fbb05/coverage-7.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:49d49e9a5e9f4dc3d3dac95278a020afa6d6bdd41f63608a76fa05a719d5b66f", size = 246116, upload-time = "2026-01-25T12:57:30.16Z" }, + { url = "https://files.pythonhosted.org/packages/09/b1/4a3f935d7df154df02ff4f71af8d61298d713a7ba305d050ae475bfbdde2/coverage-7.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed2bce0e7bfa53f7b0b01c722da289ef6ad4c18ebd52b1f93704c21f116360c8", size = 246720, upload-time = "2026-01-25T12:57:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/538a6fd44c515f1c5197a3f078094cbaf2ce9f945df5b44e29d95c864bff/coverage-7.13.2-cp310-cp310-win32.whl", hash = "sha256:1574983178b35b9af4db4a9f7328a18a14a0a0ce76ffaa1c1bacb4cc82089a7c", size = 221465, upload-time = "2026-01-25T12:57:33.511Z" }, + { url = "https://files.pythonhosted.org/packages/5e/09/4b63a024295f326ec1a40ec8def27799300ce8775b1cbf0d33b1790605c4/coverage-7.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:a360a8baeb038928ceb996f5623a4cd508728f8f13e08d4e96ce161702f3dd99", size = 222397, upload-time = "2026-01-25T12:57:34.927Z" }, + { url = "https://files.pythonhosted.org/packages/6c/01/abca50583a8975bb6e1c59eff67ed8e48bb127c07dad5c28d9e96ccc09ec/coverage-7.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:060ebf6f2c51aff5ba38e1f43a2095e087389b1c69d559fde6049a4b0001320e", size = 218971, upload-time = "2026-01-25T12:57:36.953Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0e/b6489f344d99cd1e5b4d5e1be52dfd3f8a3dc5112aa6c33948da8cabad4e/coverage-7.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1ea8ca9db5e7469cd364552985e15911548ea5b69c48a17291f0cac70484b2e", size = 219473, upload-time = "2026-01-25T12:57:38.934Z" }, + { url = "https://files.pythonhosted.org/packages/17/11/db2f414915a8e4ec53f60b17956c27f21fb68fcf20f8a455ce7c2ccec638/coverage-7.13.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b780090d15fd58f07cf2011943e25a5f0c1c894384b13a216b6c86c8a8a7c508", size = 249896, upload-time = "2026-01-25T12:57:40.365Z" }, + { url = "https://files.pythonhosted.org/packages/80/06/0823fe93913663c017e508e8810c998c8ebd3ec2a5a85d2c3754297bdede/coverage-7.13.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:88a800258d83acb803c38175b4495d293656d5fac48659c953c18e5f539a274b", size = 251810, upload-time = "2026-01-25T12:57:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/61/dc/b151c3cc41b28cdf7f0166c5fa1271cbc305a8ec0124cce4b04f74791a18/coverage-7.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6326e18e9a553e674d948536a04a80d850a5eeefe2aae2e6d7cf05d54046c01b", size = 253920, upload-time = "2026-01-25T12:57:44.026Z" }, + { url = "https://files.pythonhosted.org/packages/2d/35/e83de0556e54a4729a2b94ea816f74ce08732e81945024adee46851c2264/coverage-7.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:59562de3f797979e1ff07c587e2ac36ba60ca59d16c211eceaa579c266c5022f", size = 250025, upload-time = "2026-01-25T12:57:45.624Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/af2eb9c3926ce3ea0d58a0d2516fcbdacf7a9fc9559fe63076beaf3f2596/coverage-7.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:27ba1ed6f66b0e2d61bfa78874dffd4f8c3a12f8e2b5410e515ab345ba7bc9c3", size = 251612, upload-time = "2026-01-25T12:57:47.713Z" }, + { url = "https://files.pythonhosted.org/packages/26/62/5be2e25f3d6c711d23b71296f8b44c978d4c8b4e5b26871abfc164297502/coverage-7.13.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8be48da4d47cc68754ce643ea50b3234557cbefe47c2f120495e7bd0a2756f2b", size = 249670, upload-time = "2026-01-25T12:57:49.378Z" }, + { url = "https://files.pythonhosted.org/packages/b3/51/400d1b09a8344199f9b6a6fc1868005d766b7ea95e7882e494fa862ca69c/coverage-7.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2a47a4223d3361b91176aedd9d4e05844ca67d7188456227b6bf5e436630c9a1", size = 249395, upload-time = "2026-01-25T12:57:50.86Z" }, + { url = "https://files.pythonhosted.org/packages/e0/36/f02234bc6e5230e2f0a63fd125d0a2093c73ef20fdf681c7af62a140e4e7/coverage-7.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c6f141b468740197d6bd38f2b26ade124363228cc3f9858bd9924ab059e00059", size = 250298, upload-time = "2026-01-25T12:57:52.287Z" }, + { url = "https://files.pythonhosted.org/packages/b0/06/713110d3dd3151b93611c9cbfc65c15b4156b44f927fced49ac0b20b32a4/coverage-7.13.2-cp311-cp311-win32.whl", hash = "sha256:89567798404af067604246e01a49ef907d112edf2b75ef814b1364d5ce267031", size = 221485, upload-time = "2026-01-25T12:57:53.876Z" }, + { url = "https://files.pythonhosted.org/packages/16/0c/3ae6255fa1ebcb7dec19c9a59e85ef5f34566d1265c70af5b2fc981da834/coverage-7.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:21dd57941804ae2ac7e921771a5e21bbf9aabec317a041d164853ad0a96ce31e", size = 222421, upload-time = "2026-01-25T12:57:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/b5/37/fabc3179af4d61d89ea47bd04333fec735cd5e8b59baad44fed9fc4170d7/coverage-7.13.2-cp311-cp311-win_arm64.whl", hash = "sha256:10758e0586c134a0bafa28f2d37dd2cdb5e4a90de25c0fc0c77dabbad46eca28", size = 221088, upload-time = "2026-01-25T12:57:57.41Z" }, + { url = "https://files.pythonhosted.org/packages/46/39/e92a35f7800222d3f7b2cbb7bbc3b65672ae8d501cb31801b2d2bd7acdf1/coverage-7.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f106b2af193f965d0d3234f3f83fc35278c7fb935dfbde56ae2da3dd2c03b84d", size = 219142, upload-time = "2026-01-25T12:58:00.448Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/8bf9e9309c4c996e65c52a7c5a112707ecdd9fbaf49e10b5a705a402bbb4/coverage-7.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f45d21dc4d5d6bd29323f0320089ef7eae16e4bef712dff79d184fa7330af3", size = 219503, upload-time = "2026-01-25T12:58:02.451Z" }, + { url = "https://files.pythonhosted.org/packages/87/93/17661e06b7b37580923f3f12406ac91d78aeed293fb6da0b69cc7957582f/coverage-7.13.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fae91dfecd816444c74531a9c3d6ded17a504767e97aa674d44f638107265b99", size = 251006, upload-time = "2026-01-25T12:58:04.059Z" }, + { url = "https://files.pythonhosted.org/packages/12/f0/f9e59fb8c310171497f379e25db060abef9fa605e09d63157eebec102676/coverage-7.13.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:264657171406c114787b441484de620e03d8f7202f113d62fcd3d9688baa3e6f", size = 253750, upload-time = "2026-01-25T12:58:05.574Z" }, + { url = "https://files.pythonhosted.org/packages/e5/b1/1935e31add2232663cf7edd8269548b122a7d100047ff93475dbaaae673e/coverage-7.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae47d8dcd3ded0155afbb59c62bd8ab07ea0fd4902e1c40567439e6db9dcaf2f", size = 254862, upload-time = "2026-01-25T12:58:07.647Z" }, + { url = "https://files.pythonhosted.org/packages/af/59/b5e97071ec13df5f45da2b3391b6cdbec78ba20757bc92580a5b3d5fa53c/coverage-7.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a0b33e9fd838220b007ce8f299114d406c1e8edb21336af4c97a26ecfd185aa", size = 251420, upload-time = "2026-01-25T12:58:09.309Z" }, + { url = "https://files.pythonhosted.org/packages/3f/75/9495932f87469d013dc515fb0ce1aac5fa97766f38f6b1a1deb1ee7b7f3a/coverage-7.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b3becbea7f3ce9a2d4d430f223ec15888e4deb31395840a79e916368d6004cce", size = 252786, upload-time = "2026-01-25T12:58:10.909Z" }, + { url = "https://files.pythonhosted.org/packages/6a/59/af550721f0eb62f46f7b8cb7e6f1860592189267b1c411a4e3a057caacee/coverage-7.13.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f819c727a6e6eeb8711e4ce63d78c620f69630a2e9d53bc95ca5379f57b6ba94", size = 250928, upload-time = "2026-01-25T12:58:12.449Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b1/21b4445709aae500be4ab43bbcfb4e53dc0811c3396dcb11bf9f23fd0226/coverage-7.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4f7b71757a3ab19f7ba286e04c181004c1d61be921795ee8ba6970fd0ec91da5", size = 250496, upload-time = "2026-01-25T12:58:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/0f5d89dfe0392990e4f3980adbde3eb34885bc1effb2dc369e0bf385e389/coverage-7.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b7fc50d2afd2e6b4f6f2f403b70103d280a8e0cb35320cbbe6debcda02a1030b", size = 252373, upload-time = "2026-01-25T12:58:15.976Z" }, + { url = "https://files.pythonhosted.org/packages/01/c9/0cf1a6a57a9968cc049a6b896693faa523c638a5314b1fc374eb2b2ac904/coverage-7.13.2-cp312-cp312-win32.whl", hash = "sha256:292250282cf9bcf206b543d7608bda17ca6fc151f4cbae949fc7e115112fbd41", size = 221696, upload-time = "2026-01-25T12:58:17.517Z" }, + { url = "https://files.pythonhosted.org/packages/4d/05/d7540bf983f09d32803911afed135524570f8c47bb394bf6206c1dc3a786/coverage-7.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:eeea10169fac01549a7921d27a3e517194ae254b542102267bef7a93ed38c40e", size = 222504, upload-time = "2026-01-25T12:58:19.115Z" }, + { url = "https://files.pythonhosted.org/packages/15/8b/1a9f037a736ced0a12aacf6330cdaad5008081142a7070bc58b0f7930cbc/coverage-7.13.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a5b567f0b635b592c917f96b9a9cb3dbd4c320d03f4bf94e9084e494f2e8894", size = 221120, upload-time = "2026-01-25T12:58:21.334Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/3d3eac7568ab6096ff23791a526b0048a1ff3f49d0e236b2af6fb6558e88/coverage-7.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed75de7d1217cf3b99365d110975f83af0528c849ef5180a12fd91b5064df9d6", size = 219168, upload-time = "2026-01-25T12:58:23.376Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a6/f8b5cfeddbab95fdef4dcd682d82e5dcff7a112ced57a959f89537ee9995/coverage-7.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97e596de8fa9bada4d88fde64a3f4d37f1b6131e4faa32bad7808abc79887ddc", size = 219537, upload-time = "2026-01-25T12:58:24.932Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/8d8e6e0c516c838229d1e41cadcec91745f4b1031d4db17ce0043a0423b4/coverage-7.13.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:68c86173562ed4413345410c9480a8d64864ac5e54a5cda236748031e094229f", size = 250528, upload-time = "2026-01-25T12:58:26.567Z" }, + { url = "https://files.pythonhosted.org/packages/8e/78/befa6640f74092b86961f957f26504c8fba3d7da57cc2ab7407391870495/coverage-7.13.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7be4d613638d678b2b3773b8f687537b284d7074695a43fe2fbbfc0e31ceaed1", size = 253132, upload-time = "2026-01-25T12:58:28.251Z" }, + { url = "https://files.pythonhosted.org/packages/9d/10/1630db1edd8ce675124a2ee0f7becc603d2bb7b345c2387b4b95c6907094/coverage-7.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f63ce526a96acd0e16c4af8b50b64334239550402fb1607ce6a584a6d62ce9", size = 254374, upload-time = "2026-01-25T12:58:30.294Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1d/0d9381647b1e8e6d310ac4140be9c428a0277330991e0c35bdd751e338a4/coverage-7.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:406821f37f864f968e29ac14c3fccae0fec9fdeba48327f0341decf4daf92d7c", size = 250762, upload-time = "2026-01-25T12:58:32.036Z" }, + { url = "https://files.pythonhosted.org/packages/43/e4/5636dfc9a7c871ee8776af83ee33b4c26bc508ad6cee1e89b6419a366582/coverage-7.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ee68e5a4e3e5443623406b905db447dceddffee0dceb39f4e0cd9ec2a35004b5", size = 252502, upload-time = "2026-01-25T12:58:33.961Z" }, + { url = "https://files.pythonhosted.org/packages/02/2a/7ff2884d79d420cbb2d12fed6fff727b6d0ef27253140d3cdbbd03187ee0/coverage-7.13.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2ee0e58cca0c17dd9c6c1cdde02bb705c7b3fbfa5f3b0b5afeda20d4ebff8ef4", size = 250463, upload-time = "2026-01-25T12:58:35.529Z" }, + { url = "https://files.pythonhosted.org/packages/91/c0/ba51087db645b6c7261570400fc62c89a16278763f36ba618dc8657a187b/coverage-7.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:6e5bbb5018bf76a56aabdb64246b5288d5ae1b7d0dd4d0534fe86df2c2992d1c", size = 250288, upload-time = "2026-01-25T12:58:37.226Z" }, + { url = "https://files.pythonhosted.org/packages/03/07/44e6f428551c4d9faf63ebcefe49b30e5c89d1be96f6a3abd86a52da9d15/coverage-7.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a55516c68ef3e08e134e818d5e308ffa6b1337cc8b092b69b24287bf07d38e31", size = 252063, upload-time = "2026-01-25T12:58:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/c2/67/35b730ad7e1859dd57e834d1bc06080d22d2f87457d53f692fce3f24a5a9/coverage-7.13.2-cp313-cp313-win32.whl", hash = "sha256:5b20211c47a8abf4abc3319d8ce2464864fa9f30c5fcaf958a3eed92f4f1fef8", size = 221716, upload-time = "2026-01-25T12:58:40.484Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/e5fcf5a97c72f45fc14829237a6550bf49d0ab882ac90e04b12a69db76b4/coverage-7.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:14f500232e521201cf031549fb1ebdfc0a40f401cf519157f76c397e586c3beb", size = 222522, upload-time = "2026-01-25T12:58:43.247Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/25d7b2f946d239dd2d6644ca2cc060d24f97551e2af13b6c24c722ae5f97/coverage-7.13.2-cp313-cp313-win_arm64.whl", hash = "sha256:9779310cb5a9778a60c899f075a8514c89fa6d10131445c2207fc893e0b14557", size = 221145, upload-time = "2026-01-25T12:58:45Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f7/080376c029c8f76fadfe43911d0daffa0cbdc9f9418a0eead70c56fb7f4b/coverage-7.13.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5a1e41ce5df6b547cbc3d3699381c9e2c2c369c67837e716ed0f549d48e", size = 219861, upload-time = "2026-01-25T12:58:46.586Z" }, + { url = "https://files.pythonhosted.org/packages/42/11/0b5e315af5ab35f4c4a70e64d3314e4eec25eefc6dec13be3a7d5ffe8ac5/coverage-7.13.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b01899e82a04085b6561eb233fd688474f57455e8ad35cd82286463ba06332b7", size = 220207, upload-time = "2026-01-25T12:58:48.277Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0c/0874d0318fb1062117acbef06a09cf8b63f3060c22265adaad24b36306b7/coverage-7.13.2-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:838943bea48be0e2768b0cf7819544cdedc1bbb2f28427eabb6eb8c9eb2285d3", size = 261504, upload-time = "2026-01-25T12:58:49.904Z" }, + { url = "https://files.pythonhosted.org/packages/83/5e/1cd72c22ecb30751e43a72f40ba50fcef1b7e93e3ea823bd9feda8e51f9a/coverage-7.13.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:93d1d25ec2b27e90bcfef7012992d1f5121b51161b8bffcda756a816cf13c2c3", size = 263582, upload-time = "2026-01-25T12:58:51.582Z" }, + { url = "https://files.pythonhosted.org/packages/9b/da/8acf356707c7a42df4d0657020308e23e5a07397e81492640c186268497c/coverage-7.13.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93b57142f9621b0d12349c43fc7741fe578e4bc914c1e5a54142856cfc0bf421", size = 266008, upload-time = "2026-01-25T12:58:53.234Z" }, + { url = "https://files.pythonhosted.org/packages/41/41/ea1730af99960309423c6ea8d6a4f1fa5564b2d97bd1d29dda4b42611f04/coverage-7.13.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f06799ae1bdfff7ccb8665d75f8291c69110ba9585253de254688aa8a1ccc6c5", size = 260762, upload-time = "2026-01-25T12:58:55.372Z" }, + { url = "https://files.pythonhosted.org/packages/22/fa/02884d2080ba71db64fdc127b311db60e01fe6ba797d9c8363725e39f4d5/coverage-7.13.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f9405ab4f81d490811b1d91c7a20361135a2df4c170e7f0b747a794da5b7f23", size = 263571, upload-time = "2026-01-25T12:58:57.52Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6b/4083aaaeba9b3112f55ac57c2ce7001dc4d8fa3fcc228a39f09cc84ede27/coverage-7.13.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f9ab1d5b86f8fbc97a5b3cd6280a3fd85fef3b028689d8a2c00918f0d82c728c", size = 261200, upload-time = "2026-01-25T12:58:59.255Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d2/aea92fa36d61955e8c416ede9cf9bf142aa196f3aea214bb67f85235a050/coverage-7.13.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:f674f59712d67e841525b99e5e2b595250e39b529c3bda14764e4f625a3fa01f", size = 260095, upload-time = "2026-01-25T12:59:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ae/04ffe96a80f107ea21b22b2367175c621da920063260a1c22f9452fd7866/coverage-7.13.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c6cadac7b8ace1ba9144feb1ae3cb787a6065ba6d23ffc59a934b16406c26573", size = 262284, upload-time = "2026-01-25T12:59:02.802Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7a/6f354dcd7dfc41297791d6fb4e0d618acb55810bde2c1fd14b3939e05c2b/coverage-7.13.2-cp313-cp313t-win32.whl", hash = "sha256:14ae4146465f8e6e6253eba0cccd57423e598a4cb925958b240c805300918343", size = 222389, upload-time = "2026-01-25T12:59:04.563Z" }, + { url = "https://files.pythonhosted.org/packages/8d/d5/080ad292a4a3d3daf411574be0a1f56d6dee2c4fdf6b005342be9fac807f/coverage-7.13.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9074896edd705a05769e3de0eac0a8388484b503b68863dd06d5e473f874fd47", size = 223450, upload-time = "2026-01-25T12:59:06.677Z" }, + { url = "https://files.pythonhosted.org/packages/88/96/df576fbacc522e9fb8d1c4b7a7fc62eb734be56e2cba1d88d2eabe08ea3f/coverage-7.13.2-cp313-cp313t-win_arm64.whl", hash = "sha256:69e526e14f3f854eda573d3cf40cffd29a1a91c684743d904c33dbdcd0e0f3e7", size = 221707, upload-time = "2026-01-25T12:59:08.363Z" }, + { url = "https://files.pythonhosted.org/packages/55/53/1da9e51a0775634b04fcc11eb25c002fc58ee4f92ce2e8512f94ac5fc5bf/coverage-7.13.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:387a825f43d680e7310e6f325b2167dd093bc8ffd933b83e9aa0983cf6e0a2ef", size = 219213, upload-time = "2026-01-25T12:59:11.909Z" }, + { url = "https://files.pythonhosted.org/packages/46/35/b3caac3ebbd10230fea5a33012b27d19e999a17c9285c4228b4b2e35b7da/coverage-7.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f0d7fea9d8e5d778cd5a9e8fc38308ad688f02040e883cdc13311ef2748cb40f", size = 219549, upload-time = "2026-01-25T12:59:13.638Z" }, + { url = "https://files.pythonhosted.org/packages/76/9c/e1cf7def1bdc72c1907e60703983a588f9558434a2ff94615747bd73c192/coverage-7.13.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e080afb413be106c95c4ee96b4fffdc9e2fa56a8bbf90b5c0918e5c4449412f5", size = 250586, upload-time = "2026-01-25T12:59:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ba/49/f54ec02ed12be66c8d8897270505759e057b0c68564a65c429ccdd1f139e/coverage-7.13.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7fc042ba3c7ce25b8a9f097eb0f32a5ce1ccdb639d9eec114e26def98e1f8a4", size = 253093, upload-time = "2026-01-25T12:59:17.491Z" }, + { url = "https://files.pythonhosted.org/packages/fb/5e/aaf86be3e181d907e23c0f61fccaeb38de8e6f6b47aed92bf57d8fc9c034/coverage-7.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d0ba505e021557f7f8173ee8cd6b926373d8653e5ff7581ae2efce1b11ef4c27", size = 254446, upload-time = "2026-01-25T12:59:19.752Z" }, + { url = "https://files.pythonhosted.org/packages/28/c8/a5fa01460e2d75b0c853b392080d6829d3ca8b5ab31e158fa0501bc7c708/coverage-7.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7de326f80e3451bd5cc7239ab46c73ddb658fe0b7649476bc7413572d36cd548", size = 250615, upload-time = "2026-01-25T12:59:21.928Z" }, + { url = "https://files.pythonhosted.org/packages/86/0b/6d56315a55f7062bb66410732c24879ccb2ec527ab6630246de5fe45a1df/coverage-7.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abaea04f1e7e34841d4a7b343904a3f59481f62f9df39e2cd399d69a187a9660", size = 252452, upload-time = "2026-01-25T12:59:23.592Z" }, + { url = "https://files.pythonhosted.org/packages/30/19/9bc550363ebc6b0ea121977ee44d05ecd1e8bf79018b8444f1028701c563/coverage-7.13.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9f93959ee0c604bccd8e0697be21de0887b1f73efcc3aa73a3ec0fd13feace92", size = 250418, upload-time = "2026-01-25T12:59:25.392Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/580530a31ca2f0cc6f07a8f2ab5460785b02bb11bdf815d4c4d37a4c5169/coverage-7.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:13fe81ead04e34e105bf1b3c9f9cdf32ce31736ee5d90a8d2de02b9d3e1bcb82", size = 250231, upload-time = "2026-01-25T12:59:27.888Z" }, + { url = "https://files.pythonhosted.org/packages/e2/42/dd9093f919dc3088cb472893651884bd675e3df3d38a43f9053656dca9a2/coverage-7.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6d16b0f71120e365741bca2cb473ca6fe38930bc5431c5e850ba949f708f892", size = 251888, upload-time = "2026-01-25T12:59:29.636Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a6/0af4053e6e819774626e133c3d6f70fae4d44884bfc4b126cb647baee8d3/coverage-7.13.2-cp314-cp314-win32.whl", hash = "sha256:9b2f4714bb7d99ba3790ee095b3b4ac94767e1347fe424278a0b10acb3ff04fe", size = 221968, upload-time = "2026-01-25T12:59:31.424Z" }, + { url = "https://files.pythonhosted.org/packages/c4/cc/5aff1e1f80d55862442855517bb8ad8ad3a68639441ff6287dde6a58558b/coverage-7.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:e4121a90823a063d717a96e0a0529c727fb31ea889369a0ee3ec00ed99bf6859", size = 222783, upload-time = "2026-01-25T12:59:33.118Z" }, + { url = "https://files.pythonhosted.org/packages/de/20/09abafb24f84b3292cc658728803416c15b79f9ee5e68d25238a895b07d9/coverage-7.13.2-cp314-cp314-win_arm64.whl", hash = "sha256:6873f0271b4a15a33e7590f338d823f6f66f91ed147a03938d7ce26efd04eee6", size = 221348, upload-time = "2026-01-25T12:59:34.939Z" }, + { url = "https://files.pythonhosted.org/packages/b6/60/a3820c7232db63be060e4019017cd3426751c2699dab3c62819cdbcea387/coverage-7.13.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f61d349f5b7cd95c34017f1927ee379bfbe9884300d74e07cf630ccf7a610c1b", size = 219950, upload-time = "2026-01-25T12:59:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/fd/37/e4ef5975fdeb86b1e56db9a82f41b032e3d93a840ebaf4064f39e770d5c5/coverage-7.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a43d34ce714f4ca674c0d90beb760eb05aad906f2c47580ccee9da8fe8bfb417", size = 220209, upload-time = "2026-01-25T12:59:38.339Z" }, + { url = "https://files.pythonhosted.org/packages/54/df/d40e091d00c51adca1e251d3b60a8b464112efa3004949e96a74d7c19a64/coverage-7.13.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bff1b04cb9d4900ce5c56c4942f047dc7efe57e2608cb7c3c8936e9970ccdbee", size = 261576, upload-time = "2026-01-25T12:59:40.446Z" }, + { url = "https://files.pythonhosted.org/packages/c5/44/5259c4bed54e3392e5c176121af9f71919d96dde853386e7730e705f3520/coverage-7.13.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6ae99e4560963ad8e163e819e5d77d413d331fd00566c1e0856aa252303552c1", size = 263704, upload-time = "2026-01-25T12:59:42.346Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/ae9f005827abcbe2c70157459ae86053971c9fa14617b63903abbdce26d9/coverage-7.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e79a8c7d461820257d9aa43716c4efc55366d7b292e46b5b37165be1d377405d", size = 266109, upload-time = "2026-01-25T12:59:44.073Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c0/8e279c1c0f5b1eaa3ad9b0fb7a5637fc0379ea7d85a781c0fe0bb3cfc2ab/coverage-7.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:060ee84f6a769d40c492711911a76811b4befb6fba50abb450371abb720f5bd6", size = 260686, upload-time = "2026-01-25T12:59:45.804Z" }, + { url = "https://files.pythonhosted.org/packages/b2/47/3a8112627e9d863e7cddd72894171c929e94491a597811725befdcd76bce/coverage-7.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bca209d001fd03ea2d978f8a4985093240a355c93078aee3f799852c23f561a", size = 263568, upload-time = "2026-01-25T12:59:47.929Z" }, + { url = "https://files.pythonhosted.org/packages/92/bc/7ea367d84afa3120afc3ce6de294fd2dcd33b51e2e7fbe4bbfd200f2cb8c/coverage-7.13.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:6b8092aa38d72f091db61ef83cb66076f18f02da3e1a75039a4f218629600e04", size = 261174, upload-time = "2026-01-25T12:59:49.717Z" }, + { url = "https://files.pythonhosted.org/packages/33/b7/f1092dcecb6637e31cc2db099581ee5c61a17647849bae6b8261a2b78430/coverage-7.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4a3158dc2dcce5200d91ec28cd315c999eebff355437d2765840555d765a6e5f", size = 260017, upload-time = "2026-01-25T12:59:51.463Z" }, + { url = "https://files.pythonhosted.org/packages/2b/cd/f3d07d4b95fbe1a2ef0958c15da614f7e4f557720132de34d2dc3aa7e911/coverage-7.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3973f353b2d70bd9796cc12f532a05945232ccae966456c8ed7034cb96bbfd6f", size = 262337, upload-time = "2026-01-25T12:59:53.407Z" }, + { url = "https://files.pythonhosted.org/packages/e0/db/b0d5b2873a07cb1e06a55d998697c0a5a540dcefbf353774c99eb3874513/coverage-7.13.2-cp314-cp314t-win32.whl", hash = "sha256:79f6506a678a59d4ded048dc72f1859ebede8ec2b9a2d509ebe161f01c2879d3", size = 222749, upload-time = "2026-01-25T12:59:56.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2f/838a5394c082ac57d85f57f6aba53093b30d9089781df72412126505716f/coverage-7.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:196bfeabdccc5a020a57d5a368c681e3a6ceb0447d153aeccc1ab4d70a5032ba", size = 223857, upload-time = "2026-01-25T12:59:58.201Z" }, + { url = "https://files.pythonhosted.org/packages/44/d4/b608243e76ead3a4298824b50922b89ef793e50069ce30316a65c1b4d7ef/coverage-7.13.2-cp314-cp314t-win_arm64.whl", hash = "sha256:69269ab58783e090bfbf5b916ab3d188126e22d6070bbfc93098fdd474ef937c", size = 221881, upload-time = "2026-01-25T13:00:00.449Z" }, + { url = "https://files.pythonhosted.org/packages/d2/db/d291e30fdf7ea617a335531e72294e0c723356d7fdde8fba00610a76bda9/coverage-7.13.2-py3-none-any.whl", hash = "sha256:40ce1ea1e25125556d8e76bd0b61500839a07944cc287ac21d5626f3e620cad5", size = 210943, upload-time = "2026-01-25T13:00:02.388Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "def-form" +source = { editable = "." } +dependencies = [ + { name = "click" }, + { name = "libcst" }, +] + +[package.dev-dependencies] +dev = [ + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "click", specifier = ">=8.2.1" }, + { name = "libcst", specifier = ">=1.8.2" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "mypy", specifier = ">=1.19.1" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-cov", specifier = ">=7.0.0" }, + { name = "ruff", specifier = ">=0.11.4,<0.12.0" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "libcst" +version = "1.8.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml", marker = "python_full_version != '3.13.*'" }, + { name = "pyyaml-ft", marker = "python_full_version == '3.13.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/cd/337df968b38d94c5aabd3e1b10630f047a2b345f6e1d4456bd9fe7417537/libcst-1.8.6.tar.gz", hash = "sha256:f729c37c9317126da9475bdd06a7208eb52fcbd180a6341648b45a56b4ba708b", size = 891354, upload-time = "2025-11-03T22:33:30.621Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/52/97d5454dee9d014821fe0c88f3dc0e83131b97dd074a4d49537056a75475/libcst-1.8.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a20c5182af04332cc94d8520792befda06d73daf2865e6dddc5161c72ea92cb9", size = 2211698, upload-time = "2025-11-03T22:31:50.117Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a4/d1205985d378164687af3247a9c8f8bdb96278b0686ac98ab951bc6d336a/libcst-1.8.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:36473e47cb199b7e6531d653ee6ffed057de1d179301e6c67f651f3af0b499d6", size = 2093104, upload-time = "2025-11-03T22:31:52.189Z" }, + { url = "https://files.pythonhosted.org/packages/9e/de/1338da681b7625b51e584922576d54f1b8db8fc7ff4dc79121afc5d4d2cd/libcst-1.8.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:06fc56335a45d61b7c1b856bfab4587b84cfe31e9d6368f60bb3c9129d900f58", size = 2237419, upload-time = "2025-11-03T22:31:53.526Z" }, + { url = "https://files.pythonhosted.org/packages/50/06/ee66f2d83b870534756e593d464d8b33b0914c224dff3a407e0f74dc04e0/libcst-1.8.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6b23d14a7fc0addd9795795763af26b185deb7c456b1e7cc4d5228e69dab5ce8", size = 2300820, upload-time = "2025-11-03T22:31:55.995Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ca/959088729de8e0eac8dd516e4fb8623d8d92bad539060fa85c9e94d418a5/libcst-1.8.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:16cfe0cfca5fd840e1fb2c30afb628b023d3085b30c3484a79b61eae9d6fe7ba", size = 2301201, upload-time = "2025-11-03T22:31:57.347Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4c/2a21a8c452436097dfe1da277f738c3517f3f728713f16d84b9a3d67ca8d/libcst-1.8.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:455f49a93aea4070132c30ebb6c07c2dea0ba6c1fde5ffde59fc45dbb9cfbe4b", size = 2408213, upload-time = "2025-11-03T22:31:59.221Z" }, + { url = "https://files.pythonhosted.org/packages/3e/26/8f7b671fad38a515bb20b038718fd2221ab658299119ac9bcec56c2ced27/libcst-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:72cca15800ffc00ba25788e4626189fe0bc5fe2a0c1cb4294bce2e4df21cc073", size = 2119189, upload-time = "2025-11-03T22:32:00.696Z" }, + { url = "https://files.pythonhosted.org/packages/5b/bf/ffb23a48e27001165cc5c81c5d9b3d6583b21b7f5449109e03a0020b060c/libcst-1.8.6-cp310-cp310-win_arm64.whl", hash = "sha256:6cad63e3a26556b020b634d25a8703b605c0e0b491426b3e6b9e12ed20f09100", size = 2001736, upload-time = "2025-11-03T22:32:02.986Z" }, + { url = "https://files.pythonhosted.org/packages/dc/15/95c2ecadc0fb4af8a7057ac2012a4c0ad5921b9ef1ace6c20006b56d3b5f/libcst-1.8.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3649a813660fbffd7bc24d3f810b1f75ac98bd40d9d6f56d1f0ee38579021073", size = 2211289, upload-time = "2025-11-03T22:32:04.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/c3/7e1107acd5ed15cf60cc07c7bb64498a33042dc4821874aea3ec4942f3cd/libcst-1.8.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0cbe17067055829607c5ba4afa46bfa4d0dd554c0b5a583546e690b7367a29b6", size = 2092927, upload-time = "2025-11-03T22:32:06.209Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ff/0d2be87f67e2841a4a37d35505e74b65991d30693295c46fc0380ace0454/libcst-1.8.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:59a7e388c57d21d63722018978a8ddba7b176e3a99bd34b9b84a576ed53f2978", size = 2237002, upload-time = "2025-11-03T22:32:07.559Z" }, + { url = "https://files.pythonhosted.org/packages/69/99/8c4a1b35c7894ccd7d33eae01ac8967122f43da41325223181ca7e4738fe/libcst-1.8.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:b6c1248cc62952a3a005792b10cdef2a4e130847be9c74f33a7d617486f7e532", size = 2301048, upload-time = "2025-11-03T22:32:08.869Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8b/d1aa811eacf936cccfb386ae0585aa530ea1221ccf528d67144e041f5915/libcst-1.8.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6421a930b028c5ef4a943b32a5a78b7f1bf15138214525a2088f11acbb7d3d64", size = 2300675, upload-time = "2025-11-03T22:32:10.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/6b/7b65cd41f25a10c1fef2389ddc5c2b2cc23dc4d648083fa3e1aa7e0eeac2/libcst-1.8.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6d8b67874f2188399a71a71731e1ba2d1a2c3173b7565d1cc7ffb32e8fbaba5b", size = 2407934, upload-time = "2025-11-03T22:32:11.856Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8b/401cfff374bb3b785adfad78f05225225767ee190997176b2a9da9ed9460/libcst-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:b0d8c364c44ae343937f474b2e492c1040df96d94530377c2f9263fb77096e4f", size = 2119247, upload-time = "2025-11-03T22:32:13.279Z" }, + { url = "https://files.pythonhosted.org/packages/f1/17/085f59eaa044b6ff6bc42148a5449df2b7f0ba567307de7782fe85c39ee2/libcst-1.8.6-cp311-cp311-win_arm64.whl", hash = "sha256:5dcaaebc835dfe5755bc85f9b186fb7e2895dda78e805e577fef1011d51d5a5c", size = 2001774, upload-time = "2025-11-03T22:32:14.647Z" }, + { url = "https://files.pythonhosted.org/packages/0c/3c/93365c17da3d42b055a8edb0e1e99f1c60c776471db6c9b7f1ddf6a44b28/libcst-1.8.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0c13d5bd3d8414a129e9dccaf0e5785108a4441e9b266e1e5e9d1f82d1b943c9", size = 2206166, upload-time = "2025-11-03T22:32:16.012Z" }, + { url = "https://files.pythonhosted.org/packages/1d/cb/7530940e6ac50c6dd6022349721074e19309eb6aa296e942ede2213c1a19/libcst-1.8.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1472eeafd67cdb22544e59cf3bfc25d23dc94058a68cf41f6654ff4fcb92e09", size = 2083726, upload-time = "2025-11-03T22:32:17.312Z" }, + { url = "https://files.pythonhosted.org/packages/1b/cf/7e5eaa8c8f2c54913160671575351d129170db757bb5e4b7faffed022271/libcst-1.8.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:089c58e75cb142ec33738a1a4ea7760a28b40c078ab2fd26b270dac7d2633a4d", size = 2235755, upload-time = "2025-11-03T22:32:18.859Z" }, + { url = "https://files.pythonhosted.org/packages/55/54/570ec2b0e9a3de0af9922e3bb1b69a5429beefbc753a7ea770a27ad308bd/libcst-1.8.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c9d7aeafb1b07d25a964b148c0dda9451efb47bbbf67756e16eeae65004b0eb5", size = 2301473, upload-time = "2025-11-03T22:32:20.499Z" }, + { url = "https://files.pythonhosted.org/packages/11/4c/163457d1717cd12181c421a4cca493454bcabd143fc7e53313bc6a4ad82a/libcst-1.8.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:207481197afd328aa91d02670c15b48d0256e676ce1ad4bafb6dc2b593cc58f1", size = 2298899, upload-time = "2025-11-03T22:32:21.765Z" }, + { url = "https://files.pythonhosted.org/packages/35/1d/317ddef3669883619ef3d3395ea583305f353ef4ad87d7a5ac1c39be38e3/libcst-1.8.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:375965f34cc6f09f5f809244d3ff9bd4f6cb6699f571121cebce53622e7e0b86", size = 2408239, upload-time = "2025-11-03T22:32:23.275Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a1/f47d8cccf74e212dd6044b9d6dbc223636508da99acff1d54786653196bc/libcst-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:da95b38693b989eaa8d32e452e8261cfa77fe5babfef1d8d2ac25af8c4aa7e6d", size = 2119660, upload-time = "2025-11-03T22:32:24.822Z" }, + { url = "https://files.pythonhosted.org/packages/19/d0/dd313bf6a7942cdf951828f07ecc1a7695263f385065edc75ef3016a3cb5/libcst-1.8.6-cp312-cp312-win_arm64.whl", hash = "sha256:bff00e1c766658adbd09a175267f8b2f7616e5ee70ce45db3d7c4ce6d9f6bec7", size = 1999824, upload-time = "2025-11-03T22:32:26.131Z" }, + { url = "https://files.pythonhosted.org/packages/90/01/723cd467ec267e712480c772aacc5aa73f82370c9665162fd12c41b0065b/libcst-1.8.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7445479ebe7d1aff0ee094ab5a1c7718e1ad78d33e3241e1a1ec65dcdbc22ffb", size = 2206386, upload-time = "2025-11-03T22:32:27.422Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/b944944f910f24c094f9b083f76f61e3985af5a376f5342a21e01e2d1a81/libcst-1.8.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4fc3fef8a2c983e7abf5d633e1884c5dd6fa0dcb8f6e32035abd3d3803a3a196", size = 2083945, upload-time = "2025-11-03T22:32:28.847Z" }, + { url = "https://files.pythonhosted.org/packages/36/a1/bd1b2b2b7f153d82301cdaddba787f4a9fc781816df6bdb295ca5f88b7cf/libcst-1.8.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:1a3a5e4ee870907aa85a4076c914ae69066715a2741b821d9bf16f9579de1105", size = 2235818, upload-time = "2025-11-03T22:32:30.504Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ab/f5433988acc3b4d188c4bb154e57837df9488cc9ab551267cdeabd3bb5e7/libcst-1.8.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6609291c41f7ad0bac570bfca5af8fea1f4a27987d30a1fa8b67fe5e67e6c78d", size = 2301289, upload-time = "2025-11-03T22:32:31.812Z" }, + { url = "https://files.pythonhosted.org/packages/5d/57/89f4ba7a6f1ac274eec9903a9e9174890d2198266eee8c00bc27eb45ecf7/libcst-1.8.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:25eaeae6567091443b5374b4c7d33a33636a2d58f5eda02135e96fc6c8807786", size = 2299230, upload-time = "2025-11-03T22:32:33.242Z" }, + { url = "https://files.pythonhosted.org/packages/f2/36/0aa693bc24cce163a942df49d36bf47a7ed614a0cd5598eee2623bc31913/libcst-1.8.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04030ea4d39d69a65873b1d4d877def1c3951a7ada1824242539e399b8763d30", size = 2408519, upload-time = "2025-11-03T22:32:34.678Z" }, + { url = "https://files.pythonhosted.org/packages/db/18/6dd055b5f15afa640fb3304b2ee9df8b7f72e79513814dbd0a78638f4a0e/libcst-1.8.6-cp313-cp313-win_amd64.whl", hash = "sha256:8066f1b70f21a2961e96bedf48649f27dfd5ea68be5cd1bed3742b047f14acde", size = 2119853, upload-time = "2025-11-03T22:32:36.287Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ed/5ddb2a22f0b0abdd6dcffa40621ada1feaf252a15e5b2733a0a85dfd0429/libcst-1.8.6-cp313-cp313-win_arm64.whl", hash = "sha256:c188d06b583900e662cd791a3f962a8c96d3dfc9b36ea315be39e0a4c4792ebf", size = 1999808, upload-time = "2025-11-03T22:32:38.1Z" }, + { url = "https://files.pythonhosted.org/packages/25/d3/72b2de2c40b97e1ef4a1a1db4e5e52163fc7e7740ffef3846d30bc0096b5/libcst-1.8.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c41c76e034a1094afed7057023b1d8967f968782433f7299cd170eaa01ec033e", size = 2190553, upload-time = "2025-11-03T22:32:39.819Z" }, + { url = "https://files.pythonhosted.org/packages/0d/20/983b7b210ccc3ad94a82db54230e92599c4a11b9cfc7ce3bc97c1d2df75c/libcst-1.8.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5432e785322aba3170352f6e72b32bea58d28abd141ac37cc9b0bf6b7c778f58", size = 2074717, upload-time = "2025-11-03T22:32:41.373Z" }, + { url = "https://files.pythonhosted.org/packages/13/f2/9e01678fedc772e09672ed99930de7355757035780d65d59266fcee212b8/libcst-1.8.6-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:85b7025795b796dea5284d290ff69de5089fc8e989b25d6f6f15b6800be7167f", size = 2225834, upload-time = "2025-11-03T22:32:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/4a/0d/7bed847b5c8c365e9f1953da274edc87577042bee5a5af21fba63276e756/libcst-1.8.6-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:536567441182a62fb706e7aa954aca034827b19746832205953b2c725d254a93", size = 2287107, upload-time = "2025-11-03T22:32:44.549Z" }, + { url = "https://files.pythonhosted.org/packages/02/f0/7e51fa84ade26c518bfbe7e2e4758b56d86a114c72d60309ac0d350426c4/libcst-1.8.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f04d3672bde1704f383a19e8f8331521abdbc1ed13abb349325a02ac56e5012", size = 2288672, upload-time = "2025-11-03T22:32:45.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cd/15762659a3f5799d36aab1bc2b7e732672722e249d7800e3c5f943b41250/libcst-1.8.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f04febcd70e1e67917be7de513c8d4749d2e09206798558d7fe632134426ea4", size = 2392661, upload-time = "2025-11-03T22:32:47.232Z" }, + { url = "https://files.pythonhosted.org/packages/e4/6b/b7f9246c323910fcbe021241500f82e357521495dcfe419004dbb272c7cb/libcst-1.8.6-cp313-cp313t-win_amd64.whl", hash = "sha256:1dc3b897c8b0f7323412da3f4ad12b16b909150efc42238e19cbf19b561cc330", size = 2105068, upload-time = "2025-11-03T22:32:49.145Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0b/4fd40607bc4807ec2b93b054594373d7fa3d31bb983789901afcb9bcebe9/libcst-1.8.6-cp313-cp313t-win_arm64.whl", hash = "sha256:44f38139fa95e488db0f8976f9c7ca39a64d6bc09f2eceef260aa1f6da6a2e42", size = 1985181, upload-time = "2025-11-03T22:32:50.597Z" }, + { url = "https://files.pythonhosted.org/packages/3a/60/4105441989e321f7ad0fd28ffccb83eb6aac0b7cfb0366dab855dcccfbe5/libcst-1.8.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:b188e626ce61de5ad1f95161b8557beb39253de4ec74fc9b1f25593324a0279c", size = 2204202, upload-time = "2025-11-03T22:32:52.311Z" }, + { url = "https://files.pythonhosted.org/packages/67/2f/51a6f285c3a183e50cfe5269d4a533c21625aac2c8de5cdf2d41f079320d/libcst-1.8.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:87e74f7d7dfcba9efa91127081e22331d7c42515f0a0ac6e81d4cf2c3ed14661", size = 2083581, upload-time = "2025-11-03T22:32:54.269Z" }, + { url = "https://files.pythonhosted.org/packages/2f/64/921b1c19b638860af76cdb28bc81d430056592910b9478eea49e31a7f47a/libcst-1.8.6-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:3a926a4b42015ee24ddfc8ae940c97bd99483d286b315b3ce82f3bafd9f53474", size = 2236495, upload-time = "2025-11-03T22:32:55.723Z" }, + { url = "https://files.pythonhosted.org/packages/12/a8/b00592f9bede618cbb3df6ffe802fc65f1d1c03d48a10d353b108057d09c/libcst-1.8.6-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:3f4fbb7f569e69fd9e89d9d9caa57ca42c577c28ed05062f96a8c207594e75b8", size = 2301466, upload-time = "2025-11-03T22:32:57.337Z" }, + { url = "https://files.pythonhosted.org/packages/af/df/790d9002f31580fefd0aec2f373a0f5da99070e04c5e8b1c995d0104f303/libcst-1.8.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:08bd63a8ce674be431260649e70fca1d43f1554f1591eac657f403ff8ef82c7a", size = 2300264, upload-time = "2025-11-03T22:32:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/21/de/dc3f10e65bab461be5de57850d2910a02c24c3ddb0da28f0e6e4133c3487/libcst-1.8.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e00e275d4ba95d4963431ea3e409aa407566a74ee2bf309a402f84fc744abe47", size = 2408572, upload-time = "2025-11-03T22:33:00.552Z" }, + { url = "https://files.pythonhosted.org/packages/20/3b/35645157a7590891038b077db170d6dd04335cd2e82a63bdaa78c3297dfe/libcst-1.8.6-cp314-cp314-win_amd64.whl", hash = "sha256:fea5c7fa26556eedf277d4f72779c5ede45ac3018650721edd77fd37ccd4a2d4", size = 2193917, upload-time = "2025-11-03T22:33:02.354Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a2/1034a9ba7d3e82f2c2afaad84ba5180f601aed676d92b76325797ad60951/libcst-1.8.6-cp314-cp314-win_arm64.whl", hash = "sha256:bb9b4077bdf8857b2483879cbbf70f1073bc255b057ec5aac8a70d901bb838e9", size = 2078748, upload-time = "2025-11-03T22:33:03.707Z" }, + { url = "https://files.pythonhosted.org/packages/95/a1/30bc61e8719f721a5562f77695e6154e9092d1bdf467aa35d0806dcd6cea/libcst-1.8.6-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:55ec021a296960c92e5a33b8d93e8ad4182b0eab657021f45262510a58223de1", size = 2188980, upload-time = "2025-11-03T22:33:05.152Z" }, + { url = "https://files.pythonhosted.org/packages/2c/14/c660204532407c5628e3b615015a902ed2d0b884b77714a6bdbe73350910/libcst-1.8.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ba9ab2b012fbd53b36cafd8f4440a6b60e7e487cd8b87428e57336b7f38409a4", size = 2074828, upload-time = "2025-11-03T22:33:06.864Z" }, + { url = "https://files.pythonhosted.org/packages/82/e2/c497c354943dff644749f177ee9737b09ed811b8fc842b05709a40fe0d1b/libcst-1.8.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c0a0cc80aebd8aa15609dd4d330611cbc05e9b4216bcaeabba7189f99ef07c28", size = 2225568, upload-time = "2025-11-03T22:33:08.354Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/45999676d07bd6d0eefa28109b4f97124db114e92f9e108de42ba46a8028/libcst-1.8.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:42a4f68121e2e9c29f49c97f6154e8527cd31021809cc4a941c7270aa64f41aa", size = 2286523, upload-time = "2025-11-03T22:33:10.206Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6c/517d8bf57d9f811862f4125358caaf8cd3320a01291b3af08f7b50719db4/libcst-1.8.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a434c521fadaf9680788b50d5c21f4048fa85ed19d7d70bd40549fbaeeecab1", size = 2288044, upload-time = "2025-11-03T22:33:11.628Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/24d7d49478ffb61207f229239879845da40a374965874f5ee60f96b02ddb/libcst-1.8.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6a65f844d813ab4ef351443badffa0ae358f98821561d19e18b3190f59e71996", size = 2392605, upload-time = "2025-11-03T22:33:12.962Z" }, + { url = "https://files.pythonhosted.org/packages/39/c3/829092ead738b71e96a4e96896c96f276976e5a8a58b4473ed813d7c962b/libcst-1.8.6-cp314-cp314t-win_amd64.whl", hash = "sha256:bdb14bc4d4d83a57062fed2c5da93ecb426ff65b0dc02ddf3481040f5f074a82", size = 2181581, upload-time = "2025-11-03T22:33:14.514Z" }, + { url = "https://files.pythonhosted.org/packages/98/6d/5d6a790a02eb0d9d36c4aed4f41b277497e6178900b2fa29c35353aa45ed/libcst-1.8.6-cp314-cp314t-win_arm64.whl", hash = "sha256:819c8081e2948635cab60c603e1bbdceccdfe19104a242530ad38a36222cb88f", size = 2065000, upload-time = "2025-11-03T22:33:16.257Z" }, +] + +[[package]] +name = "librt" +version = "0.7.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/24/5f3646ff414285e0f7708fa4e946b9bf538345a41d1c375c439467721a5e/librt-0.7.8.tar.gz", hash = "sha256:1a4ede613941d9c3470b0368be851df6bb78ab218635512d0370b27a277a0862", size = 148323, upload-time = "2026-01-14T12:56:16.876Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/13/57b06758a13550c5f09563893b004f98e9537ee6ec67b7df85c3571c8832/librt-0.7.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b45306a1fc5f53c9330fbee134d8b3227fe5da2ab09813b892790400aa49352d", size = 56521, upload-time = "2026-01-14T12:54:40.066Z" }, + { url = "https://files.pythonhosted.org/packages/c2/24/bbea34d1452a10612fb45ac8356f95351ba40c2517e429602160a49d1fd0/librt-0.7.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:864c4b7083eeee250ed55135d2127b260d7eb4b5e953a9e5df09c852e327961b", size = 58456, upload-time = "2026-01-14T12:54:41.471Z" }, + { url = "https://files.pythonhosted.org/packages/04/72/a168808f92253ec3a810beb1eceebc465701197dbc7e865a1c9ceb3c22c7/librt-0.7.8-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6938cc2de153bc927ed8d71c7d2f2ae01b4e96359126c602721340eb7ce1a92d", size = 164392, upload-time = "2026-01-14T12:54:42.843Z" }, + { url = "https://files.pythonhosted.org/packages/14/5c/4c0d406f1b02735c2e7af8ff1ff03a6577b1369b91aa934a9fa2cc42c7ce/librt-0.7.8-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66daa6ac5de4288a5bbfbe55b4caa7bf0cd26b3269c7a476ffe8ce45f837f87d", size = 172959, upload-time = "2026-01-14T12:54:44.602Z" }, + { url = "https://files.pythonhosted.org/packages/82/5f/3e85351c523f73ad8d938989e9a58c7f59fb9c17f761b9981b43f0025ce7/librt-0.7.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4864045f49dc9c974dadb942ac56a74cd0479a2aafa51ce272c490a82322ea3c", size = 186717, upload-time = "2026-01-14T12:54:45.986Z" }, + { url = "https://files.pythonhosted.org/packages/08/f8/18bfe092e402d00fe00d33aa1e01dda1bd583ca100b393b4373847eade6d/librt-0.7.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a36515b1328dc5b3ffce79fe204985ca8572525452eacabee2166f44bb387b2c", size = 184585, upload-time = "2026-01-14T12:54:47.139Z" }, + { url = "https://files.pythonhosted.org/packages/4e/fc/f43972ff56fd790a9fa55028a52ccea1875100edbb856b705bd393b601e3/librt-0.7.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b7e7f140c5169798f90b80d6e607ed2ba5059784968a004107c88ad61fb3641d", size = 180497, upload-time = "2026-01-14T12:54:48.946Z" }, + { url = "https://files.pythonhosted.org/packages/e1/3a/25e36030315a410d3ad0b7d0f19f5f188e88d1613d7d3fd8150523ea1093/librt-0.7.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff71447cb778a4f772ddc4ce360e6ba9c95527ed84a52096bd1bbf9fee2ec7c0", size = 200052, upload-time = "2026-01-14T12:54:50.382Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b8/f3a5a1931ae2a6ad92bf6893b9ef44325b88641d58723529e2c2935e8abe/librt-0.7.8-cp310-cp310-win32.whl", hash = "sha256:047164e5f68b7a8ebdf9fae91a3c2161d3192418aadd61ddd3a86a56cbe3dc85", size = 43477, upload-time = "2026-01-14T12:54:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/fe/91/c4202779366bc19f871b4ad25db10fcfa1e313c7893feb942f32668e8597/librt-0.7.8-cp310-cp310-win_amd64.whl", hash = "sha256:d6f254d096d84156a46a84861183c183d30734e52383602443292644d895047c", size = 49806, upload-time = "2026-01-14T12:54:53.149Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a3/87ea9c1049f2c781177496ebee29430e4631f439b8553a4969c88747d5d8/librt-0.7.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ff3e9c11aa260c31493d4b3197d1e28dd07768594a4f92bec4506849d736248f", size = 56507, upload-time = "2026-01-14T12:54:54.156Z" }, + { url = "https://files.pythonhosted.org/packages/5e/4a/23bcef149f37f771ad30203d561fcfd45b02bc54947b91f7a9ac34815747/librt-0.7.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddb52499d0b3ed4aa88746aaf6f36a08314677d5c346234c3987ddc506404eac", size = 58455, upload-time = "2026-01-14T12:54:55.978Z" }, + { url = "https://files.pythonhosted.org/packages/22/6e/46eb9b85c1b9761e0f42b6e6311e1cc544843ac897457062b9d5d0b21df4/librt-0.7.8-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e9c0afebbe6ce177ae8edba0c7c4d626f2a0fc12c33bb993d163817c41a7a05c", size = 164956, upload-time = "2026-01-14T12:54:57.311Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3f/aa7c7f6829fb83989feb7ba9aa11c662b34b4bd4bd5b262f2876ba3db58d/librt-0.7.8-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:631599598e2c76ded400c0a8722dec09217c89ff64dc54b060f598ed68e7d2a8", size = 174364, upload-time = "2026-01-14T12:54:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/3f/2d/d57d154b40b11f2cb851c4df0d4c4456bacd9b1ccc4ecb593ddec56c1a8b/librt-0.7.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c1ba843ae20db09b9d5c80475376168feb2640ce91cd9906414f23cc267a1ff", size = 188034, upload-time = "2026-01-14T12:55:00.141Z" }, + { url = "https://files.pythonhosted.org/packages/59/f9/36c4dad00925c16cd69d744b87f7001792691857d3b79187e7a673e812fb/librt-0.7.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b5b007bb22ea4b255d3ee39dfd06d12534de2fcc3438567d9f48cdaf67ae1ae3", size = 186295, upload-time = "2026-01-14T12:55:01.303Z" }, + { url = "https://files.pythonhosted.org/packages/23/9b/8a9889d3df5efb67695a67785028ccd58e661c3018237b73ad081691d0cb/librt-0.7.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:dbd79caaf77a3f590cbe32dc2447f718772d6eea59656a7dcb9311161b10fa75", size = 181470, upload-time = "2026-01-14T12:55:02.492Z" }, + { url = "https://files.pythonhosted.org/packages/43/64/54d6ef11afca01fef8af78c230726a9394759f2addfbf7afc5e3cc032a45/librt-0.7.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:87808a8d1e0bd62a01cafc41f0fd6818b5a5d0ca0d8a55326a81643cdda8f873", size = 201713, upload-time = "2026-01-14T12:55:03.919Z" }, + { url = "https://files.pythonhosted.org/packages/2d/29/73e7ed2991330b28919387656f54109139b49e19cd72902f466bd44415fd/librt-0.7.8-cp311-cp311-win32.whl", hash = "sha256:31724b93baa91512bd0a376e7cf0b59d8b631ee17923b1218a65456fa9bda2e7", size = 43803, upload-time = "2026-01-14T12:55:04.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/de/66766ff48ed02b4d78deea30392ae200bcbd99ae61ba2418b49fd50a4831/librt-0.7.8-cp311-cp311-win_amd64.whl", hash = "sha256:978e8b5f13e52cf23a9e80f3286d7546baa70bc4ef35b51d97a709d0b28e537c", size = 50080, upload-time = "2026-01-14T12:55:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e3/33450438ff3a8c581d4ed7f798a70b07c3206d298cf0b87d3806e72e3ed8/librt-0.7.8-cp311-cp311-win_arm64.whl", hash = "sha256:20e3946863d872f7cabf7f77c6c9d370b8b3d74333d3a32471c50d3a86c0a232", size = 43383, upload-time = "2026-01-14T12:55:07.49Z" }, + { url = "https://files.pythonhosted.org/packages/56/04/79d8fcb43cae376c7adbab7b2b9f65e48432c9eced62ac96703bcc16e09b/librt-0.7.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b6943885b2d49c48d0cff23b16be830ba46b0152d98f62de49e735c6e655a63", size = 57472, upload-time = "2026-01-14T12:55:08.528Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ba/60b96e93043d3d659da91752689023a73981336446ae82078cddf706249e/librt-0.7.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:46ef1f4b9b6cc364b11eea0ecc0897314447a66029ee1e55859acb3dd8757c93", size = 58986, upload-time = "2026-01-14T12:55:09.466Z" }, + { url = "https://files.pythonhosted.org/packages/7c/26/5215e4cdcc26e7be7eee21955a7e13cbf1f6d7d7311461a6014544596fac/librt-0.7.8-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:907ad09cfab21e3c86e8f1f87858f7049d1097f77196959c033612f532b4e592", size = 168422, upload-time = "2026-01-14T12:55:10.499Z" }, + { url = "https://files.pythonhosted.org/packages/0f/84/e8d1bc86fa0159bfc24f3d798d92cafd3897e84c7fea7fe61b3220915d76/librt-0.7.8-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2991b6c3775383752b3ca0204842743256f3ad3deeb1d0adc227d56b78a9a850", size = 177478, upload-time = "2026-01-14T12:55:11.577Z" }, + { url = "https://files.pythonhosted.org/packages/57/11/d0268c4b94717a18aa91df1100e767b010f87b7ae444dafaa5a2d80f33a6/librt-0.7.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03679b9856932b8c8f674e87aa3c55ea11c9274301f76ae8dc4d281bda55cf62", size = 192439, upload-time = "2026-01-14T12:55:12.7Z" }, + { url = "https://files.pythonhosted.org/packages/8d/56/1e8e833b95fe684f80f8894ae4d8b7d36acc9203e60478fcae599120a975/librt-0.7.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3968762fec1b2ad34ce57458b6de25dbb4142713e9ca6279a0d352fa4e9f452b", size = 191483, upload-time = "2026-01-14T12:55:13.838Z" }, + { url = "https://files.pythonhosted.org/packages/17/48/f11cf28a2cb6c31f282009e2208312aa84a5ee2732859f7856ee306176d5/librt-0.7.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bb7a7807523a31f03061288cc4ffc065d684c39db7644c676b47d89553c0d714", size = 185376, upload-time = "2026-01-14T12:55:15.017Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6a/d7c116c6da561b9155b184354a60a3d5cdbf08fc7f3678d09c95679d13d9/librt-0.7.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad64a14b1e56e702e19b24aae108f18ad1bf7777f3af5fcd39f87d0c5a814449", size = 206234, upload-time = "2026-01-14T12:55:16.571Z" }, + { url = "https://files.pythonhosted.org/packages/61/de/1975200bb0285fc921c5981d9978ce6ce11ae6d797df815add94a5a848a3/librt-0.7.8-cp312-cp312-win32.whl", hash = "sha256:0241a6ed65e6666236ea78203a73d800dbed896cf12ae25d026d75dc1fcd1dac", size = 44057, upload-time = "2026-01-14T12:55:18.077Z" }, + { url = "https://files.pythonhosted.org/packages/8e/cd/724f2d0b3461426730d4877754b65d39f06a41ac9d0a92d5c6840f72b9ae/librt-0.7.8-cp312-cp312-win_amd64.whl", hash = "sha256:6db5faf064b5bab9675c32a873436b31e01d66ca6984c6f7f92621656033a708", size = 50293, upload-time = "2026-01-14T12:55:19.179Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cf/7e899acd9ee5727ad8160fdcc9994954e79fab371c66535c60e13b968ffc/librt-0.7.8-cp312-cp312-win_arm64.whl", hash = "sha256:57175aa93f804d2c08d2edb7213e09276bd49097611aefc37e3fa38d1fb99ad0", size = 43574, upload-time = "2026-01-14T12:55:20.185Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fe/b1f9de2829cf7fc7649c1dcd202cfd873837c5cc2fc9e526b0e7f716c3d2/librt-0.7.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4c3995abbbb60b3c129490fa985dfe6cac11d88fc3c36eeb4fb1449efbbb04fc", size = 57500, upload-time = "2026-01-14T12:55:21.219Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/4a60fbe2e53b825f5d9a77325071d61cd8af8506255067bf0c8527530745/librt-0.7.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44e0c2cbc9bebd074cf2cdbe472ca185e824be4e74b1c63a8e934cea674bebf2", size = 59019, upload-time = "2026-01-14T12:55:22.256Z" }, + { url = "https://files.pythonhosted.org/packages/6a/37/61ff80341ba5159afa524445f2d984c30e2821f31f7c73cf166dcafa5564/librt-0.7.8-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4d2f1e492cae964b3463a03dc77a7fe8742f7855d7258c7643f0ee32b6651dd3", size = 169015, upload-time = "2026-01-14T12:55:23.24Z" }, + { url = "https://files.pythonhosted.org/packages/1c/86/13d4f2d6a93f181ebf2fc953868826653ede494559da8268023fe567fca3/librt-0.7.8-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:451e7ffcef8f785831fdb791bd69211f47e95dc4c6ddff68e589058806f044c6", size = 178161, upload-time = "2026-01-14T12:55:24.826Z" }, + { url = "https://files.pythonhosted.org/packages/88/26/e24ef01305954fc4d771f1f09f3dd682f9eb610e1bec188ffb719374d26e/librt-0.7.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3469e1af9f1380e093ae06bedcbdd11e407ac0b303a56bbe9afb1d6824d4982d", size = 193015, upload-time = "2026-01-14T12:55:26.04Z" }, + { url = "https://files.pythonhosted.org/packages/88/a0/92b6bd060e720d7a31ed474d046a69bd55334ec05e9c446d228c4b806ae3/librt-0.7.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f11b300027ce19a34f6d24ebb0a25fd0e24a9d53353225a5c1e6cadbf2916b2e", size = 192038, upload-time = "2026-01-14T12:55:27.208Z" }, + { url = "https://files.pythonhosted.org/packages/06/bb/6f4c650253704279c3a214dad188101d1b5ea23be0606628bc6739456624/librt-0.7.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4adc73614f0d3c97874f02f2c7fd2a27854e7e24ad532ea6b965459c5b757eca", size = 186006, upload-time = "2026-01-14T12:55:28.594Z" }, + { url = "https://files.pythonhosted.org/packages/dc/00/1c409618248d43240cadf45f3efb866837fa77e9a12a71481912135eb481/librt-0.7.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60c299e555f87e4c01b2eca085dfccda1dde87f5a604bb45c2906b8305819a93", size = 206888, upload-time = "2026-01-14T12:55:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/d9/83/b2cfe8e76ff5c1c77f8a53da3d5de62d04b5ebf7cf913e37f8bca43b5d07/librt-0.7.8-cp313-cp313-win32.whl", hash = "sha256:b09c52ed43a461994716082ee7d87618096851319bf695d57ec123f2ab708951", size = 44126, upload-time = "2026-01-14T12:55:31.44Z" }, + { url = "https://files.pythonhosted.org/packages/a9/0b/c59d45de56a51bd2d3a401fc63449c0ac163e4ef7f523ea8b0c0dee86ec5/librt-0.7.8-cp313-cp313-win_amd64.whl", hash = "sha256:f8f4a901a3fa28969d6e4519deceab56c55a09d691ea7b12ca830e2fa3461e34", size = 50262, upload-time = "2026-01-14T12:55:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, + { url = "https://files.pythonhosted.org/packages/1a/73/fa8814c6ce2d49c3827829cadaa1589b0bf4391660bd4510899393a23ebc/librt-0.7.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:be927c3c94c74b05128089a955fba86501c3b544d1d300282cc1b4bd370cb418", size = 57049, upload-time = "2026-01-14T12:55:35.056Z" }, + { url = "https://files.pythonhosted.org/packages/53/fe/f6c70956da23ea235fd2e3cc16f4f0b4ebdfd72252b02d1164dd58b4e6c3/librt-0.7.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7b0803e9008c62a7ef79058233db7ff6f37a9933b8f2573c05b07ddafa226611", size = 58689, upload-time = "2026-01-14T12:55:36.078Z" }, + { url = "https://files.pythonhosted.org/packages/1f/4d/7a2481444ac5fba63050d9abe823e6bc16896f575bfc9c1e5068d516cdce/librt-0.7.8-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:79feb4d00b2a4e0e05c9c56df707934f41fcb5fe53fd9efb7549068d0495b758", size = 166808, upload-time = "2026-01-14T12:55:37.595Z" }, + { url = "https://files.pythonhosted.org/packages/ac/3c/10901d9e18639f8953f57c8986796cfbf4c1c514844a41c9197cf87cb707/librt-0.7.8-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9122094e3f24aa759c38f46bd8863433820654927370250f460ae75488b66ea", size = 175614, upload-time = "2026-01-14T12:55:38.756Z" }, + { url = "https://files.pythonhosted.org/packages/db/01/5cbdde0951a5090a80e5ba44e6357d375048123c572a23eecfb9326993a7/librt-0.7.8-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e03bea66af33c95ce3addf87a9bf1fcad8d33e757bc479957ddbc0e4f7207ac", size = 189955, upload-time = "2026-01-14T12:55:39.939Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b4/e80528d2f4b7eaf1d437fcbd6fc6ba4cbeb3e2a0cb9ed5a79f47c7318706/librt-0.7.8-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f1ade7f31675db00b514b98f9ab9a7698c7282dad4be7492589109471852d398", size = 189370, upload-time = "2026-01-14T12:55:41.057Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ab/938368f8ce31a9787ecd4becb1e795954782e4312095daf8fd22420227c8/librt-0.7.8-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a14229ac62adcf1b90a15992f1ab9c69ae8b99ffb23cb64a90878a6e8a2f5b81", size = 183224, upload-time = "2026-01-14T12:55:42.328Z" }, + { url = "https://files.pythonhosted.org/packages/3c/10/559c310e7a6e4014ac44867d359ef8238465fb499e7eb31b6bfe3e3f86f5/librt-0.7.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5bcaaf624fd24e6a0cb14beac37677f90793a96864c67c064a91458611446e83", size = 203541, upload-time = "2026-01-14T12:55:43.501Z" }, + { url = "https://files.pythonhosted.org/packages/f8/db/a0db7acdb6290c215f343835c6efda5b491bb05c3ddc675af558f50fdba3/librt-0.7.8-cp314-cp314-win32.whl", hash = "sha256:7aa7d5457b6c542ecaed79cec4ad98534373c9757383973e638ccced0f11f46d", size = 40657, upload-time = "2026-01-14T12:55:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/72/e0/4f9bdc2a98a798511e81edcd6b54fe82767a715e05d1921115ac70717f6f/librt-0.7.8-cp314-cp314-win_amd64.whl", hash = "sha256:3d1322800771bee4a91f3b4bd4e49abc7d35e65166821086e5afd1e6c0d9be44", size = 46835, upload-time = "2026-01-14T12:55:45.655Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3d/59c6402e3dec2719655a41ad027a7371f8e2334aa794ed11533ad5f34969/librt-0.7.8-cp314-cp314-win_arm64.whl", hash = "sha256:5363427bc6a8c3b1719f8f3845ea53553d301382928a86e8fab7984426949bce", size = 39885, upload-time = "2026-01-14T12:55:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/4e/9c/2481d80950b83085fb14ba3c595db56330d21bbc7d88a19f20165f3538db/librt-0.7.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ca916919793a77e4a98d4a1701e345d337ce53be4a16620f063191f7322ac80f", size = 59161, upload-time = "2026-01-14T12:55:48.45Z" }, + { url = "https://files.pythonhosted.org/packages/96/79/108df2cfc4e672336765d54e3ff887294c1cc36ea4335c73588875775527/librt-0.7.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:54feb7b4f2f6706bb82325e836a01be805770443e2400f706e824e91f6441dde", size = 61008, upload-time = "2026-01-14T12:55:49.527Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/30179898f9994a5637459d6e169b6abdc982012c0a4b2d4c26f50c06f911/librt-0.7.8-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:39a4c76fee41007070f872b648cc2f711f9abf9a13d0c7162478043377b52c8e", size = 187199, upload-time = "2026-01-14T12:55:50.587Z" }, + { url = "https://files.pythonhosted.org/packages/b4/da/f7563db55cebdc884f518ba3791ad033becc25ff68eb70902b1747dc0d70/librt-0.7.8-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac9c8a458245c7de80bc1b9765b177055efff5803f08e548dd4bb9ab9a8d789b", size = 198317, upload-time = "2026-01-14T12:55:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/b3/6c/4289acf076ad371471fa86718c30ae353e690d3de6167f7db36f429272f1/librt-0.7.8-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b67aa7eff150f075fda09d11f6bfb26edffd300f6ab1666759547581e8f666", size = 210334, upload-time = "2026-01-14T12:55:53.682Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7f/377521ac25b78ac0a5ff44127a0360ee6d5ddd3ce7327949876a30533daa/librt-0.7.8-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:535929b6eff670c593c34ff435d5440c3096f20fa72d63444608a5aef64dd581", size = 211031, upload-time = "2026-01-14T12:55:54.827Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b1/e1e96c3e20b23d00cf90f4aad48f0deb4cdfec2f0ed8380d0d85acf98bbf/librt-0.7.8-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:63937bd0f4d1cb56653dc7ae900d6c52c41f0015e25aaf9902481ee79943b33a", size = 204581, upload-time = "2026-01-14T12:55:56.811Z" }, + { url = "https://files.pythonhosted.org/packages/43/71/0f5d010e92ed9747e14bef35e91b6580533510f1e36a8a09eb79ee70b2f0/librt-0.7.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf243da9e42d914036fd362ac3fa77d80a41cadcd11ad789b1b5eec4daaf67ca", size = 224731, upload-time = "2026-01-14T12:55:58.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f0/07fb6ab5c39a4ca9af3e37554f9d42f25c464829254d72e4ebbd81da351c/librt-0.7.8-cp314-cp314t-win32.whl", hash = "sha256:171ca3a0a06c643bd0a2f62a8944e1902c94aa8e5da4db1ea9a8daf872685365", size = 41173, upload-time = "2026-01-14T12:55:59.315Z" }, + { url = "https://files.pythonhosted.org/packages/24/d4/7e4be20993dc6a782639625bd2f97f3c66125c7aa80c82426956811cfccf/librt-0.7.8-cp314-cp314t-win_amd64.whl", hash = "sha256:445b7304145e24c60288a2f172b5ce2ca35c0f81605f5299f3fa567e189d2e32", size = 47668, upload-time = "2026-01-14T12:56:00.261Z" }, + { url = "https://files.pythonhosted.org/packages/fc/85/69f92b2a7b3c0f88ffe107c86b952b397004b5b8ea5a81da3d9c04c04422/librt-0.7.8-cp314-cp314t-win_arm64.whl", hash = "sha256:8766ece9de08527deabcd7cb1b4f1a967a385d26e33e536d6d8913db6ef74f06", size = 40550, upload-time = "2026-01-14T12:56:01.542Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +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/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "pyyaml-ft" +version = "8.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/eb/5a0d575de784f9a1f94e2b1288c6886f13f34185e13117ed530f32b6f8a8/pyyaml_ft-8.0.0.tar.gz", hash = "sha256:0c947dce03954c7b5d38869ed4878b2e6ff1d44b08a0d84dc83fdad205ae39ab", size = 141057, upload-time = "2025-06-10T15:32:15.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/ba/a067369fe61a2e57fb38732562927d5bae088c73cb9bb5438736a9555b29/pyyaml_ft-8.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c1306282bc958bfda31237f900eb52c9bedf9b93a11f82e1aab004c9a5657a6", size = 187027, upload-time = "2025-06-10T15:31:48.722Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c5/a3d2020ce5ccfc6aede0d45bcb870298652ac0cf199f67714d250e0cdf39/pyyaml_ft-8.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30c5f1751625786c19de751e3130fc345ebcba6a86f6bddd6e1285342f4bbb69", size = 176146, upload-time = "2025-06-10T15:31:50.584Z" }, + { url = "https://files.pythonhosted.org/packages/e3/bb/23a9739291086ca0d3189eac7cd92b4d00e9fdc77d722ab610c35f9a82ba/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fa992481155ddda2e303fcc74c79c05eddcdbc907b888d3d9ce3ff3e2adcfb0", size = 746792, upload-time = "2025-06-10T15:31:52.304Z" }, + { url = "https://files.pythonhosted.org/packages/5f/c2/e8825f4ff725b7e560d62a3609e31d735318068e1079539ebfde397ea03e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cec6c92b4207004b62dfad1f0be321c9f04725e0f271c16247d8b39c3bf3ea42", size = 786772, upload-time = "2025-06-10T15:31:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/35/be/58a4dcae8854f2fdca9b28d9495298fd5571a50d8430b1c3033ec95d2d0e/pyyaml_ft-8.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06237267dbcab70d4c0e9436d8f719f04a51123f0ca2694c00dd4b68c338e40b", size = 778723, upload-time = "2025-06-10T15:31:56.093Z" }, + { url = "https://files.pythonhosted.org/packages/86/ed/fed0da92b5d5d7340a082e3802d84c6dc9d5fa142954404c41a544c1cb92/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8a7f332bc565817644cdb38ffe4739e44c3e18c55793f75dddb87630f03fc254", size = 758478, upload-time = "2025-06-10T15:31:58.314Z" }, + { url = "https://files.pythonhosted.org/packages/f0/69/ac02afe286275980ecb2dcdc0156617389b7e0c0a3fcdedf155c67be2b80/pyyaml_ft-8.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d10175a746be65f6feb86224df5d6bc5c049ebf52b89a88cf1cd78af5a367a8", size = 799159, upload-time = "2025-06-10T15:31:59.675Z" }, + { url = "https://files.pythonhosted.org/packages/4e/ac/c492a9da2e39abdff4c3094ec54acac9747743f36428281fb186a03fab76/pyyaml_ft-8.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:58e1015098cf8d8aec82f360789c16283b88ca670fe4275ef6c48c5e30b22a96", size = 158779, upload-time = "2025-06-10T15:32:01.029Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9b/41998df3298960d7c67653669f37710fa2d568a5fc933ea24a6df60acaf6/pyyaml_ft-8.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e64fa5f3e2ceb790d50602b2fd4ec37abbd760a8c778e46354df647e7c5a4ebb", size = 191331, upload-time = "2025-06-10T15:32:02.602Z" }, + { url = "https://files.pythonhosted.org/packages/0f/16/2710c252ee04cbd74d9562ebba709e5a284faeb8ada88fcda548c9191b47/pyyaml_ft-8.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8d445bf6ea16bb93c37b42fdacfb2f94c8e92a79ba9e12768c96ecde867046d1", size = 182879, upload-time = "2025-06-10T15:32:04.466Z" }, + { url = "https://files.pythonhosted.org/packages/9a/40/ae8163519d937fa7bfa457b6f78439cc6831a7c2b170e4f612f7eda71815/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c56bb46b4fda34cbb92a9446a841da3982cdde6ea13de3fbd80db7eeeab8b49", size = 811277, upload-time = "2025-06-10T15:32:06.214Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/28d82dbff7f87b96f0eeac79b7d972a96b4980c1e445eb6a857ba91eda00/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dab0abb46eb1780da486f022dce034b952c8ae40753627b27a626d803926483b", size = 831650, upload-time = "2025-06-10T15:32:08.076Z" }, + { url = "https://files.pythonhosted.org/packages/e8/df/161c4566facac7d75a9e182295c223060373d4116dead9cc53a265de60b9/pyyaml_ft-8.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd48d639cab5ca50ad957b6dd632c7dd3ac02a1abe0e8196a3c24a52f5db3f7a", size = 815755, upload-time = "2025-06-10T15:32:09.435Z" }, + { url = "https://files.pythonhosted.org/packages/05/10/f42c48fa5153204f42eaa945e8d1fd7c10d6296841dcb2447bf7da1be5c4/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:052561b89d5b2a8e1289f326d060e794c21fa068aa11255fe71d65baf18a632e", size = 810403, upload-time = "2025-06-10T15:32:11.051Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d2/e369064aa51009eb9245399fd8ad2c562bd0bcd392a00be44b2a824ded7c/pyyaml_ft-8.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3bb4b927929b0cb162fb1605392a321e3333e48ce616cdcfa04a839271373255", size = 835581, upload-time = "2025-06-10T15:32:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/c0/28/26534bed77109632a956977f60d8519049f545abc39215d086e33a61f1f2/pyyaml_ft-8.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:de04cfe9439565e32f178106c51dd6ca61afaa2907d143835d501d84703d3793", size = 171579, upload-time = "2025-06-10T15:32:14.34Z" }, +] + +[[package]] +name = "ruff" +version = "0.11.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/da/9c6f995903b4d9474b39da91d2d626659af3ff1eeb43e9ae7c119349dba6/ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514", size = 4282054, upload-time = "2025-06-05T21:00:15.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/ce/a11d381192966e0b4290842cc8d4fac7dc9214ddf627c11c1afff87da29b/ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46", size = 10292516, upload-time = "2025-06-05T20:59:32.944Z" }, + { url = "https://files.pythonhosted.org/packages/78/db/87c3b59b0d4e753e40b6a3b4a2642dfd1dcaefbff121ddc64d6c8b47ba00/ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48", size = 11106083, upload-time = "2025-06-05T20:59:37.03Z" }, + { url = "https://files.pythonhosted.org/packages/77/79/d8cec175856ff810a19825d09ce700265f905c643c69f45d2b737e4a470a/ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b", size = 10436024, upload-time = "2025-06-05T20:59:39.741Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5b/f6d94f2980fa1ee854b41568368a2e1252681b9238ab2895e133d303538f/ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a", size = 10646324, upload-time = "2025-06-05T20:59:42.185Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9c/b4c2acf24ea4426016d511dfdc787f4ce1ceb835f3c5fbdbcb32b1c63bda/ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc", size = 10174416, upload-time = "2025-06-05T20:59:44.319Z" }, + { url = "https://files.pythonhosted.org/packages/f3/10/e2e62f77c65ede8cd032c2ca39c41f48feabedb6e282bfd6073d81bb671d/ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629", size = 11724197, upload-time = "2025-06-05T20:59:46.935Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f0/466fe8469b85c561e081d798c45f8a1d21e0b4a5ef795a1d7f1a9a9ec182/ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933", size = 12511615, upload-time = "2025-06-05T20:59:49.534Z" }, + { url = "https://files.pythonhosted.org/packages/17/0e/cefe778b46dbd0cbcb03a839946c8f80a06f7968eb298aa4d1a4293f3448/ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165", size = 12117080, upload-time = "2025-06-05T20:59:51.654Z" }, + { url = "https://files.pythonhosted.org/packages/5d/2c/caaeda564cbe103bed145ea557cb86795b18651b0f6b3ff6a10e84e5a33f/ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71", size = 11326315, upload-time = "2025-06-05T20:59:54.469Z" }, + { url = "https://files.pythonhosted.org/packages/75/f0/782e7d681d660eda8c536962920c41309e6dd4ebcea9a2714ed5127d44bd/ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9", size = 11555640, upload-time = "2025-06-05T20:59:56.986Z" }, + { url = "https://files.pythonhosted.org/packages/5d/d4/3d580c616316c7f07fb3c99dbecfe01fbaea7b6fd9a82b801e72e5de742a/ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc", size = 10507364, upload-time = "2025-06-05T20:59:59.154Z" }, + { url = "https://files.pythonhosted.org/packages/5a/dc/195e6f17d7b3ea6b12dc4f3e9de575db7983db187c378d44606e5d503319/ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7", size = 10141462, upload-time = "2025-06-05T21:00:01.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/8e/39a094af6967faa57ecdeacb91bedfb232474ff8c3d20f16a5514e6b3534/ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432", size = 11121028, upload-time = "2025-06-05T21:00:04.06Z" }, + { url = "https://files.pythonhosted.org/packages/5a/c0/b0b508193b0e8a1654ec683ebab18d309861f8bd64e3a2f9648b80d392cb/ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492", size = 11602992, upload-time = "2025-06-05T21:00:06.249Z" }, + { url = "https://files.pythonhosted.org/packages/7c/91/263e33ab93ab09ca06ce4f8f8547a858cc198072f873ebc9be7466790bae/ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250", size = 10474944, upload-time = "2025-06-05T21:00:08.459Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/7c27734ac2073aae8efb0119cae6931b6fb48017adf048fdf85c19337afc/ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3", size = 11548669, upload-time = "2025-06-05T21:00:11.147Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bf/b273dd11673fed8a6bd46032c0ea2a04b2ac9bfa9c628756a5856ba113b0/ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b", size = 10683928, upload-time = "2025-06-05T21:00:13.758Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] From 19f22e5a14212998345aa288e20444d91b0c99de Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 02:19:19 +0200 Subject: [PATCH 2/6] Fixes --- Justfile | 2 +- def_form/cli/cli.py | 4 +- def_form/cli/main.py | 4 +- def_form/exceptions/def_formatter.py | 2 +- def_form/formatters/__init__.py | 2 +- def_form/formatters/def_formatter/__init__.py | 2 +- def_form/formatters/def_formatter/base.py | 45 +- def_form/formatters/def_formatter/checker.py | 4 +- .../formatters/def_formatter/formatter.py | 22 +- def_form/formatters/def_formatter/manager.py | 23 +- def_form/formatters/def_formatter/models.py | 4 +- def_form/utils/find_pyproject.py | 6 +- pyproject.toml | 3 +- tests/constants.py | 2 +- tests/mock_data/__init__.py | 137 +++--- tests/mock_data/example.py | 155 ++++++- tests/mock_data/expected.py | 427 +++++++++++++++--- uv.lock | 3 + 18 files changed, 639 insertions(+), 208 deletions(-) diff --git a/Justfile b/Justfile index 92f54cc..57d1fdf 100755 --- a/Justfile +++ b/Justfile @@ -17,4 +17,4 @@ fix: uv run ruff check --fix --unsafe-fixes {{ SOURCE_PATH }} tests: - uv run pytest --cov=probirka --cov-report lcov:tests.lcov tests/ + uv run pytest --cov=def-form --cov-report lcov:tests.lcov tests/ diff --git a/def_form/cli/cli.py b/def_form/cli/cli.py index 779152d..72a96b8 100644 --- a/def_form/cli/cli.py +++ b/def_form/cli/cli.py @@ -14,7 +14,7 @@ @click.option('--config', type=str, default=None, help='path to pyproject.toml') @click.option('--exclude', multiple=True, help='paths to exclude from formatting') @click.option('--show-skipped', is_flag=True, help='show skipped files/directories') -def format( +def format( # noqa: PLR0913 path: str, max_def_length: int | None, max_inline_args: int | None, @@ -49,7 +49,7 @@ def format( @click.option('--config', type=str, default=None, help='path to pyproject.toml') @click.option('--exclude', multiple=True, help='paths to exclude from checking') @click.option('--show-skipped', is_flag=True, help='show skipped files/directories') -def check( +def check( # noqa: PLR0913 path: str, max_def_length: int | None, max_inline_args: int | None, diff --git a/def_form/cli/main.py b/def_form/cli/main.py index 07929b8..04dbcdc 100644 --- a/def_form/cli/main.py +++ b/def_form/cli/main.py @@ -5,7 +5,7 @@ @click.group(name='def-form') -def main(): +def main() -> None: click.help_option() @@ -13,4 +13,4 @@ def main(): main.add_command(check) if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/def_form/exceptions/def_formatter.py b/def_form/exceptions/def_formatter.py index 7ff4120..3b62b58 100644 --- a/def_form/exceptions/def_formatter.py +++ b/def_form/exceptions/def_formatter.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from .base import BaseDefFormException +from def_form.exceptions.base import BaseDefFormException @dataclass diff --git a/def_form/formatters/__init__.py b/def_form/formatters/__init__.py index b349561..6722c49 100644 --- a/def_form/formatters/__init__.py +++ b/def_form/formatters/__init__.py @@ -1,4 +1,4 @@ -from .def_formatter import DefManager +from def_form.formatters.def_formatter import DefManager __all__ = [ 'DefManager', diff --git a/def_form/formatters/def_formatter/__init__.py b/def_form/formatters/def_formatter/__init__.py index 994475e..caed506 100644 --- a/def_form/formatters/def_formatter/__init__.py +++ b/def_form/formatters/def_formatter/__init__.py @@ -1,4 +1,4 @@ -from .manager import DefManager +from def_form.formatters.def_formatter.manager import DefManager __all__ = [ 'DefManager', diff --git a/def_form/formatters/def_formatter/base.py b/def_form/formatters/def_formatter/base.py index a43892f..0388fdb 100644 --- a/def_form/formatters/def_formatter/base.py +++ b/def_form/formatters/def_formatter/base.py @@ -1,11 +1,14 @@ import re +from pathlib import Path +from typing import cast -from libcst import FunctionDef +from libcst import FunctionDef, Comma from libcst import MetadataDependent from libcst import Module from libcst import Param from libcst import ParenthesizedWhitespace from libcst import SimpleWhitespace +from libcst.matchers import BaseParenthesizableWhitespace from libcst.metadata import PositionProvider from def_form.exceptions.base import BaseDefFormException @@ -18,16 +21,18 @@ class DefBase(MetadataDependent): METADATA_DEPENDENCIES = (PositionProvider,) - def __init__(self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int = 4): + def __init__( + self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int | None = None + ): super().__init__() self.filepath = filepath self.max_def_length = max_def_length self.max_inline_args = max_inline_args - self.indent_size = indent_size + self.indent_size = indent_size if indent_size is not None else 4 self.issues: list[BaseDefFormException] = [] def _check_issues(self, line_length: int, line_no: int, arg_count: int) -> list[BaseDefFormException]: - issues = [] + issues: list[BaseDefFormException] = [] if self.max_def_length and line_length > self.max_def_length: issues.append( @@ -62,7 +67,7 @@ def has_skip_comment(self, node: FunctionDef) -> bool: pos = self.get_metadata(PositionProvider, node) try: - with open(self.filepath, encoding='utf-8') as f: + with Path.open(Path(self.filepath), encoding='utf-8') as f: lines = f.readlines() except (OSError, UnicodeDecodeError): return False @@ -79,7 +84,7 @@ def has_skip_comment(self, node: FunctionDef) -> bool: return False def _get_params_list(self, node: FunctionDef) -> list[Param]: - params = [] + params: list[Param] = [] params.extend(node.params.params) if isinstance(node.params.star_arg, Param): @@ -92,7 +97,7 @@ def _get_params_list(self, node: FunctionDef) -> list[Param]: return params - def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: + def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: # noqa: PLR0911, PLR0912 ws = node.whitespace_before_params if not isinstance(ws, ParenthesizedWhitespace): return False @@ -114,10 +119,10 @@ def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: for param in all_params[:-1]: comma = param.comma - if comma is None: + if not isinstance(comma, Comma): return False - ws_after = comma.whitespace_after + ws_after = cast(BaseParenthesizableWhitespace, comma.whitespace_after) if not isinstance(ws_after, ParenthesizedWhitespace): return False @@ -132,10 +137,10 @@ def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: last_param = all_params[-1] comma = last_param.comma - if comma is None: + if not isinstance(comma, Comma): return False - ws_after = comma.whitespace_after + ws_after = cast(BaseParenthesizableWhitespace, comma.whitespace_after) if not isinstance(ws_after, ParenthesizedWhitespace): return False @@ -145,10 +150,7 @@ def has_correct_multiline_params_format(self, node: FunctionDef) -> bool: if not isinstance(ws_after.last_line, SimpleWhitespace): return False - if ws_after.last_line.value != '': - return False - - return True + return ws_after.last_line.value == '' def _count_arguments(self, node: FunctionDef) -> int: count = len(node.params.params) @@ -193,14 +195,13 @@ def analyze_function(self, node: FunctionDef) -> FunctionAnalysis: if self.is_single_line_function(node): issues = self._check_issues(line_length, line_no, arg_count) - else: - if not self.has_correct_multiline_params_format(node): - issues.append( - InvalidMultilineParamsIndentException( - path=f'{self.filepath}:{line_no}', - message=f'Invalid multiline function parameters indentation (expected {self.indent_size} spaces)', - ) + elif not self.has_correct_multiline_params_format(node): + issues.append( + InvalidMultilineParamsIndentException( + path=f'{self.filepath}:{line_no}', + message=f'Invalid multiline function parameters indentation (expected {self.indent_size} spaces)', ) + ) has_issues = bool(issues) diff --git a/def_form/formatters/def_formatter/checker.py b/def_form/formatters/def_formatter/checker.py index ded7b97..6f4a248 100644 --- a/def_form/formatters/def_formatter/checker.py +++ b/def_form/formatters/def_formatter/checker.py @@ -16,10 +16,8 @@ def __init__( filepath=filepath, max_def_length=max_def_length, max_inline_args=max_inline_args, indent_size=indent_size ) - def leave_FunctionDef(self, original_node: FunctionDef) -> FunctionDef: + def leave_FunctionDef(self, original_node: FunctionDef) -> None: analysis = self.analyze_function(original_node) if analysis.issues: self.issues.extend(analysis.issues) - - return original_node diff --git a/def_form/formatters/def_formatter/formatter.py b/def_form/formatters/def_formatter/formatter.py index 462f3ee..92abf45 100644 --- a/def_form/formatters/def_formatter/formatter.py +++ b/def_form/formatters/def_formatter/formatter.py @@ -1,3 +1,5 @@ +from typing import Any, cast + from libcst import Comma from libcst import Comment from libcst import CSTTransformer @@ -12,14 +14,16 @@ class DefFormatter(DefBase, CSTTransformer): - def __init__(self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int = 4): + def __init__( + self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int | None = None + ): super().__init__(filepath, max_def_length, max_inline_args) - self.indent_size = indent_size + self.indent_size = indent_size if indent_size is not None else 4 - def _is_valid_param(self, param) -> bool: + def _is_valid_param(self, param: Any) -> bool: return isinstance(param, Param) - def _extract_comment_from_whitespace(self, ws) -> TrailingWhitespace | None: + def _extract_comment_from_whitespace(self, ws: Any) -> TrailingWhitespace | None: if isinstance(ws, TrailingWhitespace): return ws if isinstance(ws, ParenthesizedWhitespace) and isinstance(ws.first_line, TrailingWhitespace): @@ -76,12 +80,16 @@ def _create_formatted_param_for_multi_line(self, param: Param, is_last: bool = F return param.with_changes(comma=new_comma, whitespace_after_param=SimpleWhitespace('')) def _collect_all_params(self, node: FunctionDef) -> list[Param]: - all_params = list(node.params.params) + all_params: list[Param] = list(node.params.params) + if self._is_valid_param(node.params.star_arg): - all_params.append(node.params.star_arg) + all_params.append(cast(Param, node.params.star_arg)) + all_params.extend(node.params.kwonly_params) + if self._is_valid_param(node.params.star_kwarg): - all_params.append(node.params.star_kwarg) + all_params.append(cast(Param, node.params.star_kwarg)) + return all_params def _restore_param_groups(self, formatted_params: list[Param], node: FunctionDef) -> tuple: diff --git a/def_form/formatters/def_formatter/manager.py b/def_form/formatters/def_formatter/manager.py index b8548fd..dd69d1b 100644 --- a/def_form/formatters/def_formatter/manager.py +++ b/def_form/formatters/def_formatter/manager.py @@ -1,5 +1,5 @@ import os -import tomllib +import tomli from collections.abc import Generator from pathlib import Path @@ -13,7 +13,7 @@ class DefManager: - def __init__( + def __init__( # noqa: PLR0913 self, path: str, excluded: tuple[str, ...] | None = None, @@ -51,19 +51,17 @@ def _init_config( self.max_def_length = max_def_length self.max_inline_args = max_inline_args self.indent_size = indent_size - self.is_config_found = bool(config) if not config: config = find_pyproject_toml() - self.is_config_found = bool(config) config_excluded: list[str] = [] - if self.is_config_found and config: + if config: try: - with open(config, 'rb') as f: + with Path.open(Path(config), 'rb') as f: click.secho(f'Using config: {config}', fg='yellow') - config_data = tomllib.load(f) + config_data = tomli.load(f) config_def = config_data.get('tool', {}).get('def-form', {}) @@ -82,7 +80,7 @@ def _init_config( ) config_excluded = config_def.get('exclude', []) - except (FileNotFoundError, tomllib.TOMLDecodeError) as e: + except (FileNotFoundError, tomli.TOMLDecodeError) as e: click.secho(f'Error loading config {config}: {e}', fg='red') self.is_config_found = False @@ -160,7 +158,7 @@ def _process_file( processor_class: type[DefFormatter] | type[DefChecker], ) -> tuple[cst.Module | None, list[BaseDefFormException]]: try: - with open(filepath, encoding='utf-8') as f: + with Path.open(Path(filepath), encoding='utf-8') as f: code = f.read() except (OSError, UnicodeDecodeError) as e: click.secho(f'Error reading {filepath}: {e}', fg='red') @@ -174,9 +172,8 @@ def _process_file( if issubclass(processor_class, DefFormatter): new_tree = wrapper.visit(processor) return new_tree, processor.issues - else: - wrapper.visit(processor) - return None, processor.issues + wrapper.visit(processor) + return None, processor.issues except cst.ParserSyntaxError as e: click.secho(f'Syntax error in {filepath}: {e}', fg='red') @@ -198,7 +195,7 @@ def format(self, write_to: str | None = None) -> None: if new_tree is not None: try: - with open(write_to or filepath, 'w', encoding='utf-8') as f: + with Path.open(Path(write_to or filepath), 'w', encoding='utf-8') as f: f.write(new_tree.code) except OSError as e: click.secho(f'Error writing {filepath}: {e}', fg='red') diff --git a/def_form/formatters/def_formatter/models.py b/def_form/formatters/def_formatter/models.py index 5b40bc3..d7f2c1a 100644 --- a/def_form/formatters/def_formatter/models.py +++ b/def_form/formatters/def_formatter/models.py @@ -17,8 +17,8 @@ class FunctionAnalysis: line_no: int | None = None pos: CodeRange | None = None node: FunctionDef | None = None - issues: list[BaseDefFormException] = None + issues: list[BaseDefFormException] | None = None - def __post_init__(self): + def __post_init__(self) -> None: if self.issues is None: self.issues = [] diff --git a/def_form/utils/find_pyproject.py b/def_form/utils/find_pyproject.py index 1426368..9f28351 100644 --- a/def_form/utils/find_pyproject.py +++ b/def_form/utils/find_pyproject.py @@ -1,10 +1,10 @@ from pathlib import Path -def find_pyproject_toml() -> Path | None: +def find_pyproject_toml() -> str | None: current = Path.cwd() - for parent in [current] + list(current.parents): + for parent in [current, *list(current.parents)]: candidate = parent / 'pyproject.toml' if candidate.is_file(): - return candidate + return str(candidate) return None diff --git a/pyproject.toml b/pyproject.toml index 5b6a54e..04000cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ classifiers = [ dependencies = [ "click>=8.2.1", "libcst>=1.8.2", + "tomli>=2.4.0", ] [build-system] @@ -155,4 +156,4 @@ lint.select = [ [tool.ruff.format] quote-style = "single" -docstring-code-format = true \ No newline at end of file +docstring-code-format = true diff --git a/tests/constants.py b/tests/constants.py index e4a9b94..5acff22 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -2,7 +2,7 @@ MAX_DEF_LENGTH = 100 MAX_INLINE_ARGS = 2 -EXPECTED_TOTAL_ISSUES = 23 +EXPECTED_TOTAL_ISSUES = 62 src_path = Path(__file__).resolve().parent diff --git a/tests/mock_data/__init__.py b/tests/mock_data/__init__.py index 6b3c73e..7ab28e7 100644 --- a/tests/mock_data/__init__.py +++ b/tests/mock_data/__init__.py @@ -1,75 +1,68 @@ -from def_form.exceptions.def_formatter import DefStringTooLongException +from def_form.exceptions.def_formatter import DefStringTooLongException, InvalidMultilineParamsIndentException from def_form.exceptions.def_formatter import TooManyInlineArgumentsException from tests.constants import EXAMPLE_PATH -EXPECTED_ISSUES: list[DefStringTooLongException | TooManyInlineArgumentsException] = [ - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:19', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:25', message='Too many inline args (4 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:28', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:31', message='Too many inline args (4 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:34', message='Too many inline args (3 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:42', message='Too many inline args (4 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:58', message='Too many inline args (4 > 2)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:61', message='Function definition too long (165 > 100)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:64', message='Function definition too long (163 > 100)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:67', message='Function definition too long (126 > 100)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:75', message='Too many inline args (4 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:78', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:84', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:88', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:103', message='Too many inline args (5 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:110', message='Too many inline args (6 > 2)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:113', message='Too many inline args (3 > 2)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:116', message='Function definition too long (174 > 100)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:119', message='Function definition too long (199 > 100)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:119', message='Too many inline args (5 > 2)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:122', message='Function definition too long (127 > 100)', description=None - ), - DefStringTooLongException( - path=f'{EXAMPLE_PATH}:125', message='Function definition too long (138 > 100)', description=None - ), - TooManyInlineArgumentsException( - path=f'{EXAMPLE_PATH}:125', message='Too many inline args (3 > 2)', description=None - ), -] +EXPECTED_ISSUES: list[DefStringTooLongException | TooManyInlineArgumentsException | InvalidMultilineParamsIndentException] = [ + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:19", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:25", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:32", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:35", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:39", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:43", message="Too many inline args (3 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:46", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:51", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:55", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:59", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:64", message="Too many inline args (3 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:67", message="Too many inline args (3 > 2)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:70", message="Function definition too long (104 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:70", message="Too many inline args (3 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:73", message="Too many inline args (3 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:79", message="Too many inline args (3 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:82", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:86", message="Too many inline args (3 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:89", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:92", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:96", message="Function definition too long (142 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:96", message="Too many inline args (3 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:99", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:102", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:105", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:111", message="Too many inline args (5 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:119", message="Too many inline args (3 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:123", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:128", message="Too many inline args (3 > 2)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:149", message="Function definition too long (163 > 100)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:152", message="Function definition too long (126 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:160", message="Too many inline args (3 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:163", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:167", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:170", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:173", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:177", message="Function definition too long (108 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:177", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:183", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:186", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:190", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:194", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:198", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:203", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:218", message="Too many inline args (4 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:225", message="Too many inline args (6 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:228", message="Too many inline args (6 > 2)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:231", message="Too many inline args (5 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:234", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:240", message="Function definition too long (102 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:240", message="Too many inline args (5 > 2)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:243", message="Function definition too long (174 > 100)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:246", message="Function definition too long (199 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:246", message="Too many inline args (4 > 2)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:249", message="Function definition too long (127 > 100)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:252", message="Function definition too long (138 > 100)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:255", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:260", message="Function definition too long (118 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:260", message="Too many inline args (4 > 2)", description=None), + InvalidMultilineParamsIndentException(path=f"{EXAMPLE_PATH}:269", message="Invalid multiline function parameters indentation (expected 4 spaces)", description=None), + DefStringTooLongException(path=f"{EXAMPLE_PATH}:276", message="Function definition too long (129 > 100)", description=None), + TooManyInlineArgumentsException(path=f"{EXAMPLE_PATH}:276", message="Too many inline args (5 > 2)", description=None), +] \ No newline at end of file diff --git a/tests/mock_data/example.py b/tests/mock_data/example.py index b376196..5a5e25e 100644 --- a/tests/mock_data/example.py +++ b/tests/mock_data/example.py @@ -10,8 +10,21 @@ def clear_def(): return def formatted_def( + a, + b: Optional[Union[int, str]] = None, + c: int | str | None = None, +): + return + +def wrong_formatted_def( a, b: Optional[Union[int, str]] = None, +): + return + +def with_comments( + a, # this is an argument + b: Optional[Union[int, str]] = None, c: int | str | None = None, ): return @@ -19,21 +32,85 @@ def formatted_def( def to_much_inline_args(to, much, inline, arguments): return +def single_line_with_comment_first(a, # first param comment + b, c): + return + +def single_line_with_comment_middle(a, b, # middle param comment + c): + return + +def single_line_with_comment_last(a, b, c): # end comment + return + +def single_line_with_multiple_comments(a, # first comment + b, # second comment + c): # third comment + return + +def single_line_with_comment_and_default(a, # param with default + b: int = 42, c: str = "test"): + return + +def single_line_with_comment_and_typehint(a: int, # typed param + b: str, c: Optional[int] = None): + return + +def single_line_with_comment_and_args(a, # regular param + *args, # varargs comment + **kwargs): # keyword args comment + return + +def truly_single_line_with_comment(a, b, c): # truly single line + return + +def truly_single_line_with_comment_first(a, b, c): # comment at end of signature + return + +def truly_single_line_with_type_and_comment(a: int, b: str, c: Optional[int] = None): # all in one line + return + +def truly_single_line_with_args_comment(a, *args, **kwargs): # args and kwargs + return + async def async_def(): return async def async_def_with_args(a, b, c): return +async def async_single_line_with_comment(a, # async with comment + b, c): + return + +async def truly_async_single_line_with_comment(a, b, c): # async single line + return + def to_much_inline_args_with_typehints(to: str, much, inline: int, arguments): return +def single_line_with_comment_and_complex_type(a: Optional[Union[int, str]], # complex type + b: dict[str, int] = {}, c: list[str] = []): + return + +def truly_single_line_with_complex_type(a: Optional[Union[int, str]], b: dict[str, int] = {}, c: list[str] = []): # complex types in one line + return + def kw_args(def_, with_, *args, **kwargs): return def def_kw_args_first(a, *args, b, **kwargs): return +def single_line_kw_only_with_comments(a, *args, # varargs + b: str, # keyword-only + c: int = 10, # keyword-only with default + **kwargs): # kwargs + return + +def truly_single_line_kw_only(a, *args, b: str, c: int = 10, **kwargs): # kw-only in one line + return + @example_of_decorator def def_with_decorator(): return @@ -42,6 +119,15 @@ def def_with_decorator(): def def_with_decorator_and_args(a, b, c: Optional[str]): return +@example_of_decorator +def single_line_decorated_with_comment(a, # decorated with comment + b, c: Optional[str]): + return + +@example_of_decorator +def truly_single_line_decorated(a, b, c: Optional[str]): # decorated single line + return + def skipped_with_right_comment(a, b, c): # def-form: skip return @@ -53,9 +139,8 @@ def skipped_with_up_comment(a, b, c): def skipped_with_right_comment_and_decorator(a, b, c): # def-form: skip return -# def-form: skip @example_of_decorator -def skipped_with_up_comment_and_decorator(a, b, c): +def skipped_with_up_comment_and_decorator(a, b, c): # def-form: skip return def long_typehint_without_args() -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: @@ -75,19 +160,49 @@ def class_method(self): def class_method_with_args(self, a, b): return + def class_method_single_line_with_comment(self, a, # class method comment + b, c): + return + + def truly_class_method_single_line(self, a, b, c): # class method single line + return + def class_method_with_args_with_typehints(self, a: Optional[int], b: int, c): return + def class_method_single_line_with_typehint_comment(self, a: int, # typed comment + b: str, c: Optional[int] = None): + return + + def truly_class_method_with_types(self, a: int, b: str, c: Optional[int] = None): # class method with types + return + async def async_def(self): return async def async_def_with_args(self, a, b, c): return + async def async_class_method_single_line(self, a, # async class method + b, c): + return + + async def truly_async_class_method(self, a, b, c): # async class method single line + return + @example_of_decorator async def async_def_with_args_and_decorator(self, a, b, c): return + @example_of_decorator + async def async_class_method_with_comment(self, a, # decorated async + b, c): + return + + @example_of_decorator + async def truly_async_decorated_class_method(self, a, b, c): # decorated async class method + return + def skipped_with_right_comment(self, a, b, c): # def-form: skip return @@ -113,6 +228,18 @@ def def_with_kw_args(self, a, b, c, *args, **kwargs): def def_with_kw_args_first(self, *args, a, b, c, **kwargs): return + def def_with_kw_only_args(self, *args, b: str, s: str, **kwargs): + return + + def class_method_kw_only_with_comments(self, *args, # varargs in class + b: str, # keyword-only in class + s: str, # another keyword-only + **kwargs): # kwargs in class + return + + def truly_class_method_kw_only(self, *args, b: str, s: str, **kwargs): # kw-only in class single line + return + def class_method_with_long_typehint(self) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: return @@ -124,3 +251,27 @@ def class_method_with_looooooooooooooooooooooooooooooooooooooooooooooooooooooooo def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name_and_arg(self, a): return + + def class_method_single_line_with_defaults(self, a: int = 1, # default value + b: str = "test", # another default + c: Optional[int] = None): # optional default + return + + def truly_class_method_with_defaults(self, a: int = 1, b: str = "test", c: Optional[int] = None): # defaults in class + return + +def single_line_with_return_type_comment(a: int, b: str) -> bool: # return type comment + return True + +def truly_single_line_with_return_type(a: int, b: str) -> bool: # return type in one line + return True + +def single_line_with_all_features(a: int, # typed with comment + b: str = "default", # default with comment + *args, # varargs with comment + c: int = 10, # keyword-only with comment + **kwargs) -> bool: # kwargs and return type + return True + +def truly_single_line_all_features(a: int, b: str = "default", *args, c: int = 10, **kwargs) -> bool: # all features in one line + return True diff --git a/tests/mock_data/expected.py b/tests/mock_data/expected.py index 1037a62..febc558 100644 --- a/tests/mock_data/expected.py +++ b/tests/mock_data/expected.py @@ -10,66 +10,218 @@ def clear_def(): return def formatted_def( - a, - b: Optional[Union[int, str]] = None, - c: int | str | None = None, + a, + b: Optional[Union[int, str]] = None, + c: int | str | None = None, +): + return + +def wrong_formatted_def( + a, + b: Optional[Union[int, str]] = None, +): + return + +def with_comments( + a, # this is an argument + b: Optional[Union[int, str]] = None, + c: int | str | None = None, ): return def to_much_inline_args( - to, - much, - inline, - arguments, + to, + much, + inline, + arguments, +): + return + +def single_line_with_comment_first( + a, # first param comment + b, + c, +): + return + +def single_line_with_comment_middle( + a, + b, # middle param comment + c, +): + return + +def single_line_with_comment_last( + a, + b, + c, +): # end comment + return + +def single_line_with_multiple_comments( + a, # first comment + b, # second comment + c, +): # third comment + return + +def single_line_with_comment_and_default( + a, # param with default + b: int = 42, + c: str = "test", ): return +def single_line_with_comment_and_typehint( + a: int, # typed param + b: str, + c: Optional[int] = None, +): + return + +def single_line_with_comment_and_args( + a, # regular param + *args, # varargs comment + **kwargs, +): # keyword args comment + return + +def truly_single_line_with_comment( + a, + b, + c, +): # truly single line + return + +def truly_single_line_with_comment_first( + a, + b, + c, +): # comment at end of signature + return + +def truly_single_line_with_type_and_comment( + a: int, + b: str, + c: Optional[int] = None, +): # all in one line + return + +def truly_single_line_with_args_comment( + a, + *args, + **kwargs, +): # args and kwargs + return + async def async_def(): return async def async_def_with_args( - a, - b, - c, + a, + b, + c, +): + return + +async def async_single_line_with_comment( + a, # async with comment + b, + c, ): return +async def truly_async_single_line_with_comment( + a, + b, + c, +): # async single line + return + def to_much_inline_args_with_typehints( - to: str, - much, - inline: int, - arguments, + to: str, + much, + inline: int, + arguments, +): + return + +def single_line_with_comment_and_complex_type( + a: Optional[Union[int, str]], # complex type + b: dict[str, int] = {}, + c: list[str] = [], ): return +def truly_single_line_with_complex_type( + a: Optional[Union[int, str]], + b: dict[str, int] = {}, + c: list[str] = [], +): # complex types in one line + return + def kw_args( - def_, - with_, - *args, - **kwargs, + def_, + with_, + *args, + **kwargs, ): return def def_kw_args_first( - a, - *args, - b, - **kwargs, + a, + *args, + b, + **kwargs, ): return +def single_line_kw_only_with_comments( + a, + *args, # varargs + b: str, # keyword-only + c: int = 10, # keyword-only with default + **kwargs, +): # kwargs + return + +def truly_single_line_kw_only( + a, + *args, + b: str, + c: int = 10, + **kwargs, +): # kw-only in one line + return + @example_of_decorator def def_with_decorator(): return @example_of_decorator def def_with_decorator_and_args( - a, - b, - c: Optional[str], + a, + b, + c: Optional[str], +): + return + +@example_of_decorator +def single_line_decorated_with_comment( + a, # decorated with comment + b, + c: Optional[str], ): return +@example_of_decorator +def truly_single_line_decorated( + a, + b, + c: Optional[str], +): # decorated single line + return + def skipped_with_right_comment(a, b, c): # def-form: skip return @@ -81,26 +233,20 @@ def skipped_with_up_comment(a, b, c): def skipped_with_right_comment_and_decorator(a, b, c): # def-form: skip return -# def-form: skip @example_of_decorator -def skipped_with_up_comment_and_decorator( - a, - b, - c, -): +def skipped_with_up_comment_and_decorator(a, b, c): # def-form: skip return -def long_typehint_without_args( - ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: +def long_typehint_without_args() -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: return def long_typehint_with_args( - a, + a, ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: return def loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_def_name( - a, + a, ): return @@ -110,40 +256,106 @@ def class_method(self): return def class_method_with_args( - self, - a, - b, + self, + a, + b, ): return + def class_method_single_line_with_comment( + self, + a, # class method comment + b, + c, + ): + return + + def truly_class_method_single_line( + self, + a, + b, + c, + ): # class method single line + return + def class_method_with_args_with_typehints( - self, - a: Optional[int], - b: int, - c, + self, + a: Optional[int], + b: int, + c, ): return + def class_method_single_line_with_typehint_comment( + self, + a: int, # typed comment + b: str, + c: Optional[int] = None, + ): + return + + def truly_class_method_with_types( + self, + a: int, + b: str, + c: Optional[int] = None, + ): # class method with types + return + async def async_def(self): return async def async_def_with_args( - self, - a, - b, - c, + self, + a, + b, + c, + ): + return + + async def async_class_method_single_line( + self, + a, # async class method + b, + c, ): return + async def truly_async_class_method( + self, + a, + b, + c, + ): # async class method single line + return + @example_of_decorator async def async_def_with_args_and_decorator( - self, - a, - b, - c, + self, + a, + b, + c, ): return + @example_of_decorator + async def async_class_method_with_comment( + self, + a, # decorated async + b, + c, + ): + return + + @example_of_decorator + async def truly_async_decorated_class_method( + self, + a, + b, + c, + ): # decorated async class method + return + def skipped_with_right_comment(self, a, b, c): # def-form: skip return @@ -157,10 +369,10 @@ def def_with_decorator(self): @example_of_decorator def def_with_decorator_and_args( - self, - a, - b, - c, + self, + a, + b, + c, ): return @@ -169,45 +381,112 @@ def static_method(): return def def_with_kw_args( - self, - a, - b, - c, - *args, - **kwargs, + self, + a, + b, + c, + *args, + **kwargs, ): return def def_with_kw_args_first( - self, - *args, - a, - b, - c, - **kwargs, + self, + *args, + a, + b, + c, + **kwargs, ): return + def def_with_kw_only_args( + self, + *args, + b: str, + s: str, + **kwargs, + ): + return + + def class_method_kw_only_with_comments( + self, + *args, # varargs in class + b: str, # keyword-only in class + s: str, # another keyword-only + **kwargs, + ): # kwargs in class + return + + def truly_class_method_kw_only( + self, + *args, + b: str, + s: str, + **kwargs, + ): # kw-only in class single line + return + def class_method_with_long_typehint( - self, + self, ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: return def class_method_with_long_typehint_with_args( - self, - a, - b: int, - c, + self, + a, + b: int, + c, ) -> Optional[Union[int, str, list, dict[str, str], tuple[str], None, dict[str | int, list[str] | tuple[str, str, int, int] | None]]]: return def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name( - self, + self, ): return def class_method_with_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name_and_arg( - self, - a, + self, + a, ): return + + def class_method_single_line_with_defaults( + self, + a: int = 1, # default value + b: str = "test", # another default + c: Optional[int] = None, + ): # optional default + return + + def truly_class_method_with_defaults( + self, + a: int = 1, + b: str = "test", + c: Optional[int] = None, + ): # defaults in class + return + +def single_line_with_return_type_comment(a: int, b: str) -> bool: # return type comment + return True + +def truly_single_line_with_return_type(a: int, b: str) -> bool: # return type in one line + return True + +def single_line_with_all_features( + a: int, # typed with comment + b: str = "default", # default with comment + *args, # varargs with comment + c: int = 10, # keyword-only with comment + **kwargs, +) -> bool: # kwargs and return type + return True + +def truly_single_line_all_features( + a: int, + b: str = "default", + *args, + c: int = 10, + **kwargs, +) -> bool: # all features in one line + return True diff --git a/uv.lock b/uv.lock index e5815bf..a96900f 100644 --- a/uv.lock +++ b/uv.lock @@ -134,10 +134,12 @@ toml = [ [[package]] name = "def-form" +version = "0.1.0" source = { editable = "." } dependencies = [ { name = "click" }, { name = "libcst" }, + { name = "tomli" }, ] [package.dev-dependencies] @@ -152,6 +154,7 @@ dev = [ requires-dist = [ { name = "click", specifier = ">=8.2.1" }, { name = "libcst", specifier = ">=1.8.2" }, + { name = "tomli", specifier = ">=2.4.0" }, ] [package.metadata.requires-dev] From 4a8bbb5b7a5df3cafd5c2d28b02f7d2dd6922640 Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 15:54:02 +0200 Subject: [PATCH 3/6] Update CI/CD --- .github/workflows/pypi.yml | 33 +++++++++++++++++++++++++++++++++ .github/workflows/tests.yml | 14 +++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 0ad2f97..ff531eb 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -10,8 +10,41 @@ permissions: contents: read jobs: + lint: + name: lint + runs-on: ubuntu-latest + strategy: + matrix: + just-trigger: [ format, lint, mypy ] + + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: astral-sh/setup-uv@v4 + + - run: uv python install 3.12 + - run: uv sync --all-extras --dev + - run: just ${{ matrix.just-trigger }} + + tests: + name: tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: extractions/setup-just@v2 + - uses: astral-sh/setup-uv@v4 + + - run: uv python install 3.12 + - run: uv sync --all-extras --dev + + - name: Run tests + run: just tests + publish: + name: publish to PyPI runs-on: ubuntu-latest + needs: [ lint, tests ] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9e10fbd..77c7135 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,6 +5,8 @@ on: branches: [ main ] push: branches: [ main ] + tags: + - "v*.*.*" jobs: linters: @@ -29,6 +31,16 @@ jobs: - uses: actions/checkout@v4 - uses: extractions/setup-just@v2 - uses: astral-sh/setup-uv@v4 + - run: uv python install ${{ matrix.python-version }} - run: uv sync --all-extras --dev - - run: just tests + + - name: Run tests + run: just tests + + - name: Upload coverage to Coveralls + if: matrix.python-version == '3.12' + uses: coverallsapp/github-action@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: tests.lcov From 547fab8458b9f883580140d8c07f42518461bdba Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 16:01:47 +0200 Subject: [PATCH 4/6] Fix review threads --- def_form/formatters/def_formatter/base.py | 6 +++++- def_form/formatters/def_formatter/formatter.py | 6 +++++- def_form/formatters/def_formatter/manager.py | 9 --------- def_form/formatters/def_formatter/models.py | 2 -- pyproject.toml | 17 ++++++++--------- tests/test_checker/test_checker.py | 11 ++++++----- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/def_form/formatters/def_formatter/base.py b/def_form/formatters/def_formatter/base.py index 0388fdb..7b2f6ba 100644 --- a/def_form/formatters/def_formatter/base.py +++ b/def_form/formatters/def_formatter/base.py @@ -22,7 +22,11 @@ class DefBase(MetadataDependent): METADATA_DEPENDENCIES = (PositionProvider,) def __init__( - self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int | None = None + self, + filepath: str, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None = None, ): super().__init__() self.filepath = filepath diff --git a/def_form/formatters/def_formatter/formatter.py b/def_form/formatters/def_formatter/formatter.py index 92abf45..bf0dd55 100644 --- a/def_form/formatters/def_formatter/formatter.py +++ b/def_form/formatters/def_formatter/formatter.py @@ -15,7 +15,11 @@ class DefFormatter(DefBase, CSTTransformer): def __init__( - self, filepath: str, max_def_length: int | None, max_inline_args: int | None, indent_size: int | None = None + self, + filepath: str, + max_def_length: int | None, + max_inline_args: int | None, + indent_size: int | None = None, ): super().__init__(filepath, max_def_length, max_inline_args) self.indent_size = indent_size if indent_size is not None else 4 diff --git a/def_form/formatters/def_formatter/manager.py b/def_form/formatters/def_formatter/manager.py index dd69d1b..fa6bb3e 100644 --- a/def_form/formatters/def_formatter/manager.py +++ b/def_form/formatters/def_formatter/manager.py @@ -183,7 +183,6 @@ def _process_file( return None, [] def format(self, write_to: str | None = None) -> None: - """Форматирует файлы согласно правилам.""" processed_count = 0 self.issues.clear() @@ -231,11 +230,3 @@ def _echo_summary(self, mode: str, processed_count: int) -> None: click.secho(f'{mode.capitalize()} Summary:', fg='cyan', bold=True) click.echo(f'Processed files: {processed_count}') click.echo(f'Issues found: {len(self.issues)}') - - -if __name__ == '__main__': - DefManager( - path='../../../../test_file.py', - max_def_length=100, - max_inline_args=2, - ).format() diff --git a/def_form/formatters/def_formatter/models.py b/def_form/formatters/def_formatter/models.py index d7f2c1a..5c1c2a5 100644 --- a/def_form/formatters/def_formatter/models.py +++ b/def_form/formatters/def_formatter/models.py @@ -8,8 +8,6 @@ @dataclass class FunctionAnalysis: - """Результат анализа функции.""" - should_process: bool reason: str | None = None line_length: int | None = None diff --git a/pyproject.toml b/pyproject.toml index 04000cf..2b6e829 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,13 +30,20 @@ classifiers = [ "Programming Language :: Python :: 3.13", ] - dependencies = [ "click>=8.2.1", "libcst>=1.8.2", "tomli>=2.4.0", ] +[dependency-groups] +dev = [ + "pytest>=8.3.5", + "pytest-cov>=7.0.0", + "ruff>=0.11.4,<0.12.0", + "mypy>=1.19.1", +] + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" @@ -49,14 +56,6 @@ Homepage = "https://github.com/TopNik073/def-form" Repository = "https://github.com/TopNik073/def-form" Issues = "https://github.com/TopNik073/def-form/issues" -[dependency-groups] -dev = [ - "pytest>=8.3.5", - "pytest-cov>=7.0.0", - "ruff>=0.11.4,<0.12.0", - "mypy>=1.19.1", -] - [tool.def-form] max_def_length = 100 max_inline_args = 2 diff --git a/tests/test_checker/test_checker.py b/tests/test_checker/test_checker.py index 1812141..e4d3d72 100644 --- a/tests/test_checker/test_checker.py +++ b/tests/test_checker/test_checker.py @@ -1,3 +1,5 @@ +import pytest + from def_form.exceptions.base import BaseDefFormException from def_form.formatters.def_formatter import DefManager from tests.constants import EXPECTED_TOTAL_ISSUES @@ -5,12 +7,11 @@ def test_failed_check(get_def_manager: DefManager): - try: + with pytest.raises(BaseDefFormException): get_def_manager.check() - except BaseDefFormException as e: - assert isinstance(e, BaseDefFormException) - assert get_def_manager.issues == EXPECTED_ISSUES - assert len(get_def_manager.issues) == EXPECTED_TOTAL_ISSUES + + assert get_def_manager.issues == EXPECTED_ISSUES + assert len(get_def_manager.issues) == EXPECTED_TOTAL_ISSUES def test_successful_check(get_correct_def_manager: DefManager): From 6df231ac3d4c938495f1854a17982146862a4d73 Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 16:07:18 +0200 Subject: [PATCH 5/6] Fix just test --- Justfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index 57d1fdf..c6b9ffd 100755 --- a/Justfile +++ b/Justfile @@ -17,4 +17,8 @@ fix: uv run ruff check --fix --unsafe-fixes {{ SOURCE_PATH }} tests: - uv run pytest --cov=def-form --cov-report lcov:tests.lcov tests/ + uv run pytest \ + --cov=def_form \ + --cov-report=lcov:tests.lcov \ + tests/ + From 58322567650fde0041c8c736f633a3fd805aa90b Mon Sep 17 00:00:00 2001 From: Nikita Borzov Date: Tue, 27 Jan 2026 16:10:56 +0200 Subject: [PATCH 6/6] Update coveralls badge --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 9bf9995..67e46ee 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ Python function definition formatter [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![PyPI](https://img.shields.io/pypi/v/def-form.svg)](https://pypi.python.org/pypi/def-form) [![PyPI](https://img.shields.io/pypi/dm/def-form.svg)](https://pypi.python.org/pypi/def-form) -[![Coverage Status](https://coveralls.io/repos/github/TopNik073/def-form/badge.svg?branch=polish-docs)](https://coveralls.io/github/TopNik073/def-form?branch=polish-docs) - +[![Coverage Status](https://coveralls.io/repos/github/TopNik073/def-form/badge.svg?branch=init)](https://coveralls.io/github/TopNik073/def-form?branch=init) ## Overview `def-form` is a code formatting tool that focuses specifically on Python function definitions. It helps maintain consistent formatting of function signatures by automatically organizing arguments vertically when they exceed specified thresholds.