diff --git a/.github/workflows/reusable-linters.yml b/.github/workflows/reusable-linters.yml index 68417ea5..95eee3e1 100644 --- a/.github/workflows/reusable-linters.yml +++ b/.github/workflows/reusable-linters.yml @@ -71,9 +71,7 @@ jobs: with: token: ${{ secrets.codecov-token }} files: >- - .tox/.tmp/.mypy/python-3.12/cobertura.xml, - .tox/.tmp/.mypy/python-3.10/cobertura.xml, - .tox/.tmp/.mypy/python-3.8/cobertura.xml + .tox/.tmp/.mypy/python-3.11/cobertura.xml flags: >- CI-GHA, MyPy diff --git a/.mypy.ini b/.mypy.ini index 912c9402..9741fed6 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,48 +1,33 @@ [mypy] -python_version = 3.8 -color_output = True -error_summary = True -files = - frozenlist/, - packaging/, - tests/ - +files = frozenlist, packaging, tests check_untyped_defs = True - -disallow_untyped_defs = True +follow_imports_for_stubs = True +disallow_any_decorated = True disallow_any_generics = True - -enable_error_code = - ignore-without-code - -follow_imports = normal - -ignore_missing_imports = False - -pretty = true - -show_column_numbers = true -show_error_codes = true -strict_optional = True - -warn_no_return = True +disallow_any_unimported = True +disallow_incomplete_defs = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_decorators = True +disallow_untyped_defs = True +# TODO(PY312): explicit-override +enable_error_code = ignore-without-code, possibly-undefined, redundant-expr, redundant-self, truthy-bool, truthy-iterable, unused-awaitable +extra_checks = True +implicit_reexport = False +no_implicit_optional = True +pretty = True +show_column_numbers = True +show_error_codes = True +show_error_code_links = True +strict_equality = True +warn_incomplete_stub = True warn_redundant_casts = True +warn_return_any = True +warn_unreachable = True warn_unused_ignores = True [mypy-Cython.*] ignore_missing_imports = true -[mypy-distutils.*] -ignore_missing_imports = true - -[mypy-expandvars] -ignore_missing_imports = true - -[mypy-pep517_backend.*] -check_untyped_defs = False -disallow_any_generics = False -disallow_untyped_defs = False -warn_unused_ignores = False - -[mypy-tomllib] +[mypy-expandvars.*] ignore_missing_imports = true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 176aaabd..6ac89ef2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -168,8 +168,8 @@ repos: rev: v1.14.1 hooks: - id: mypy - alias: mypy-py312 - name: MyPy, for Python 3.12 + alias: mypy-py311 + name: MyPy, for Python 3.11 additional_dependencies: - lxml # dep of `--txt-report`, `--cobertura-xml-report` & `--html-report` - pytest @@ -178,42 +178,10 @@ repos: args: - --install-types - --non-interactive - - --python-version=3.12 - - --txt-report=.tox/.tmp/.mypy/python-3.12 - - --cobertura-xml-report=.tox/.tmp/.mypy/python-3.12 - - --html-report=.tox/.tmp/.mypy/python-3.12 - pass_filenames: false - - id: mypy - alias: mypy-py310 - name: MyPy, for Python 3.10 - additional_dependencies: - - lxml # dep of `--txt-report`, `--cobertura-xml-report` & `--html-report` - - pytest - - tomli # requirement of packaging/pep517_backend/ - - types-setuptools # requirement of packaging/pep517_backend/ - args: - - --install-types - - --non-interactive - - --python-version=3.10 - - --txt-report=.tox/.tmp/.mypy/python-3.10 - - --cobertura-xml-report=.tox/.tmp/.mypy/python-3.10 - - --html-report=.tox/.tmp/.mypy/python-3.10 - pass_filenames: false - - id: mypy - alias: mypy-py38 - name: MyPy, for Python 3.8 - additional_dependencies: - - lxml # dep of `--txt-report`, `--cobertura-xml-report` & `--html-report` - - pytest - - tomli # requirement of packaging/pep517_backend/ - - types-setuptools # requirement of packaging/pep517_backend/ - args: - - --install-types - - --non-interactive - - --python-version=3.8 - - --txt-report=.tox/.tmp/.mypy/python-3.8 - - --cobertura-xml-report=.tox/.tmp/.mypy/python-3.8 - - --html-report=.tox/.tmp/.mypy/python-3.8 + - --python-version=3.11 + - --txt-report=.tox/.tmp/.mypy/python-3.11 + - --cobertura-xml-report=.tox/.tmp/.mypy/python-3.11 + - --html-report=.tox/.tmp/.mypy/python-3.11 pass_filenames: false - repo: https://github.com/rhysd/actionlint diff --git a/packaging/pep517_backend/_backend.py b/packaging/pep517_backend/_backend.py index 23f80c2a..bce4a9e1 100644 --- a/packaging/pep517_backend/_backend.py +++ b/packaging/pep517_backend/_backend.py @@ -4,7 +4,6 @@ from __future__ import annotations import os -import typing as t from contextlib import contextmanager, nullcontext, suppress from functools import partial from pathlib import Path @@ -12,6 +11,7 @@ from sys import implementation as _system_implementation from sys import stderr as _standard_error_stream from tempfile import TemporaryDirectory +from typing import Dict, Iterator, List, Union from warnings import warn as _warn_that from setuptools.build_meta import build_sdist as _setuptools_build_sdist @@ -55,7 +55,7 @@ 'get_requires_for_build_wheel', 'prepare_metadata_for_build_wheel', *( - () if _setuptools_build_editable is None + () if _setuptools_build_editable is None # type: ignore[redundant-expr] else ( 'build_editable', 'get_requires_for_build_editable', @@ -64,7 +64,7 @@ ), ) -_ConfigDict = t.Dict[str, t.Union[str, t.List[str], None]] +_ConfigDict = Dict[str, Union[str, List[str], None]] CYTHON_TRACING_CONFIG_SETTING = 'with-cython-tracing' @@ -86,7 +86,7 @@ """A fallback for ``pure-python`` is not set.""" -def _is_truthy_setting_value(setting_value) -> bool: +def _is_truthy_setting_value(setting_value: str) -> bool: truthy_values = {'', None, 'true', '1', 'on'} return setting_value.lower() in truthy_values @@ -107,7 +107,7 @@ def _get_setting_value( continue with suppress(lookup_errors): # type: ignore[arg-type] - return _is_truthy_setting_value(src_mapping[src_key]) # type: ignore[index] + return _is_truthy_setting_value(src_mapping[src_key]) # type: ignore[arg-type,index] return default @@ -124,7 +124,7 @@ def _make_pure_python(config_settings: _ConfigDict | None = None) -> bool: def _include_cython_line_tracing( config_settings: _ConfigDict | None = None, *, - default=False, + default: bool = False, ) -> bool: return _get_setting_value( config_settings, @@ -135,7 +135,7 @@ def _include_cython_line_tracing( @contextmanager -def patched_distutils_cmd_install(): +def patched_distutils_cmd_install() -> Iterator[None]: """Make `install_lib` of `install` cmd always use `platlib`. :yields: None @@ -143,19 +143,19 @@ def patched_distutils_cmd_install(): # Without this, build_lib puts stuff under `*.data/purelib/` folder orig_finalize = _distutils_install_cmd.finalize_options - def new_finalize_options(self): # noqa: WPS430 + def new_finalize_options(self: _distutils_install_cmd) -> None: # noqa: WPS430 self.install_lib = self.install_platlib orig_finalize(self) - _distutils_install_cmd.finalize_options = new_finalize_options + _distutils_install_cmd.finalize_options = new_finalize_options # type: ignore[method-assign] try: yield finally: - _distutils_install_cmd.finalize_options = orig_finalize + _distutils_install_cmd.finalize_options = orig_finalize # type: ignore[method-assign] @contextmanager -def patched_dist_has_ext_modules(): +def patched_dist_has_ext_modules() -> Iterator[None]: """Make `has_ext_modules` of `Distribution` always return `True`. :yields: None @@ -163,15 +163,15 @@ def patched_dist_has_ext_modules(): # Without this, build_lib puts stuff under `*.data/platlib/` folder orig_func = _DistutilsDistribution.has_ext_modules - _DistutilsDistribution.has_ext_modules = lambda *args, **kwargs: True + _DistutilsDistribution.has_ext_modules = lambda *args, **kwargs: True # type: ignore[method-assign] try: yield finally: - _DistutilsDistribution.has_ext_modules = orig_func + _DistutilsDistribution.has_ext_modules = orig_func # type: ignore[method-assign] @contextmanager -def patched_dist_get_long_description(): +def patched_dist_get_long_description() -> Iterator[None]: """Make `has_ext_modules` of `Distribution` always return `True`. :yields: None @@ -179,16 +179,17 @@ def patched_dist_get_long_description(): # Without this, build_lib puts stuff under `*.data/platlib/` folder _orig_func = _DistutilsDistributionMetadata.get_long_description - def _get_sanitized_long_description(self): + def _get_sanitized_long_description(self: _DistutilsDistributionMetadata) -> str: + assert self.long_description is not None return sanitize_rst_roles(self.long_description) - _DistutilsDistributionMetadata.get_long_description = ( + _DistutilsDistributionMetadata.get_long_description = ( # type: ignore[method-assign] _get_sanitized_long_description ) try: yield finally: - _DistutilsDistributionMetadata.get_long_description = _orig_func + _DistutilsDistributionMetadata.get_long_description = _orig_func # type: ignore[method-assign] def _exclude_dir_path( @@ -215,7 +216,7 @@ def _exclude_dir_path( @contextmanager -def _in_temporary_directory(src_dir: Path) -> t.Iterator[None]: +def _in_temporary_directory(src_dir: Path) -> Iterator[None]: with TemporaryDirectory(prefix='.tmp-frozenlist-pep517-') as tmp_dir: tmp_dir_path = Path(tmp_dir) root_tmp_dir_path = tmp_dir_path.parent @@ -238,7 +239,7 @@ def maybe_prebuild_c_extensions( line_trace_cython_when_unset: bool = False, build_inplace: bool = False, config_settings: _ConfigDict | None = None, -) -> t.Generator[None, t.Any, t.Any]: +) -> Iterator[None]: """Pre-build C-extensions in a temporary directory, when needed. This context manager also patches metadata, setuptools and distutils. diff --git a/packaging/pep517_backend/_compat.py b/packaging/pep517_backend/_compat.py index 5e2ffe09..af50e577 100644 --- a/packaging/pep517_backend/_compat.py +++ b/packaging/pep517_backend/_compat.py @@ -1,16 +1,19 @@ """Cross-python stdlib shims.""" import os -import typing as t +import sys from contextlib import contextmanager from pathlib import Path +from typing import Iterator -try: - from contextlib import chdir as chdir_cm # type: ignore[attr-defined] -except ImportError: +if sys.version_info >= (3, 11): + from contextlib import chdir as chdir_cm + from tomllib import loads as load_toml_from_string +else: + from tomli import loads as load_toml_from_string @contextmanager # type: ignore[no-redef] - def chdir_cm(path: os.PathLike) -> t.Iterator[None]: + def chdir_cm(path: "os.PathLike[str]") -> Iterator[None]: """Temporarily change the current directory, recovering on exit.""" original_wd = Path.cwd() os.chdir(path) @@ -20,10 +23,4 @@ def chdir_cm(path: os.PathLike) -> t.Iterator[None]: os.chdir(original_wd) -try: - from tomllib import loads as load_toml_from_string -except ImportError: - from tomli import loads as load_toml_from_string # type: ignore[no-redef] - - __all__ = ("chdir_cm", "load_toml_from_string") # noqa: WPS410 diff --git a/packaging/pep517_backend/_cython_configuration.py b/packaging/pep517_backend/_cython_configuration.py index ef5ea391..b55df672 100644 --- a/packaging/pep517_backend/_cython_configuration.py +++ b/packaging/pep517_backend/_cython_configuration.py @@ -6,6 +6,7 @@ from contextlib import contextmanager from pathlib import Path from sys import version_info as _python_version_tuple +from typing import Iterator, TypedDict from expandvars import expandvars @@ -13,7 +14,14 @@ from ._transformers import get_cli_kwargs_from_config, get_enabled_cli_flags_from_config -def get_local_cython_config() -> dict: +class Config(TypedDict): + env: dict[str, str] + flags: dict[str, bool] + kwargs: dict[str, str] + src: list[str] + + +def get_local_cython_config() -> Config: """Grab optional build dependencies from pyproject.toml config. :returns: config section from ``pyproject.toml`` @@ -67,10 +75,10 @@ def get_local_cython_config() -> dict: """ config_toml_txt = (Path.cwd().resolve() / 'pyproject.toml').read_text() config_mapping = load_toml_from_string(config_toml_txt) - return config_mapping['tool']['local']['cythonize'] + return config_mapping['tool']['local']['cythonize'] # type: ignore[no-any-return] -def make_cythonize_cli_args_from_config(config) -> list[str]: +def make_cythonize_cli_args_from_config(config: Config) -> list[str]: py_ver_arg = f'-{_python_version_tuple.major!s}' cli_flags = get_enabled_cli_flags_from_config(config['flags']) @@ -80,7 +88,7 @@ def make_cythonize_cli_args_from_config(config) -> list[str]: @contextmanager -def patched_env(env: dict[str, str], cython_line_tracing_requested: bool): +def patched_env(env: dict[str, str], cython_line_tracing_requested: bool) -> Iterator[None]: """Temporary set given env vars. :param env: tmp env vars to set diff --git a/packaging/pep517_backend/_transformers.py b/packaging/pep517_backend/_transformers.py index 76f15f40..4e6dca4e 100644 --- a/packaging/pep517_backend/_transformers.py +++ b/packaging/pep517_backend/_transformers.py @@ -2,25 +2,26 @@ from itertools import chain from re import sub as _substitute_with_regexp +from typing import Dict, Iterable, Iterator, List, Mapping, Tuple, Union -def _emit_opt_pairs(opt_pair): +def _emit_opt_pairs(opt_pair: Tuple[str, Union[str, Dict[str, str]]]) -> Iterator[str]: flag, flag_value = opt_pair flag_opt = f"--{flag!s}" if isinstance(flag_value, dict): - sub_pairs = flag_value.items() + sub_pairs: Iterable[Tuple[str, ...]] = flag_value.items() else: sub_pairs = ((flag_value,),) yield from ("=".join(map(str, (flag_opt,) + pair)) for pair in sub_pairs) -def get_cli_kwargs_from_config(kwargs_map): +def get_cli_kwargs_from_config(kwargs_map: Mapping[str, str]) -> List[str]: """Make a list of options with values from config.""" return list(chain.from_iterable(map(_emit_opt_pairs, kwargs_map.items()))) -def get_enabled_cli_flags_from_config(flags_map): +def get_enabled_cli_flags_from_config(flags_map: Mapping[str, bool]) -> List[str]: """Make a list of enabled boolean flags from config.""" return [f"--{flag}" for flag, is_enabled in flags_map.items() if is_enabled] diff --git a/packaging/pep517_backend/cli.py b/packaging/pep517_backend/cli.py index f3a1c85c..52cfcbcb 100644 --- a/packaging/pep517_backend/cli.py +++ b/packaging/pep517_backend/cli.py @@ -5,6 +5,7 @@ import sys from itertools import chain from pathlib import Path +from typing import Sequence from Cython.Compiler.Main import compile as _translate_cython_cli_cmd from Cython.Compiler.Main import parse_command_line as _split_cython_cli_args @@ -18,7 +19,7 @@ _PROJECT_PATH = Path(__file__).parents[2] -def run_main_program(argv) -> int | str: +def run_main_program(argv: Sequence[str]) -> int | str: """Invoke ``translate-cython`` or fail.""" if len(argv) != 2: return 'This program only accepts one argument -- "translate-cython"' @@ -43,7 +44,7 @@ def run_main_program(argv) -> int | str: ) with _patched_cython_env(config['env'], cython_line_tracing_requested=True): - return _translate_cython_cli_cmd( + return _translate_cython_cli_cmd( # type: ignore[no-any-return] cython_sources, cython_options, ).num_errors diff --git a/packaging/pep517_backend/hooks.py b/packaging/pep517_backend/hooks.py index f8f3fb24..f601b193 100644 --- a/packaging/pep517_backend/hooks.py +++ b/packaging/pep517_backend/hooks.py @@ -2,7 +2,7 @@ from contextlib import suppress as _suppress -from setuptools.build_meta import * # Re-exporting PEP 517 hooks # pylint: disable=unused-wildcard-import,wildcard-import # noqa: E501, F401, F403 +from setuptools.build_meta import * # Re-exporting PEP 517 hooks # pylint: disable=unused-wildcard-import,wildcard-import # noqa: F401, F403 # Re-exporting PEP 517 hooks from ._backend import ( # type: ignore[assignment] diff --git a/requirements/ci.txt b/requirements/ci.txt index b1283dae..08ff38d0 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -7,3 +7,4 @@ pytest==7.4.3 pytest-cov==4.1.0 tox==4.11.4 twine==4.0.2 +types-setuptools==75.8.0.20250210 diff --git a/setup.cfg b/setup.cfg index d8c9f7e9..c2148c55 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,7 +65,8 @@ include_package_data = True max-line-length=88 [flake8] -ignore = N801,N802,N803,E226,W503,W504,E252 +extend-select = B950 +ignore = E226,E252,E501,N801,N802,N803,W503,W504 max-line-length = 88 # Allow certain violations in certain files: