From 40c43b5e68a22ab1d437c9e5fab828ba52f48970 Mon Sep 17 00:00:00 2001 From: David Salvisberg Date: Fri, 13 Dec 2024 11:26:22 +0100 Subject: [PATCH] chore: Drop Python 3.8 support. Add Python 3.13 support. Update CI/dev dependencies. --- .github/workflows/testing.yml | 31 +- .pre-commit-config.yaml | 18 +- flake8_type_checking/checker.py | 62 ++- flake8_type_checking/constants.py | 2 - flake8_type_checking/types.py | 5 +- poetry.lock | 621 ++++++++++++++--------------- pyproject.toml | 22 +- setup.cfg | 12 - tests/test_attrs.py | 30 +- tests/test_errors.py | 88 ++-- tests/test_exempt_modules.py | 24 +- tests/test_fastapi_decorators.py | 24 +- tests/test_fastapi_dependencies.py | 6 +- tests/test_import_visitors.py | 4 +- tests/test_injector.py | 36 +- tests/test_name_extraction.py | 8 +- tests/test_name_visitor.py | 62 ++- tests/test_pydantic.py | 42 +- tests/test_should_warn.py | 56 ++- tests/test_sqlalchemy.py | 42 +- tests/test_strict.py | 6 +- tests/test_tc001_to_tc003.py | 117 ++++-- tests/test_tc004.py | 96 +++-- tests/test_tc005.py | 30 +- tests/test_tc006.py | 72 ++-- tests/test_tc007.py | 18 +- tests/test_tc008.py | 56 ++- tests/test_tc009.py | 72 ++-- tests/test_tc010.py | 6 +- tests/test_tc100.py | 12 +- tests/test_tc101.py | 72 ++-- tests/test_tc200.py | 42 +- tests/test_tc201.py | 72 ++-- 33 files changed, 1109 insertions(+), 757 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 6492cb5..12224bc 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -10,25 +10,25 @@ jobs: linting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: "3.10.7" - - uses: actions/cache@v3 + python-version: "3.10" + - uses: actions/cache@v4 id: cache-venv with: path: .venv - key: venv-5 # increment to reset + key: venv-6 # increment to reset - run: | python -m venv .venv --upgrade-deps source .venv/bin/activate pip install pre-commit if: steps.cache-venv.outputs.cache-hit != 'true' - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: pre-commit-cache with: path: ~/.cache/pre-commit - key: ${{ hashFiles('**/pre-commit-config.yaml') }}-4 + key: ${{ hashFiles('**/pre-commit-config.yaml') }}-5 - run: | source .venv/bin/activate pre-commit run --all-files @@ -38,19 +38,20 @@ jobs: strategy: fail-fast: false matrix: - python-version: [ "3.8.18", "3.9.18", "3.10.13", "3.11.6", "3.12.0" ] + python-version: [ "3.9", "3.10", "3.11", "3.12", "3.13" ] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + id: setup-python with: python-version: "${{ matrix.python-version }}" - - uses: actions/cache@v3 + - uses: actions/cache@v4 id: poetry-cache with: path: | ~/.local .venv - key: ${{ hashFiles('**/poetry.lock') }}-${{ matrix.python-version }}-8 + key: ${{ hashFiles('**/poetry.lock') }}-${{ steps.setup-python.outputs.python-version }}-9 - name: Install Poetry uses: snok/install-poetry@v1 with: @@ -72,9 +73,9 @@ jobs: coverage run -m pytest tests coverage xml coverage report - - uses: codecov/codecov-action@v2 + - uses: codecov/codecov-action@v5 with: - file: ./coverage.xml + files: ./coverage.xml fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} - if: matrix.python-version == '3.10.7' + if: matrix.python-version == '3.12' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2a54689..4fe1b9e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/psf/black - rev: 23.11.0 + rev: 24.10.0 hooks: - id: black - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v5.0.0 hooks: - id: check-ast - id: check-added-large-files @@ -19,7 +19,7 @@ repos: - id: mixed-line-ending - id: trailing-whitespace - repo: https://github.com/pycqa/flake8 - rev: 6.1.0 + rev: 7.1.1 hooks: - id: flake8 additional_dependencies: [ @@ -31,23 +31,21 @@ repos: 'flake8-pytest-style', 'flake8-docstrings', 'flake8-printf-formatting', - 'flake8-type-checking==2.0.6', + 'flake8-type-checking==2.9.1', ] - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.19.0 hooks: - id: pyupgrade - args: [ "--py36-plus", "--py37-plus", "--py38-plus", '--keep-runtime-typing' ] + args: [ "--py39-plus", '--keep-runtime-typing' ] - repo: https://github.com/pycqa/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.13.0 hooks: - id: mypy - args: - - --config-file=setup.cfg additional_dependencies: - pytest - flake8 diff --git a/flake8_type_checking/checker.py b/flake8_type_checking/checker.py index bbb2f12..93d28df 100644 --- a/flake8_type_checking/checker.py +++ b/flake8_type_checking/checker.py @@ -5,13 +5,13 @@ import os import sys from abc import ABC, abstractmethod -from ast import Index, literal_eval +from ast import literal_eval from collections import defaultdict from contextlib import contextmanager, suppress from dataclasses import dataclass from itertools import chain from pathlib import Path -from typing import TYPE_CHECKING, Literal, NamedTuple, cast +from typing import TYPE_CHECKING, Any, Literal, NamedTuple, cast from classify_imports import Classified, classify_base @@ -36,27 +36,13 @@ TC200, TC201, builtin_names, - py38, sqlalchemy_default_mapped_dotted_names, ) -try: - ast_unparse = ast.unparse # type: ignore[attr-defined] -except AttributeError: # pragma: no cover - # Python < 3.9 - - import astor - - def ast_unparse(node: ast.AST) -> str: - """AST unparsing helper for Python < 3.9.""" - return cast('str', astor.to_source(node)).strip() - - if TYPE_CHECKING: from _ast import AsyncFunctionDef, FunctionDef from argparse import Namespace from collections.abc import Iterator - from typing import Any, Optional, Union from flake8_type_checking.types import ( Comprehension, @@ -104,18 +90,18 @@ def visit(self, node: ast.AST) -> None: setattr(node.right, BINOP_OPERAND_PROPERTY, True) self.visit(node.left) self.visit(node.right) - elif (py38 and isinstance(node, Index)) or isinstance(node, ast.Attribute): + elif isinstance(node, ast.Attribute): self.visit(node.value) elif isinstance(node, ast.Subscript): self.visit(node.value) if self.is_typing(node.value, 'Literal'): return elif self.is_typing(node.value, 'Annotated') and isinstance( - (elts_node := node.slice.value if py38 and isinstance(node.slice, Index) else node.slice), + node.slice, (ast.Tuple, ast.List), ): - if elts_node.elts: - elts_iter = iter(elts_node.elts) + if node.slice.elts: + elts_iter = iter(node.slice.elts) # only visit the first element like a type expression self.visit_annotated_type(next(elts_iter)) for value_node in elts_iter: @@ -144,9 +130,9 @@ class AttrsMixin: if TYPE_CHECKING: third_party_imports: dict[str, Import] - def get_all_attrs_imports(self) -> dict[Optional[str], str]: + def get_all_attrs_imports(self) -> dict[str | None, str]: """Return a map of all attrs/attr imports.""" - attrs_imports: dict[Optional[str], str] = {} # map of alias to full import name + attrs_imports: dict[str | None, str] = {} # map of alias to full import name for node in self.third_party_imports.values(): module = getattr(node, 'module', '') @@ -166,7 +152,7 @@ def is_attrs_class(self, class_node: ast.ClassDef) -> bool: attrs_imports = self.get_all_attrs_imports() return any(self.is_attrs_decorator(decorator, attrs_imports) for decorator in class_node.decorator_list) - def is_attrs_decorator(self, decorator: Any, attrs_imports: dict[Optional[str], str]) -> bool: + def is_attrs_decorator(self, decorator: Any, attrs_imports: dict[str | None, str]) -> bool: """Check whether a class decorator is an attrs decorator or not.""" if isinstance(decorator, ast.Call): return self.is_attrs_decorator(decorator.func, attrs_imports) @@ -185,7 +171,7 @@ def is_attrs_attribute(attribute: ast.Attribute) -> bool: return any(e for e in actual if e in ATTRS_DECORATORS) @staticmethod - def is_attrs_str(attribute: Union[str, ast.expr], attrs_imports: dict[Optional[str], str]) -> bool: + def is_attrs_str(attribute: str | ast.expr, attrs_imports: dict[str | None, str]) -> bool: """Check whether an ast.expr or string is an attrs string or not.""" actual = attrs_imports.get(str(attribute), '') return actual in ATTRS_DECORATORS @@ -211,7 +197,7 @@ class DunderAllMixin: """ if TYPE_CHECKING: - uses: dict[str, list[tuple[ast.AST, Scope]]] + uses: dict[str, list[tuple[ast.expr, Scope]]] current_scope: Scope def generic_visit(self, node: ast.AST) -> None: # noqa: D102 @@ -285,12 +271,12 @@ class PydanticMixin: if TYPE_CHECKING: pydantic_enabled: bool - pydantic_validate_arguments_import_name: Optional[str] + pydantic_validate_arguments_import_name: str | None def visit(self, node: ast.AST) -> ast.AST: # noqa: D102 ... - def _function_is_wrapped_by_validate_arguments(self, node: Union[FunctionDef, AsyncFunctionDef]) -> bool: + def _function_is_wrapped_by_validate_arguments(self, node: FunctionDef | AsyncFunctionDef) -> bool: if self.pydantic_enabled and node.decorator_list: for decorator_node in node.decorator_list: if getattr(decorator_node, 'id', '') == self.pydantic_validate_arguments_import_name: @@ -360,7 +346,7 @@ class SQLAlchemyMixin: sqlalchemy_enabled: bool sqlalchemy_mapped_dotted_names: set[str] current_scope: Scope - uses: dict[str, list[tuple[ast.AST, Scope]]] + uses: dict[str, list[tuple[ast.expr, Scope]]] soft_uses: set[str] in_soft_use_context: bool @@ -504,7 +490,7 @@ def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None: if self.injector_enabled: self.handle_injector_declaration(node) - def handle_injector_declaration(self, node: Union[AsyncFunctionDef, FunctionDef]) -> None: + def handle_injector_declaration(self, node: AsyncFunctionDef | FunctionDef) -> None: """ Adjust for injector declaration setting. @@ -553,7 +539,7 @@ def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None: if (self.fastapi_enabled and node.decorator_list) or self.fastapi_dependency_support_enabled: self.handle_fastapi_decorator(node) - def handle_fastapi_decorator(self, node: Union[AsyncFunctionDef, FunctionDef]) -> None: + def handle_fastapi_decorator(self, node: AsyncFunctionDef | FunctionDef) -> None: """ Adjust for FastAPI decorator setting. @@ -648,7 +634,7 @@ class ImportName: _module: str _name: str - _alias: Optional[str] + _alias: str | None #: Whether or not this import is exempt from TC001-004 checks. exempt: bool @@ -1034,8 +1020,8 @@ def __init__( injector_enabled: bool, cattrs_enabled: bool, pydantic_enabled_baseclass_passlist: list[str], - typing_modules: Optional[list[str]] = None, - exempt_modules: Optional[list[str]] = None, + typing_modules: list[str] | None = None, + exempt_modules: list[str] | None = None, ) -> None: super().__init__() @@ -1074,7 +1060,7 @@ def __init__( self.scopes: list[Scope] = [] #: List of all names and ids, except type declarations - self.uses: dict[str, list[tuple[ast.AST, Scope]]] = defaultdict(list) + self.uses: dict[str, list[tuple[ast.expr, Scope]]] = defaultdict(list) #: Contains a set of all names to be treated like soft-uses. # i.e. we don't know if it will be used at runtime or not, so @@ -1085,7 +1071,7 @@ def __init__( self.annotation_visitor = ImportAnnotationVisitor(self) #: Whether there is a `from __futures__ import annotations` present in the file - self.futures_annotation: Optional[bool] = None + self.futures_annotation: bool | None = None #: Where the type checking block exists (line_start, line_end, col_offset) # Empty type checking blocks are used for TC005 errors, while the type @@ -1098,7 +1084,7 @@ def __init__( self.unquoted_types_in_casts: list[tuple[int, int, str]] = [] #: For tracking which comprehension/IfExp we're currently inside of - self.active_context: Optional[Comprehension | ast.IfExp] = None + self.active_context: Comprehension | ast.IfExp | None = None #: Whether or not we're in a context where uses count as soft-uses. # E.g. the type expression of `typing.Annotated[type, value]` @@ -1914,7 +1900,7 @@ def register_unquoted_type_in_typing_cast(self, node: ast.Call) -> None: if isinstance(arg, ast.Constant) and isinstance(arg.value, str): return # Type argument is already a string literal. - self.unquoted_types_in_casts.append((arg.lineno, arg.col_offset, ast_unparse(arg))) + self.unquoted_types_in_casts.append((arg.lineno, arg.col_offset, ast.unparse(arg))) def visit_Call(self, node: ast.Call) -> None: """Check arguments of calls, e.g. typing.cast().""" @@ -1937,7 +1923,7 @@ class TypingOnlyImportsChecker: 'future_option_enabled', ] - def __init__(self, node: ast.Module, options: Optional[Namespace]) -> None: + def __init__(self, node: ast.Module, options: Namespace | None) -> None: self.cwd = Path(os.getcwd()) self.strict_mode = getattr(options, 'type_checking_strict', False) diff --git a/flake8_type_checking/constants.py b/flake8_type_checking/constants.py index 7cf3c11..e06516f 100644 --- a/flake8_type_checking/constants.py +++ b/flake8_type_checking/constants.py @@ -1,5 +1,4 @@ import builtins -import sys import flake8 @@ -18,7 +17,6 @@ ] ATTRS_IMPORTS = {'attrs', 'attr'} -py38 = sys.version_info.major == 3 and sys.version_info.minor == 8 flake_version_gt_v4 = tuple(int(i) for i in flake8.__version__.split('.')) >= (4, 0, 0) # Based off of what pyflakes does diff --git a/flake8_type_checking/types.py b/flake8_type_checking/types.py index 85db8af..c83805b 100644 --- a/flake8_type_checking/types.py +++ b/flake8_type_checking/types.py @@ -4,12 +4,13 @@ if TYPE_CHECKING: import ast - from typing import Any, Generator, Optional, Protocol, Tuple, Union + from collections.abc import Generator + from typing import Any, Optional, Protocol, Union Function = Union[ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda] Comprehension = Union[ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp] Import = Union[ast.Import, ast.ImportFrom] - Flake8Generator = Generator[Tuple[int, int, str, Any], None, None] + Flake8Generator = Generator[tuple[int, int, str, Any], None, None] class Name(Protocol): asname: Optional[str] diff --git a/poetry.lock b/poetry.lock index 7999a44..8c937bd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,56 +1,39 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. - -[[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = "*" -files = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] - -[[package]] -name = "astor" -version = "0.8.1" -description = "Read/rewrite/write Python ASTs" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -files = [ - {file = "astor-0.8.1-py2.py3-none-any.whl", hash = "sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5"}, - {file = "astor-0.8.1.tar.gz", hash = "sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e"}, -] +# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + [[package]] name = "cfgv" version = "3.4.0" @@ -86,63 +69,73 @@ files = [ [[package]] name = "coverage" -version = "7.3.2" +version = "7.6.9" description = "Code coverage measurement for Python" optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"}, - {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"}, - {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"}, - {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"}, - {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"}, - {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"}, - {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"}, - {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"}, - {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"}, - {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"}, - {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"}, - {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"}, - {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"}, - {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"}, - {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"}, - {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"}, - {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"}, - {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"}, - {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"}, - {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"}, - {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"}, - {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"}, - {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"}, - {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"}, - {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"}, - {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"}, - {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"}, - {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"}, +python-versions = ">=3.9" +files = [ + {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, + {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, + {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, + {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, + {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, + {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, + {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, + {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, + {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, + {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, + {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, + {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, + {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, + {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, + {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, ] [package.extras] @@ -161,24 +154,24 @@ files = [ [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.9" description = "Distribution utilities" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] name = "exceptiongroup" -version = "1.2.0" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -186,13 +179,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -200,59 +193,63 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "filelock" -version = "3.13.1" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "flake8" -version = "5.0.4" +version = "7.1.1" description = "the modular source code checker: pep8 pyflakes and co" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8.1" files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, + {file = "flake8-7.1.1-py2.py3-none-any.whl", hash = "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"}, + {file = "flake8-7.1.1.tar.gz", hash = "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38"}, ] [package.dependencies] mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" +pycodestyle = ">=2.12.0,<2.13.0" +pyflakes = ">=3.2.0,<3.3.0" [[package]] -name = "flake8-use-pathlib" -version = "0.3.0" -description = "A plugin for flake8 finding use of functions that can be replaced by pathlib module." +name = "flake8-bugbear" +version = "24.10.31" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8.1" files = [ - {file = "flake8-use-pathlib-0.3.0.tar.gz", hash = "sha256:0ef19f255a51601bcf04ff54f25ef8a466dff68210cd95b4f1db36a78ace5223"}, - {file = "flake8_use_pathlib-0.3.0-py3-none-any.whl", hash = "sha256:c7b6d71575b575f7d70ebf3f1d7f2dd6685e401d3280208f1db9dbb6bfa32608"}, + {file = "flake8_bugbear-24.10.31-py3-none-any.whl", hash = "sha256:cccf786ccf9b2e1052b1ecfa80fb8f80832d0880425bcbd4cd45d3c8128c2683"}, + {file = "flake8_bugbear-24.10.31.tar.gz", hash = "sha256:435b531c72b27f8eff8d990419697956b9fd25c6463c5ba98b3991591de439db"}, ] [package.dependencies] -flake8 = ">=3.6" +attrs = ">=22.2.0" +flake8 = ">=6.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"] [[package]] name = "identify" -version = "2.5.32" +version = "2.6.3" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.5.32-py2.py3-none-any.whl", hash = "sha256:0b7656ef6cba81664b783352c73f8c24b39cf82f926f78f4550eda928e5e0545"}, - {file = "identify-2.5.32.tar.gz", hash = "sha256:5d9979348ec1a21c768ae07e0a652924538e8bce67313a73cb0f681cf08ba407"}, + {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, + {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, ] [package.extras] @@ -271,71 +268,69 @@ files = [ [[package]] name = "ipython" -version = "8.12.3" +version = "8.18.1" description = "IPython: Productive Interactive Computing" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"}, - {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"}, + {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, + {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, ] [package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0" +prompt-toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack-data = "*" traitlets = ">=5" typing-extensions = {version = "*", markers = "python_version < \"3.10\""} [package.extras] -all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] +all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] black = ["black"] -doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] +doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] kernel = ["ipykernel"] nbconvert = ["nbconvert"] nbformat = ["nbformat"] notebook = ["ipywidgets", "notebook"] parallel = ["ipyparallel"] qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"] +test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "matplotlib-inline" -version = "0.1.6" +version = "0.1.7" description = "Inline Matplotlib backend for Jupyter" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"}, - {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"}, + {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, + {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, ] [package.dependencies] @@ -354,43 +349,40 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.1" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "23.2" +version = "24.2" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] name = "parso" -version = "0.8.3" +version = "0.8.4" description = "A Python Parser" optional = false python-versions = ">=3.6" files = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, ] [package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] [[package]] name = "pexpect" @@ -406,41 +398,31 @@ files = [ [package.dependencies] ptyprocess = ">=0.5" -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -optional = false -python-versions = "*" -files = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] - [[package]] name = "platformdirs" -version = "4.0.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -449,13 +431,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "3.5.0" +version = "4.0.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, - {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, + {file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, + {file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, ] [package.dependencies] @@ -467,13 +449,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.41" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"}, - {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] @@ -492,13 +474,13 @@ files = [ [[package]] name = "pure-eval" -version = "0.2.2" +version = "0.2.3" description = "Safely evaluate AST nodes without side effects" optional = false python-versions = "*" files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, ] [package.extras] @@ -506,50 +488,49 @@ tests = ["pytest"] [[package]] name = "pycodestyle" -version = "2.9.1" +version = "2.12.1" description = "Python style guide checker" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, + {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, + {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, ] [[package]] name = "pyflakes" -version = "2.5.0" +version = "3.2.0" description = "passive checker of Python programs" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, + {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, + {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] [[package]] name = "pygments" -version = "2.17.2" +version = "2.18.0" description = "Pygments is a syntax highlighting package written in Python." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, ] [package.extras] -plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "7.4.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -557,21 +538,21 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} +pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-flake8-path" -version = "1.5.0" +version = "1.6.0" description = "A pytest fixture for testing flake8 plugins." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest_flake8_path-1.5.0-py3-none-any.whl", hash = "sha256:8766879344883e67762fc3f4f8f0d624a0d143de71f6488788599b3583e279a9"}, - {file = "pytest_flake8_path-1.5.0.tar.gz", hash = "sha256:2c144a3127995d34e82df397350dbef7780c9920c5b7e9ebc81f63299011381e"}, + {file = "pytest_flake8_path-1.6.0-py3-none-any.whl", hash = "sha256:dafc81dc8f2ba49060a45139caa153fba291163d670c00ae20e194989b73a34e"}, + {file = "pytest_flake8_path-1.6.0.tar.gz", hash = "sha256:40bb85c806630e37097a45c4e67130b9655efbb81904a9bfa89b1e23bc2529a5"}, ] [package.dependencies] @@ -580,88 +561,64 @@ pytest = "*" [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "setuptools" -version = "69.0.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, - {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -685,50 +642,80 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "traitlets" -version = "5.13.0" +version = "5.14.3" description = "Traitlets Python configuration system" optional = false python-versions = ">=3.8" files = [ - {file = "traitlets-5.13.0-py3-none-any.whl", hash = "sha256:baf991e61542da48fe8aef8b779a9ea0aa38d8a54166ee250d5af5ecf4486619"}, - {file = "traitlets-5.13.0.tar.gz", hash = "sha256:9b232b9430c8f57288c1024b34a8f0251ddcc47268927367a0dd3eeaca40deb5"}, + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.6.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "virtualenv" -version = "20.24.7" +version = "20.28.0" description = "Virtual Python Environment builder" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, - {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, + {file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"}, + {file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"}, ] [package.dependencies] @@ -737,21 +724,21 @@ filelock = ">=3.12.2,<4" platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [[package]] name = "wcwidth" -version = "0.2.12" +version = "0.2.13" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, - {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, + {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, + {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, ] [metadata] lock-version = "2.0" -python-versions = ">=3.8" -content-hash = "fc29baa07456bcdac7221d0392dc90f69729d0222196360c4f955fb144483603" +python-versions = ">=3.9" +content-hash = "bdbc7df6445c077ca6561713374ce9544f3be73d77cbaed68b990c8b3a9e204b" diff --git a/pyproject.toml b/pyproject.toml index 95377ce..6e7a0cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,11 +17,11 @@ classifiers = [ 'License :: OSI Approved :: BSD License', 'Topic :: Software Development :: Quality Assurance', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Typing :: Typed', ] @@ -29,10 +29,10 @@ classifiers = [ "Releases" = "https://github.com/snok/flake8-type-checking/releases" [tool.poetry.dependencies] -python = '>=3.8' +python = '>=3.9' flake8 = '*' -astor = {python = "<3.9", version = "*"} classify-imports = '*' +flake8-bugbear = "^24.10.31" [tool.poetry.dev-dependencies] pytest = '*' @@ -44,9 +44,6 @@ pytest-flake8-path = '*' [tool.poetry.plugins.'flake8.extension'] TC = 'flake8_type_checking.plugin:Plugin' -[tool.poetry.group.dev.dependencies] -flake8-use-pathlib = "^0.3.0" - [build-system] requires = ['poetry-core>=1.0.0'] build-backend = 'poetry.core.masonry.api' @@ -73,3 +70,16 @@ exclude_lines = [ 'if TYPE_CHECKING:', 'pragma: no cover' ] + +[tool.mypy] +python_version = 3.9 +strict = true +warn_redundant_casts = true +warn_unused_configs = true +warn_return_any = true +show_error_codes = true +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = ["tests.*"] +disallow_untyped_defs = false diff --git a/setup.cfg b/setup.cfg index bc210df..dfef943 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,15 +31,3 @@ per-file-ignores = flake8_type_checking/checker.py:SIM114 flake8_type_checking/types.py:D tests/**.py:D103,D205,D400,D102,D101 - -[mypy] -python_version = 3.8 -strict = True -warn_redundant_casts = True -warn_unused_configs = True -warn_return_any = True -show_error_codes = True -ignore_missing_imports = True - -[mypy-tests.*] -disallow_untyped_defs = False diff --git a/tests/test_attrs.py b/tests/test_attrs.py index 9df014b..7abaf38 100644 --- a/tests/test_attrs.py +++ b/tests/test_attrs.py @@ -28,7 +28,8 @@ def test_attrs_model(imp, dec): Test `attrs` classes together with a non-`attrs` class that has a class var of the same type. `attrs` classes are instantiated using different dataclass decorators. The `attrs` module is imported as whole. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' {imp} from decimal import Decimal @@ -42,7 +43,8 @@ class Y: class Z: x: Decimal - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003') == set() @@ -63,7 +65,8 @@ def test_complex_attrs_model(imp, dec, expected): Test `attrs` classes together with a non-`attrs` class tha has a class var of another type. `attrs` classes are instantiated using different dataclass decorators. The `attrs` module is imported as whole. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' {imp} from decimals import Decimal from decimal import Context @@ -78,7 +81,8 @@ class Y: class Z: x: Context - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003') == expected @@ -99,7 +103,8 @@ def test_complex_attrs_model_direct_import(imp, dec, expected): Test `attrs` classes together with a non-`attrs` class tha has a class var of another type. `attrs` classes are instantiated using different dataclass decorators which are imported as submodules. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' {imp} from decimals import Decimal from decimal import Context @@ -114,7 +119,8 @@ class Y: class Z: x: Context - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003') == expected @@ -144,7 +150,8 @@ def test_complex_attrs_model_as_import(imp, dec, expected): `attrs` classes are instantiated using different dataclass decorators which are imported as submodules using an alias. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' {imp} from decimals import Decimal from decimal import Context @@ -159,7 +166,8 @@ class Y: class Z: x: Context - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003') == expected @@ -185,7 +193,8 @@ def test_complex_attrs_model_slots_frozen(imp, dec, expected): Test `attrs` classes together with a non-`attrs` class tha has a class var of another type. `attrs` classes are instantiated using different dataclass decorators and arguments. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' {imp} from decimals import Decimal from decimal import Context @@ -200,5 +209,6 @@ class Y: class Z: x: Context - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003') == expected diff --git a/tests/test_errors.py b/tests/test_errors.py index 3d57672..9050a68 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -10,13 +10,15 @@ class TestFoundBugs: def test_mixed_errors(self): - example = textwrap.dedent(f""" + example = textwrap.dedent( + f""" import {mod} import pytest from x import y x: {mod} | pytest | y - """) + """ + ) assert _get_error(example) == { f"2:0 {TC001.format(module=f'{mod}')}", '3:0 ' + TC002.format(module='pytest'), @@ -24,7 +26,8 @@ def test_mixed_errors(self): } def test_type_checking_block_imports_dont_generate_errors(self): - example = textwrap.dedent(""" + example = textwrap.dedent( + """ import x from y import z @@ -37,7 +40,8 @@ def test_type_checking_block_imports_dont_generate_errors(self): def test(foo: z, bar: x): pass - """) + """ + ) assert _get_error(example) == { '2:0 ' + TC002.format(module='x'), '3:0 ' + TC002.format(module='y.z'), @@ -48,7 +52,8 @@ def test_model_declarations_dont_trigger_error(self): Initially found false positives in Django project, because name visitor did not capture the SomeModel usage in the example below. """ - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from django.db import models from app.models import SomeModel @@ -57,12 +62,14 @@ class LoanProvider(models.Model): SomeModel, on_delete=models.CASCADE, ) - """) + """ + ) assert _get_error(example) == set() def test_all_list_declaration(self): """__all__ declarations originally generated false positives.""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from app.models import SomeModel from another_app.models import AnotherModel @@ -70,12 +77,14 @@ def test_all_list_declaration(self): 'SomeModel', 'AnotherModel' ] - """) + """ + ) assert _get_error(example) == set() def test_all_tuple_declaration(self): """__all__ declarations originally generated false positives.""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from app.models import SomeModel from another_app.models import AnotherModel @@ -83,12 +92,14 @@ def test_all_tuple_declaration(self): 'SomeModel', 'AnotherModel' ) - """) + """ + ) assert _get_error(example) == set() def test_callable_import(self): """__all__ declarations originally generated false positives.""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from x import y class X: @@ -96,25 +107,31 @@ def __init__(self): self.all_sellable_models: list[CostModel] = y( country=self.country ) - """) + """ + ) assert _get_error(example) == set() def test_ellipsis(self): - example = textwrap.dedent(""" + example = textwrap.dedent( + """ x: Tuple[str, ...] - """) + """ + ) assert _get_error(example) == set() def test_literal(self): - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from __future__ import annotations x: Literal['string'] - """) + """ + ) assert _get_error(example) == set() def test_conditional_import(self): - example = textwrap.dedent(""" + example = textwrap.dedent( + """ version = 2 if version == 2: @@ -123,7 +140,8 @@ def test_conditional_import(self): import y as x var: x - """) + """ + ) assert _get_error(example) == {"7:4 TC002 Move third-party import 'x' into a type-checking block"} def test_type_checking_block_formats_detected(self): @@ -309,7 +327,8 @@ def test_tc004_false_positive(self, example): def test_tc002_false_positive(self): """Re https://github.com/snok/flake8-type-checking/issues/120.""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from logging import INFO from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR @@ -317,7 +336,8 @@ def test_tc002_false_positive(self): class C: level: int = INFO status: int = HTTP_500_INTERNAL_SERVER_ERROR - """) + """ + ) assert _get_error(example) == set() def test_tc001_false_positive(self): @@ -326,19 +346,27 @@ def test_tc001_false_positive(self): def test_works_with_other_plugins(self, flake8_path): """Re https://github.com/snok/flake8-type-checking/issues/139.""" - (flake8_path / 'example.py').write_text(textwrap.dedent(''' - import os - - t = os.path.dirname(os.path.realpath(__file__)) - ''')) + (flake8_path / 'example.py').write_text( + textwrap.dedent( + ''' + def this_is_buggy(n): + x = ++n + return x + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [ - './example.py:4:5: PL120 os.path.dirname("foo/bar") should be replaced by bar_path.parent', + './example.py:3:9: B002 Python does not support the unary prefix increment. ' + 'Writing ++n is equivalent to +(+(n)), which equals n. You meant n += 1.', ] def test_shadowed_function_arg(self): """Re https://github.com/snok/flake8-type-checking/issues/160.""" - assert _get_error(textwrap.dedent(''' + assert ( + _get_error( + textwrap.dedent( + ''' from __future__ import annotations from typing import TYPE_CHECKING @@ -348,4 +376,8 @@ def test_shadowed_function_arg(self): def create(request: request.Request) -> None: str(request) - ''')) == set() + ''' + ) + ) + == set() + ) diff --git a/tests/test_exempt_modules.py b/tests/test_exempt_modules.py index 77286c9..10d3701 100644 --- a/tests/test_exempt_modules.py +++ b/tests/test_exempt_modules.py @@ -10,23 +10,27 @@ def test_exempt_modules_option(): which is meant to passlist certain modules from TC001 and TC002 errors. """ # Check that typing is passlisted when exempted - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from typing import TYPE_CHECKING from pandas import DataFrame x: DataFrame - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002') == {'3:0 ' + TC002.format(module='pandas.DataFrame')} assert _get_error(example, error_code_filter='TC002', type_checking_exempt_modules=['pandas']) == set() # Check that other basic errors are still caught - example2 = textwrap.dedent(''' + example2 = textwrap.dedent( + ''' from typing import TYPE_CHECKING from pandas import DataFrame from a import B x: Callable[[DataFrame, B], List] - ''') + ''' + ) assert _get_error(example2, error_code_filter='TC002') == { '3:0 ' + TC002.format(module='pandas.DataFrame'), '4:0 ' + TC002.format(module='a.B'), @@ -36,22 +40,26 @@ def test_exempt_modules_option(): } # Check Import - example3 = textwrap.dedent(''' + example3 = textwrap.dedent( + ''' import pandas x: pandas.DataFrame - ''') + ''' + ) assert _get_error(example3, error_code_filter='TC002') == {'2:0 ' + TC002.format(module='pandas')} assert _get_error(example3, error_code_filter='TC002', type_checking_exempt_modules=['pandas']) == set() # Check template Import - example4 = textwrap.dedent(''' + example4 = textwrap.dedent( + ''' from apps.app_1.choices import ExampleChoice from apps.app_2.choices import Example2Choice x: ExampleChoice y: Example2Choice - ''') + ''' + ) assert _get_error(example4, error_code_filter='TC002') == { '2:0 ' + TC002.format(module='apps.app_1.choices.ExampleChoice'), '3:0 ' + TC002.format(module='apps.app_2.choices.Example2Choice'), diff --git a/tests/test_fastapi_decorators.py b/tests/test_fastapi_decorators.py index f4f997e..a47746d 100644 --- a/tests/test_fastapi_decorators.py +++ b/tests/test_fastapi_decorators.py @@ -18,7 +18,8 @@ @pytest.mark.parametrize('fdef', ['def', 'async def']) def test_api_router_decorated_function(fdef): """Test sync and async function definition, with an arg and a kwarg.""" - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from fastapi import APIRouter from app.models import SomeModel @@ -30,7 +31,8 @@ def test_api_router_decorated_function(fdef): @some_router.get('/{{resource_id}}') {fdef} list_something(resource_id: CustomType, some_model: SomeModel = Depends(some_function)): return None - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003', **defaults) == set() @@ -40,7 +42,8 @@ def test_api_router_decorated_function_return_type(fdef): We don't care about return types. To my knowledge, these are not evaluated by FastAPI/pydantic. """ - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from fastapi import APIRouter from fastapi import Request @@ -51,7 +54,8 @@ def test_api_router_decorated_function_return_type(fdef): @some_router.get('/{{resource_id}}') {fdef} list_something(request: Request) -> CustomType: return None - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003', **defaults) == { '5:0 ' + TC002.format(module='app.types.CustomType') } @@ -59,7 +63,8 @@ def test_api_router_decorated_function_return_type(fdef): @pytest.mark.parametrize('fdef', ['def', 'async def']) def test_api_router_decorated_nested_function(fdef): - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' import logging from typing import TYPE_CHECKING @@ -79,13 +84,15 @@ def get_auth_router() -> APIRouter: {fdef} login(request: Request) -> "RedirectResponse": ... - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003', **defaults) == set() @pytest.mark.parametrize('fdef', ['def', 'async def']) def test_app_decorated_function(fdef): - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from app.main import app from app.models import SomeModel from app.types import CustomType @@ -93,5 +100,6 @@ def test_app_decorated_function(fdef): @app.get('/{{resource_id}}') {fdef} list_something(resource_id: CustomType, some_model: SomeModel = Depends(lambda: 1)): return None - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003', **defaults) == set() diff --git a/tests/test_fastapi_dependencies.py b/tests/test_fastapi_dependencies.py index 412715e..253f28f 100644 --- a/tests/test_fastapi_dependencies.py +++ b/tests/test_fastapi_dependencies.py @@ -16,7 +16,8 @@ @pytest.mark.parametrize('fdef', ['def', 'async def']) def test_api_router_decorated_function(fdef): """Test sync and async function definition, with an arg and a kwarg.""" - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from fastapi import APIRouter from app.models import SomeModel @@ -27,7 +28,8 @@ def test_api_router_decorated_function(fdef): {fdef} list_something(resource_id: CustomType, some_model: SomeModel = Depends(some_function)): return None - ''') + ''' + ) assert _get_error(example, error_code_filter='TC001,TC002,TC003', type_checking_fastapi_enabled=True) == { '4:0 ' + TC002.format(module='app.models.SomeModel'), '6:0 ' + TC002.format(module='app.types.CustomType'), diff --git a/tests/test_import_visitors.py b/tests/test_import_visitors.py index 48ea239..5be3dce 100644 --- a/tests/test_import_visitors.py +++ b/tests/test_import_visitors.py @@ -9,7 +9,7 @@ from tests.conftest import REPO_ROOT if TYPE_CHECKING: - from typing import Callable, List, Tuple + from typing import Callable def _visit(example: str) -> ImportVisitor: @@ -83,7 +83,7 @@ def _get_built_in_imports(example: str) -> list[str]: ('\nfrom _pytest.config import argparsing', ['_pytest.config.argparsing'], f), ] -typing_block_imports: List[Tuple[str, list[str], Callable[[str], list[str]]]] = [ +typing_block_imports: list[tuple[str, list[str], Callable[[str], list[str]]]] = [ (f'if TYPE_CHECKING:\n\t{example}', [], f) for _list in [stdlib_imports, venv_imports] for example, expected, f in _list[:-1] diff --git a/tests/test_injector.py b/tests/test_injector.py index c306daa..6ea5f75 100644 --- a/tests/test_injector.py +++ b/tests/test_injector.py @@ -17,13 +17,15 @@ ) def test_non_pydantic_model(enabled, expected): """A class does not use injector, so error should be risen in both scenarios.""" - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from services import Service class X: def __init__(self, service: Service) -> None: self.service = service - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=enabled) == expected @@ -36,14 +38,16 @@ def __init__(self, service: Service) -> None: ) def test_injector_option(enabled, expected): """When an injector option is enabled, injector should be ignored.""" - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from injector import Inject from services import Service class X: def __init__(self, service: Inject[Service]) -> None: self.service = service - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_injector_enabled=enabled) == expected @@ -63,7 +67,8 @@ def __init__(self, service: Inject[Service]) -> None: ) def test_injector_option_only_allows_injected_dependencies(enabled, expected): """Whenever an injector option is enabled, only injected dependencies should be ignored.""" - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from injector import Inject from services import Service from other_dependency import OtherDependency @@ -72,7 +77,8 @@ class X: def __init__(self, service: Inject[Service], other: OtherDependency) -> None: self.service = service self.other = other - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_injector_enabled=enabled) == expected @@ -95,7 +101,8 @@ def test_injector_option_only_allows_injector_slices(enabled, expected): Whenever an injector option is enabled, only injected dependencies should be ignored, not any dependencies with slices. """ - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from injector import Inject from services import Service from other_dependency import OtherDependency @@ -104,7 +111,8 @@ class X: def __init__(self, service: Inject[Service], other_deps: list[OtherDependency]) -> None: self.service = service self.other_deps = other_deps - """) + """ + ) assert _get_error(example, error_code_filter='TC002', type_checking_injector_enabled=enabled) == expected @@ -117,14 +125,16 @@ def __init__(self, service: Inject[Service], other_deps: list[OtherDependency]) ) def test_injector_option_allows_injector_as_module(enabled, expected): """Whenever an injector option is enabled, injected dependencies should be ignored, even if import as module.""" - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' import injector from services import Service class X: def __init__(self, service: injector.Inject[Service]) -> None: self.service = service - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_injector_enabled=enabled) == expected @@ -137,7 +147,8 @@ def __init__(self, service: injector.Inject[Service]) -> None: ) def test_injector_option_only_mentioned_second_time(enabled, expected): """Whenever an injector option is enabled, dependency referenced second time is accepted.""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from injector import Inject from services import Service @@ -145,5 +156,6 @@ class X: def __init__(self, service: Inject[Service], other_deps: list[Service]) -> None: self.service = service self.other_deps = other_deps - """) + """ + ) assert _get_error(example, error_code_filter='TC002', type_checking_injector_enabled=enabled) == expected diff --git a/tests/test_name_extraction.py b/tests/test_name_extraction.py index e00aac2..7f898c9 100644 --- a/tests/test_name_extraction.py +++ b/tests/test_name_extraction.py @@ -28,9 +28,11 @@ ] if sys.version_info >= (3, 11): - examples.extend([ - ('*Ts', {'Ts'}), - ]) + examples.extend( + [ + ('*Ts', {'Ts'}), + ] + ) @pytest.mark.parametrize(('example', 'expected'), examples) diff --git a/tests/test_name_visitor.py b/tests/test_name_visitor.py index 4e85ae1..65708ae 100644 --- a/tests/test_name_visitor.py +++ b/tests/test_name_visitor.py @@ -45,16 +45,19 @@ def _get_names_and_soft_uses(example: str) -> tuple[set[str], set[str]]: # Attribute ('x.y', {'x.y', 'x'}, set()), ( - textwrap.dedent(""" + textwrap.dedent( + """ def example(c): a = 2 b = c * 2 - """), + """ + ), {'a', 'b', 'c'}, set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ class Test: x = 13 @@ -63,43 +66,51 @@ def __init__(self, z): a = Test() b = a.y - """), + """ + ), {'self.y', 'z', 'Test', 'self', 'a', 'b', 'x', 'a.y'}, set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ import ast ImportType = Union[Import, ImportFrom] - """), # ast should not be a part of this + """ + ), # ast should not be a part of this {'Union', 'Import', 'ImportFrom', 'ImportType'}, set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ import ast def _get_usages(example): visitor = UnusedImportVisitor() visitor.visit(parse(example)) return visitor.usage_names - """), + """ + ), {'UnusedImportVisitor', 'example', 'parse', 'visitor', 'visitor.usage_names', 'visitor.visit'}, set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import Annotated from foo import Gt x: Annotated[int, Gt(5)] - """), + """ + ), {'Gt'}, {'int'}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from __future__ import annotations from typing import Annotated @@ -107,26 +118,31 @@ def _get_usages(example): from foo import Gt x: Annotated[int, Gt(5)] - """), + """ + ), set(), {'Gt', 'int'}, ), ] if sys.version_info >= (3, 12): - examples.extend([ - ( - textwrap.dedent(""" + examples.extend( + [ + ( + textwrap.dedent( + """ from typing import Annotated from foo import Gt type x = Annotated[int, Gt(5)] - """), - set(), - {'Gt', 'int'}, - ), - ]) + """ + ), + set(), + {'Gt', 'int'}, + ), + ] + ) @pytest.mark.parametrize(('example', 'result', 'soft_uses'), examples) @@ -136,7 +152,8 @@ def test_basic_annotations_are_removed(example, result, soft_uses): def test_model_declarations_are_included_in_names(): """Class definition arguments need to be included in our "names".""" - example = textwrap.dedent(""" + example = textwrap.dedent( + """ from django.db import models from app.models import SomeModel @@ -145,7 +162,8 @@ class LoanProvider(models.Model): SomeModel, on_delete=models.CASCADE, ) - """) + """ + ) assert _get_names_and_soft_uses(example) == ( {'SomeModel', 'fk', 'models', 'models.CASCADE', 'models.ForeignKey', 'models.Model'}, set(), diff --git a/tests/test_pydantic.py b/tests/test_pydantic.py index 0f8a716..ec68fe6 100644 --- a/tests/test_pydantic.py +++ b/tests/test_pydantic.py @@ -25,12 +25,14 @@ def test_non_pydantic_model(enabled, expected): A class cannot be a pydantic model if it doesn't have a base class, so we should raise the same error here in both cases. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from pandas import DataFrame class X: x: DataFrame - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=enabled) == expected @@ -40,18 +42,21 @@ def test_class_with_base_class(): to assume it might be a pydantic model, for which we need to register annotations as uses. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from pandas import DataFrame class X(Y): x: DataFrame - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=True) == set() def test_complex_pydantic_model(): """Test actual Pydantic models, with different annotation types.""" - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from __future__ import annotations from datetime import datetime @@ -95,21 +100,24 @@ class FinalModel(ModelBase): f: NestedModel g: condecimal(ge=Decimal(0)) = Decimal(0) - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=True) == set() @pytest.mark.parametrize('c', ['NamedTuple', 'TypedDict']) def test_type_checking_pydantic_enabled_baseclass_passlist(c): """Test that named tuples are not ignored.""" - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from typing import {c} from x import Y, Z class ModelBase({c}): a: Y[str] b: Z[int] - ''') + ''' + ) assert _get_error( example, error_code_filter='TC002', @@ -124,33 +132,38 @@ class ModelBase({c}): @pytest.mark.parametrize('f', ['def', 'async def']) def test_type_checking_pydantic_enabled_validate_arguments_decorator(f): """Test that @validate_argument-decorated functions have their annotations ignored.""" - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from pydantic import validate_arguments from x import Y, Z @validate_arguments {f} f(y: Y) -> Z: pass - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=True) == set() @pytest.mark.parametrize('f', ['def', 'async def']) def test_type_checking_pydantic_enabled_validate_arguments_decorator_alias(f): - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from pydantic import validate_arguments as va from x import Y, Z @va {f} f(y: Y) -> Z: pass - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=True) == set() @pytest.mark.parametrize('f', ['def', 'async def']) def test_type_checking_pydantic_enabled_validate_arguments_decorator_method(f): - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from pydantic import validate_arguments from x import Y, Z @@ -158,5 +171,6 @@ class Test: @validate_arguments {f} f(self, y: Y) -> Z: pass - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_pydantic_enabled=True) == set() diff --git a/tests/test_should_warn.py b/tests/test_should_warn.py index 20d62a3..e357503 100644 --- a/tests/test_should_warn.py +++ b/tests/test_should_warn.py @@ -20,11 +20,15 @@ def test_version(flake8_path): def test_tc_is_enabled_with_config(flake8_path): (flake8_path / 'setup.cfg').write_text('[flake8]\nselect = TC') - (flake8_path / 'example.py').write_text(dedent(''' + (flake8_path / 'example.py').write_text( + dedent( + ''' from x import Y x: Y[str, int] = 1 - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [ f".{os.sep}example.py:2:1: TC002 Move third-party import 'x.Y' into a type-checking block" @@ -33,59 +37,79 @@ def test_tc_is_enabled_with_config(flake8_path): def test_tc1_and_tc2_are_disabled_by_default(flake8_path): (flake8_path / 'setup.cfg').write_text('') - (flake8_path / 'example.py').write_text(dedent(''' + (flake8_path / 'example.py').write_text( + dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Union x: Union[str, int] - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [] def test_tc1_and_tc2_are_disabled_by_default_when_tc_is_enabled(flake8_path): (flake8_path / 'setup.cfg').write_text('[flake8]\nselect = TC') - (flake8_path / 'example.py').write_text(dedent(''' + (flake8_path / 'example.py').write_text( + dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Union x: Union[str, int] - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [] def test_tc1_works_when_opted_in(flake8_path): (flake8_path / 'setup.cfg').write_text('[flake8]\nselect = TC1') - (flake8_path / 'example.py').write_text(dedent(''' + (flake8_path / 'example.py').write_text( + dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Union x: Union[str, int] - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [f".{os.sep}example.py:1:1: TC100 Add 'from __future__ import annotations' import"] def test_tc2_works_when_opted_in(flake8_path): - (flake8_path / 'setup.cfg').write_text(dedent("""\ + (flake8_path / 'setup.cfg').write_text( + dedent( + """\ [flake8] select = TC2 - """)) - (flake8_path / 'example.py').write_text(dedent(''' + """ + ) + ) + (flake8_path / 'example.py').write_text( + dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Union x: Union[str, int] - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [ f".{os.sep}example.py:7:4: TC200 Annotation 'Union' needs to be made into a string literal" @@ -93,9 +117,13 @@ def test_tc2_works_when_opted_in(flake8_path): def test_pyi_ignored(flake8_path): - (flake8_path / 'example.pyi').write_text(dedent(''' + (flake8_path / 'example.pyi').write_text( + dedent( + ''' import pandas x: pandas.DataFrame - ''')) + ''' + ) + ) result = flake8_path.run_flake8() assert result.out_lines == [] diff --git a/tests/test_sqlalchemy.py b/tests/test_sqlalchemy.py index 163fbd7..52a27ff 100644 --- a/tests/test_sqlalchemy.py +++ b/tests/test_sqlalchemy.py @@ -25,13 +25,15 @@ def test_simple_mapped_use(enabled, expected): Mapped itself must be available at runtime and the inner type may or may not need to be available at runtime. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from foo import Bar from sqlalchemy.orm import Mapped class User: x: Mapped[Bar] - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_sqlalchemy_enabled=enabled) == expected @@ -49,13 +51,15 @@ class User: ) def test_default_mapped_names(name, expected): """Check the three default names and a bogus name.""" - example = textwrap.dedent(f''' + example = textwrap.dedent( + f''' from foo import Bar from sqlalchemy.orm import {name} class User: x: {name}[Bar] - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_sqlalchemy_enabled=True) == expected @@ -64,14 +68,16 @@ def test_mapped_with_circular_forward_reference(): Mapped must still be available at runtime even with forward references to a different model. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from sqlalchemy.orm import Mapped if TYPE_CHECKING: from .address import Address class User: address: Mapped['Address'] - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_sqlalchemy_enabled=True) == set() @@ -81,7 +87,8 @@ def test_mapped_soft_uses(): as such we can't trigger a TC002 here, despite the only uses being inside type annotations. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from foo import Bar, Gt from sqlalchemy.orm import Mapped from typing import Annotated @@ -90,7 +97,8 @@ class User: number: Mapped[Annotated[Bar, Gt(2)]] bar: Bar validator: Gt - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_sqlalchemy_enabled=True) == set() @@ -99,14 +107,16 @@ def test_mapped_use_without_runtime_import(): Mapped must be available at runtime, so even if it is inside a wrapped annotation we should raise a TC004 for Mapped but not for Bar """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' if TYPE_CHECKING: from foo import Bar from sqlalchemy.orm import Mapped class User: created: 'Mapped[Bar]' - ''') + ''' + ) assert _get_error(example, error_code_filter='TC004', type_checking_sqlalchemy_enabled=True) == { '4:0 ' + TC004.format(module='Mapped') } @@ -117,7 +127,8 @@ def test_custom_mapped_dotted_names_unwrapped(): Check a couple of custom dotted names and a bogus one. This also tests the various styles of imports """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' import a import a.b as ab from a import b @@ -134,7 +145,8 @@ class User: x: b.MyMapped[Bar] y: a.b.MyMapped[Bar] z: ab.MyMapped[Bar] - ''') + ''' + ) assert _get_error( example, error_code_filter='TC002', @@ -149,7 +161,8 @@ def test_custom_mapped_dotted_names_wrapped(): Same as the unwrapped test but with wrapped annotations. This should generate a bunch of TC004 errors for the uses of mapped that should be available at runtime. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' if TYPE_CHECKING: import a import a.b as ab @@ -167,7 +180,8 @@ class User: x: 'b.MyMapped[Bar]' y: 'a.b.MyMapped[Bar]' z: 'ab.MyMapped[Bar]' - ''') + ''' + ) assert _get_error( example, error_code_filter='TC004', diff --git a/tests/test_strict.py b/tests/test_strict.py index 7c50990..ac00f09 100644 --- a/tests/test_strict.py +++ b/tests/test_strict.py @@ -9,12 +9,14 @@ def test_strict_mode(): Assert that imports are flagged for TC00[1-3] on a per-module basis by default, but individually when --type-checking-strict is set to true. """ - example = textwrap.dedent(''' + example = textwrap.dedent( + ''' from x import Y, Z a = Y b: Z - ''') + ''' + ) assert _get_error(example, error_code_filter='TC002', type_checking_strict=False) == set() assert _get_error(example, error_code_filter='TC002', type_checking_strict=True) == { '2:0 ' + TC002.format(module='x.Z') diff --git a/tests/test_tc001_to_tc003.py b/tests/test_tc001_to_tc003.py index ecebd25..e0dcd57 100644 --- a/tests/test_tc001_to_tc003.py +++ b/tests/test_tc001_to_tc003.py @@ -11,14 +11,13 @@ import sys import textwrap -from typing import List, Set, Tuple import pytest from flake8_type_checking.constants import TC001, TC002, TC003 from tests.conftest import _get_error, mod -L = List[Tuple[str, Set[str]]] +L = list[tuple[str, set[str]]] def get_tc_001_to_003_tests(import_: str, ERROR: str) -> L: @@ -43,11 +42,13 @@ def get_tc_001_to_003_tests(import_: str, ERROR: str) -> L: if ERROR == TC001: # test relative imports - no_use.extend([ - ('from .. import Plugin\nx:Plugin', {'1:0 ' + ERROR.format(module='..Plugin')}), - ('\n\nfrom .foo import constants\nx:constants', {'3:0 ' + ERROR.format(module='.foo.constants')}), - ('from . import constants as x\ny:x', {'1:0 ' + ERROR.format(module='x')}), - ]) + no_use.extend( + [ + ('from .. import Plugin\nx:Plugin', {'1:0 ' + ERROR.format(module='..Plugin')}), + ('\n\nfrom .foo import constants\nx:constants', {'3:0 ' + ERROR.format(module='.foo.constants')}), + ('from . import constants as x\ny:x', {'1:0 ' + ERROR.format(module='x')}), + ] + ) # These imports are all used. None should generate errors. used: L = [ @@ -123,7 +124,8 @@ def get_tc_001_to_003_tests(import_: str, ERROR: str) -> L: # Imports used for `functools.singledispatch`. None of these should generate errors. used_for_singledispatch: L = [ ( - textwrap.dedent(f''' + textwrap.dedent( + f''' import functools from {import_} import Dict, Any @@ -131,11 +133,13 @@ def get_tc_001_to_003_tests(import_: str, ERROR: str) -> L: @functools.singledispatch def foo(arg: Dict[str, Any]) -> Any: return 1 - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from functools import singledispatch from {import_} import Dict, Any @@ -143,11 +147,13 @@ def foo(arg: Dict[str, Any]) -> Any: @singledispatch def foo(arg: Dict[str, Any]) -> Any: return 1 - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from functools import singledispatchmethod from {import_} import Dict, Any @@ -156,36 +162,42 @@ class Foo: @singledispatchmethod def foo(self, arg: Dict[str, Any]) -> Any: return 1 - '''), + ''' + ), set(), ), ] other_useful_test_cases: L = [ ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from {import_} import Dict, Any def example() -> Any: return 1 x: Dict[int] = 20 - '''), + ''' + ), {'2:0 ' + ERROR.format(module=f'{import_}.Dict'), '2:0 ' + ERROR.format(module=f'{import_}.Any')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Dict x: Dict[int] = 20 - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from pathlib import Path class ImportVisitor(ast.NodeTransformer): @@ -199,75 +211,118 @@ class ExampleClass: def __init__(self): self.cwd = Path(pandas.getcwd()) - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' import {import_} class Migration: enum={import_} - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' import {import_} class Migration: enum={import_}.EnumClass - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from {import_} import y if TYPE_CHECKING: _type = x else: _type = y - '''), + ''' + ), set(), ), ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from {import_} import y if TYPE_CHECKING: _type = x elif True: _type = y - '''), + ''' + ), set(), ), # Annotated soft use ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from typing import Annotated from {import_} import Depends x: Annotated[str, Depends] y: Depends - '''), + ''' + ), set(), ), # This is not a soft-use, it's just a plain string ( - textwrap.dedent(f''' + textwrap.dedent( + f''' from typing import Annotated from {import_} import Depends x: Annotated[str, "Depends"] y: Depends - '''), + ''' + ), {'4:0 ' + ERROR.format(module=f'{import_}.Depends')}, ), + # global + ( + textwrap.dedent( + f''' + from {import_} import Dict + + def example() -> Any: + global Dict + x = Dict[int] # runtime use of Dict import + + x: Dict[int] = 20 + ''' + ), + set(), + ), + # nonlocal + ( + textwrap.dedent( + f''' + from {import_} import Dict + + def example() -> Any: + nonlocal Dict + x = Dict[int] # runtime use of Dict import + + x: Dict[int] = 20 + ''' + ), + set(), + ), ] return [ diff --git a/tests/test_tc004.py b/tests/test_tc004.py index 0653950..bd4a28b 100644 --- a/tests/test_tc004.py +++ b/tests/test_tc004.py @@ -17,19 +17,22 @@ ('', set()), # Used in file ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: from datetime import datetime x = datetime - """), + """ + ), {'5:0 ' + TC004.format(module='datetime')}, ), # Used in function ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -37,22 +40,26 @@ def example(): return date() - """), + """ + ), {'5:0 ' + TC004.format(module='date')}, ), # Used, but only used inside the type checking block ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from typing import Any CustomType = Any - """), + """ + ), set(), ), # Used for typing only ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from typing import Any @@ -60,33 +67,39 @@ def example(*args: Any, **kwargs: Any): return my_type: Type[Any] | Any - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from typing import List, Sequence, Set def example(a: List[int], /, b: Sequence[int], *, c: Set[int]): return - """), + """ + ), set(), ), # Used different places, but where each function scope has it's own import ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from pandas import DataFrame def example(): from pandas import DataFrame x = DataFrame - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from __future__ import annotations from typing import TYPE_CHECKING @@ -99,11 +112,13 @@ class Example: async def example(self) -> AsyncIterator[List[str]]: yield 0 - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING from weakref import WeakKeyDictionary @@ -112,11 +127,13 @@ async def example(self) -> AsyncIterator[List[str]]: d = WeakKeyDictionary["Any", "Any"]() - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: import a import b @@ -125,13 +142,15 @@ async def example(self) -> AsyncIterator[List[str]]: def test_function(a, /, b, *, c, **d): print(a, b, c, d) - """), + """ + ), set(), ), # Regression test for #131 # handle scopes correctly ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from a import Foo @@ -152,14 +171,16 @@ class X: bar: Foo = Foo() - """), + """ + ), set(), ), # Inverse Regression test for #131 # handle scopes correctly, so we should get an error for the imports # in the inner scopes, but not one for the outer scope. ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from a import Foo @@ -176,7 +197,8 @@ class X: bar: Foo = Foo() - """), + """ + ), { '7:0 ' + TC004.format(module='Foo'), '14:0 ' + TC004.format(module='Foo'), @@ -193,7 +215,8 @@ class X: # special rules (such as being able to access enclosing class scopes) # so it's either to not treat them as separate scopes for now. ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from foo import v, w, x, y, z @@ -203,12 +226,14 @@ class X: {{y: baz for y, bar in foo for baz in y}} foo = z if (z := bar) else None - """), + """ + ), set(), ), # Inverse test for complex cases ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from foo import v, w, x, y, z @@ -218,7 +243,8 @@ class X: {{a: y for a in foo}} x = foo if (foo := z) else None - """), + """ + ), { '3:0 ' + TC004.format(module='v'), '3:0 ' + TC004.format(module='w'), @@ -229,7 +255,8 @@ class X: ), # functools.singledispatch ( - textwrap.dedent(""" + textwrap.dedent( + """ import functools if TYPE_CHECKING: @@ -238,11 +265,13 @@ class X: @functools.singledispatch def foo(arg: FooType) -> int: return 1 - """), + """ + ), {'5:0 ' + TC004.format(module='FooType')}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from functools import singledispatch if TYPE_CHECKING: @@ -251,11 +280,13 @@ def foo(arg: FooType) -> int: @functools.singledispatch def foo(arg: FooType) -> int: return 1 - """), + """ + ), {'5:0 ' + TC004.format(module='FooType')}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from functools import singledispatchmethod if TYPE_CHECKING: @@ -265,7 +296,8 @@ class Foo: @functools.singledispatch def foo(self, arg: FooType) -> int: return 1 - """), + """ + ), {'5:0 ' + TC004.format(module='FooType')}, ), ] diff --git a/tests/test_tc005.py b/tests/test_tc005.py index 885f7ff..51f2ac5 100644 --- a/tests/test_tc005.py +++ b/tests/test_tc005.py @@ -19,56 +19,66 @@ ('', set()), # Found in file ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: pass - """), + """ + ), {'4:0 ' + TC005}, ), # Found in function ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING def example(): if TYPE_CHECKING: pass return - """), + """ + ), {'5:0 ' + TC005}, ), # Found in class ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING class Test: if TYPE_CHECKING: pass x = 2 - """), + """ + ), {'5:0 ' + TC005}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: if 2: pass - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING from typing import List if TYPE_CHECKING: x: List - """), + """ + ), set(), ), ] diff --git a/tests/test_tc006.py b/tests/test_tc006.py index 59c36f3..7cfd92e 100644 --- a/tests/test_tc006.py +++ b/tests/test_tc006.py @@ -21,108 +21,132 @@ ('', set()), # Simple type unquoted ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import cast cast(int, 3.0) - """), + """ + ), {'4:5 ' + TC006.format(annotation='int')}, ), # Complex type unquoted ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import cast cast(list[tuple[bool | float | int | str]], 3.0) - """), + """ + ), {'4:5 ' + TC006.format(annotation='list[tuple[bool | float | int | str]]')}, ), # Complex type unquoted using Union ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import Union, cast cast(list[tuple[Union[bool, float, int, str]]], 3.0) - """), + """ + ), {'4:5 ' + TC006.format(annotation='list[tuple[Union[bool, float, int, str]]]')}, ), # Simple type quoted ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import cast cast("int", 3.0) - """), + """ + ), set(), ), # Complex type quoted ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import cast cast("list[tuple[bool | float | int | str]]", 3.0) - """), + """ + ), set(), ), # Complex type quoted using Union ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import Union, cast cast("list[tuple[Union[bool, float, int, str]]]", 3.0) - """), + """ + ), set(), ), # Call aliased function ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import cast as typecast typecast(int, 3.0) - """), + """ + ), {'4:9 ' + TC006.format(annotation='int')}, ), # Call function from module ( - textwrap.dedent(""" + textwrap.dedent( + """ import typing typing.cast(int, 3.0) - """), + """ + ), {'4:12 ' + TC006.format(annotation='int')}, ), # Call function from aliased module ( - textwrap.dedent(""" + textwrap.dedent( + """ import typing as t t.cast(int, 3.0) - """), + """ + ), {'4:7 ' + TC006.format(annotation='int')}, ), # re-export of cast using a registered compat module ( - textwrap.dedent(""" + textwrap.dedent( + """ from mylib import compat compat.cast(int, 3.0) - """), + """ + ), {'4:12 ' + TC006.format(annotation='int')}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from .compat import cast cast(int, 3.0) - """), + """ + ), {'4:5 ' + TC006.format(annotation='int')}, ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from ..compat import cast cast(int, 3.0) - """), + """ + ), {'4:5 ' + TC006.format(annotation='int')}, ), ] diff --git a/tests/test_tc007.py b/tests/test_tc007.py index 6b736e8..8fca26d 100644 --- a/tests/test_tc007.py +++ b/tests/test_tc007.py @@ -22,7 +22,8 @@ ('if TYPE_CHECKING:\n\tfrom typing import Dict\nx: TypeAlias = Dict[int]', {'3:15 ' + TC007.format(alias='Dict')}), # Regression test for issue #163 ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -30,12 +31,14 @@ from typing_extensions import TypeAlias Foo: TypeAlias = Sequence[int] - '''), + ''' + ), set(), ), # Inverse regression test for issue #163 ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING from typing_extensions import TypeAlias @@ -43,7 +46,8 @@ from collections.abc import Sequence Foo: TypeAlias = Sequence[int] - '''), + ''' + ), { '8:17 ' + TC007.format(alias='Sequence'), }, @@ -54,12 +58,14 @@ # RHS on an explicit TypeAlias with 3.12 syntax should not emit a TC007 examples.append( ( - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: from collections.abc import Sequence type Foo = Sequence[int] - '''), + ''' + ), set(), ) ) diff --git a/tests/test_tc008.py b/tests/test_tc008.py index 0541f16..84fd52c 100644 --- a/tests/test_tc008.py +++ b/tests/test_tc008.py @@ -33,51 +33,60 @@ {'4:20 ' + TC008.format(alias='int')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: import something x: TypeAlias = "something" - '''), + ''' + ), set(), ), ( # Regression test for Issue #164 - textwrap.dedent(''' + textwrap.dedent( + ''' from wtforms import Field from wtforms.fields.core import UnboundField foo: TypeAlias = 'UnboundField[Field]' - '''), + ''' + ), set(), ), ( # this used to yield false negatives but works now, yay - textwrap.dedent(''' + textwrap.dedent( + ''' class Foo(Protocol): pass x: TypeAlias = 'Foo | None' - '''), + ''' + ), {'5:15 ' + TC008.format(alias='Foo | None')}, ), ( # Regression test for Issue #168 - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: Foo: TypeAlias = str | int Bar: TypeAlias = 'Foo' - '''), + ''' + ), set(), ), ( # Regression test for Issue #168 # The runtime declaration are inside a Protocol so they should not # affect the outcome - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: Foo: TypeAlias = str | int else: @@ -85,27 +94,32 @@ class X(Protocol): Foo: str | int Bar: TypeAlias = 'Foo' - '''), + ''' + ), set(), ), ] if sys.version_info >= (3, 12): - examples.extend([ - ( - # new style type alias should never be wrapped - textwrap.dedent(''' + examples.extend( + [ + ( + # new style type alias should never be wrapped + textwrap.dedent( + ''' if TYPE_CHECKING: type Foo = 'str' type Bar = 'Foo' - '''), - { - '3:15 ' + TC008.format(alias='str'), - '5:11 ' + TC008.format(alias='Foo'), - }, - ) - ]) + ''' + ), + { + '3:15 ' + TC008.format(alias='str'), + '5:11 ' + TC008.format(alias='Foo'), + }, + ) + ] + ) @pytest.mark.parametrize(('example', 'expected'), examples) diff --git a/tests/test_tc009.py b/tests/test_tc009.py index b41e5e5..15e985b 100644 --- a/tests/test_tc009.py +++ b/tests/test_tc009.py @@ -18,19 +18,22 @@ ('', set()), # Used in file ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: datetime = Any x = datetime - """), + """ + ), {'5:4 ' + TC009.format(name='datetime')}, ), # Used in function ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -38,22 +41,26 @@ class date: ... def example(): return date() - """), + """ + ), {'5:4 ' + TC009.format(name='date')}, ), # Used, but only used inside the type checking block ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: class date: ... CustomType = date - """), + """ + ), set(), ), # Used for typing only ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: class date: ... @@ -61,11 +68,13 @@ def example(*args: date, **kwargs: date): return my_type: Type[date] | date - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from __future__ import annotations from typing import TYPE_CHECKING @@ -78,11 +87,13 @@ class Example: async def example(self) -> AsyncIterator[list[str]]: yield 0 - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ from typing import TYPE_CHECKING from weakref import WeakKeyDictionary @@ -91,11 +102,13 @@ async def example(self) -> AsyncIterator[list[str]]: d = WeakKeyDictionary["Any", "Any"]() - """), + """ + ), set(), ), ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: a = int b: TypeAlias = str @@ -104,13 +117,15 @@ class d(TypedDict): ... def test_function(a, /, b, *, c, **d): print(a, b, c, d) - """), + """ + ), set(), ), # Regression test for #131 # handle scopes correctly ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: Foo: something @@ -132,13 +147,15 @@ class Foo(Protocol): bar: Foo = Foo() - """), + """ + ), set(), ), # regression test for #131 # a common pattern for inheriting from generics that aren't runtime subscriptable ( - textwrap.dedent(""" + textwrap.dedent( + """ from wtforms import Field if TYPE_CHECKING: @@ -148,13 +165,15 @@ class Foo(Protocol): class IntegerField(BaseField): pass - """), + """ + ), set(), ), # inverse regression test for #131 # here we forgot the else so it will complain about BaseField ( - textwrap.dedent(""" + textwrap.dedent( + """ from wtforms import Field if TYPE_CHECKING: @@ -162,7 +181,8 @@ class IntegerField(BaseField): class IntegerField(BaseField): pass - """), + """ + ), {'5:4 ' + TC009.format(name='BaseField')}, ), ] @@ -170,23 +190,27 @@ class IntegerField(BaseField): if sys.version_info >= (3, 12): examples.append( ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: type Foo = int x = Foo - """), + """ + ), {'3:4 ' + TC009.format(name='Foo')}, ) ) examples.append( ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: type Foo = int x: Foo - """), + """ + ), set(), ) ) diff --git a/tests/test_tc010.py b/tests/test_tc010.py index fc2091d..d351a2b 100644 --- a/tests/test_tc010.py +++ b/tests/test_tc010.py @@ -54,7 +54,8 @@ # case at some point and then it might become an error, so it's better # to have cleaned up those annotations by then ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: x: "int" | None y: int | "None" @@ -65,7 +66,8 @@ def foo(x: int | "str" | None) -> int | "None": pass - """), + """ + ), { '3:7 ' + TC010, '4:13 ' + TC010, diff --git a/tests/test_tc100.py b/tests/test_tc100.py index 447e144..d7a978a 100644 --- a/tests/test_tc100.py +++ b/tests/test_tc100.py @@ -46,13 +46,15 @@ ('if TYPE_CHECKING:\n\tfrom typing import Dict\ndef example() -> Dict[str, int]:\n\tpass', {'1:0 ' + TC100}), ( # Regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: from baz import Bar def foo(self) -> None: x: Bar - '''), + ''' + ), set(), ), ] @@ -61,7 +63,8 @@ def foo(self) -> None: # PEP695 tests examples += [ ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from .types import T @@ -71,7 +74,8 @@ def foo[T](a: T) -> T: ... class Bar[T](Sequence[T]): x: T - """), + """ + ), set(), ) ] diff --git a/tests/test_tc101.py b/tests/test_tc101.py index d39f3f8..26fad26 100644 --- a/tests/test_tc101.py +++ b/tests/test_tc101.py @@ -37,20 +37,23 @@ ), # ast.AnnAssign from type checking block import with quotes ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: import something x: "something" - '''), + ''' + ), {'7:3 ' + TC101.format(annotation='something')}, ), # No futures import and no type checking block ("from typing import Dict\nx: 'Dict'", {'2:3 ' + TC101.format(annotation='Dict')}), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: @@ -58,11 +61,13 @@ def example(x: "something") -> something: pass - '''), + ''' + ), {'7:15 ' + TC101.format(annotation='something')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: @@ -70,80 +75,97 @@ def example(x: "something") -> something: def example(x: "something") -> "something": pass - '''), + ''' + ), {'7:15 ' + TC101.format(annotation='something'), '7:31 ' + TC101.format(annotation='something')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations def example(x: "something") -> "something": pass - '''), + ''' + ), {'4:15 ' + TC101.format(annotation='something'), '4:31 ' + TC101.format(annotation='something')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: import something def example(x: "something") -> "something": pass - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' class X: def foo(self) -> 'X': pass - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations class X: def foo(self) -> 'X': pass - '''), + ''' + ), {'4:21 ' + TC101.format(annotation='X')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import Annotated x: Annotated[int, 42] - '''), + ''' + ), set(), ), # Make sure we didn't introduce any regressions while solving #167 # since we started to treat the RHS sort of like an annotation for # some of the use-cases ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: from foo import Foo x: TypeAlias = 'Foo' - '''), + ''' + ), set(), ), ( # Regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' def foo(self) -> None: x: Bar - '''), + ''' + ), set(), ), ( # Reverse regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' def foo(self) -> None: x: 'Bar' - '''), + ''' + ), {'3:7 ' + TC101.format(annotation='Bar')}, ), ] @@ -152,13 +174,15 @@ def foo(self) -> None: # PEP695 tests examples += [ ( - textwrap.dedent(""" + textwrap.dedent( + """ def foo[T](a: 'T') -> 'T': pass class Bar[T](Set['T']): x: 'T' - """), + """ + ), { '2:14 ' + TC101.format(annotation='T'), '2:22 ' + TC101.format(annotation='T'), diff --git a/tests/test_tc200.py b/tests/test_tc200.py index 7d3316e..149292e 100644 --- a/tests/test_tc200.py +++ b/tests/test_tc200.py @@ -46,7 +46,8 @@ set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import Dict, TYPE_CHECKING if TYPE_CHECKING: @@ -54,11 +55,13 @@ def example(x: Dict[something]) -> Dict["something"]: pass - '''), + ''' + ), {'7:20 ' + TC200.format(annotation='something')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -66,12 +69,14 @@ def example(x: Dict[something]) -> Dict["something"]: def example(x: ast.If): pass - '''), + ''' + ), {'7:15 ' + TC200.format(annotation='ast')}, ), # Regression test for issue #163 ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -89,12 +94,14 @@ class FooProtocol(Protocol): class FooDict(TypedDict): seq: Sequence[int] - '''), + ''' + ), set(), ), # Inverse regression test for issue #163 ( - textwrap.dedent(''' + textwrap.dedent( + ''' from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -118,7 +125,8 @@ class FooDict(TypedDict): Bar: int x: Bar - '''), + ''' + ), { '9:5 ' + TC200.format(annotation='TypeAlias'), '12:9 ' + TC200.format(annotation='Sequence'), @@ -128,13 +136,15 @@ class FooDict(TypedDict): ), ( # Regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: from baz import Bar def foo(self) -> None: x: Bar - '''), + ''' + ), set(), ), ] @@ -143,12 +153,14 @@ def foo(self) -> None: # PEP646 tests examples += [ ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: Ts = TypeVarTuple("Ts") x: tuple[*Ts] - """), + """ + ), {'5:10 ' + TC200.format(annotation='Ts')}, ) ] @@ -157,7 +169,8 @@ def foo(self) -> None: # PEP695 tests examples += [ ( - textwrap.dedent(""" + textwrap.dedent( + """ if TYPE_CHECKING: from .types import T @@ -167,7 +180,8 @@ def foo[T](a: T) -> T: ... class Bar[T](Sequence[T]): x: T - """), + """ + ), set(), ) ] diff --git a/tests/test_tc201.py b/tests/test_tc201.py index 72186d5..3d205d9 100644 --- a/tests/test_tc201.py +++ b/tests/test_tc201.py @@ -32,18 +32,21 @@ {'4:8 ' + TC201.format(annotation='int')}, ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: import something x: "something" - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations if TYPE_CHECKING: @@ -51,49 +54,59 @@ def example(x: "something") -> something: pass - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' class X: def foo(self) -> 'X': pass - '''), + ''' + ), set(), ), ( - textwrap.dedent(''' + textwrap.dedent( + ''' from __future__ import annotations class X: def foo(self) -> 'X': pass - '''), + ''' + ), set(), ), ( # Regression test for Issue #164 - textwrap.dedent(''' + textwrap.dedent( + ''' from wtforms import Field from wtforms.fields.core import UnboundField foo: 'UnboundField[Field]' - '''), + ''' + ), set(), ), ( # this used to yield false negatives but works now, yay - textwrap.dedent(''' + textwrap.dedent( + ''' class Foo(Protocol): pass x: 'Foo | None' - '''), + ''' + ), {'5:3 ' + TC201.format(annotation='Foo | None')}, ), ( # Regression test for Issue #168 - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: Foo = str | int Bar: TypeAlias = Foo | None @@ -110,7 +123,8 @@ def foo(a: 'T', *args: Unpack['Ts']) -> None: def bar(*args: 'P.args', **kwargs: 'P.kwargs') -> None: pass - '''), + ''' + ), set(), ), ( @@ -121,7 +135,8 @@ def bar(*args: 'P.args', **kwargs: 'P.kwargs') -> None: # ideally it still would, but it would require more complex # logic in order to avoid false positives, so for now we # put up with the false negatives here - textwrap.dedent(''' + textwrap.dedent( + ''' if TYPE_CHECKING: Foo = str | int Bar: TypeAlias = Foo | None @@ -145,32 +160,39 @@ def foo(a: 'T', *args: Unpack['Ts']) -> None: def bar(*args: 'P.args', **kwargs: 'P.kwargs') -> None: pass - '''), + ''' + ), set(), ), ( # Regression test for type checking only module attributes - textwrap.dedent(''' + textwrap.dedent( + ''' import lxml.etree foo: 'lxml.etree._Element' - '''), + ''' + ), set(), ), ( # Regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' def foo(self) -> None: x: Bar - '''), + ''' + ), set(), ), ( # Reverse regression test for #186 - textwrap.dedent(''' + textwrap.dedent( + ''' def foo(self) -> None: x: 'Bar' - '''), + ''' + ), {'3:7 ' + TC201.format(annotation='Bar')}, ), ] @@ -179,13 +201,15 @@ def foo(self) -> None: # PEP695 tests examples += [ ( - textwrap.dedent(""" + textwrap.dedent( + """ def foo[T](a: 'T') -> 'T': pass class Bar[T]: x: 'T' - """), + """ + ), { '2:14 ' + TC201.format(annotation='T'), '2:22 ' + TC201.format(annotation='T'),