Skip to content

Commit bbaada8

Browse files
committed
Initial pyright config
1 parent 4cc8ad3 commit bbaada8

20 files changed

+121
-49
lines changed

pkg_resources/__init__.py

+12
Original file line numberDiff line numberDiff line change
@@ -568,24 +568,30 @@ def get_entry_info(dist: _EPDistType, group: str, name: str) -> EntryPoint | Non
568568
class IMetadataProvider(Protocol):
569569
def has_metadata(self, name: str) -> bool:
570570
"""Does the package's distribution contain the named metadata?"""
571+
...
571572

572573
def get_metadata(self, name: str) -> str:
573574
"""The named metadata resource as a string"""
575+
...
574576

575577
def get_metadata_lines(self, name: str) -> Iterator[str]:
576578
"""Yield named metadata resource as list of non-blank non-comment lines
577579
578580
Leading and trailing whitespace is stripped from each line, and lines
579581
with ``#`` as the first non-blank character are omitted."""
582+
...
580583

581584
def metadata_isdir(self, name: str) -> bool:
582585
"""Is the named metadata a directory? (like ``os.path.isdir()``)"""
586+
...
583587

584588
def metadata_listdir(self, name: str) -> list[str]:
585589
"""List of metadata names in the directory (like ``os.listdir()``)"""
590+
...
586591

587592
def run_script(self, script_name: str, namespace: dict[str, Any]) -> None:
588593
"""Execute the named script in the supplied namespace dictionary"""
594+
...
589595

590596

591597
class IResourceProvider(IMetadataProvider, Protocol):
@@ -597,29 +603,35 @@ def get_resource_filename(
597603
"""Return a true filesystem path for `resource_name`
598604
599605
`manager` must be a ``ResourceManager``"""
606+
...
600607

601608
def get_resource_stream(
602609
self, manager: ResourceManager, resource_name: str
603610
) -> _ResourceStream:
604611
"""Return a readable file-like object for `resource_name`
605612
606613
`manager` must be a ``ResourceManager``"""
614+
...
607615

608616
def get_resource_string(
609617
self, manager: ResourceManager, resource_name: str
610618
) -> bytes:
611619
"""Return the contents of `resource_name` as :obj:`bytes`
612620
613621
`manager` must be a ``ResourceManager``"""
622+
...
614623

615624
def has_resource(self, resource_name: str) -> bool:
616625
"""Does the package contain the named resource?"""
626+
...
617627

618628
def resource_isdir(self, resource_name: str) -> bool:
619629
"""Is the named resource a directory? (like ``os.path.isdir()``)"""
630+
...
620631

621632
def resource_listdir(self, resource_name: str) -> list[str]:
622633
"""List of resource names in the directory (like ``os.listdir()``)"""
634+
...
623635

624636

625637
class WorkingSet:

pkg_resources/tests/test_pkg_resources.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def teardown_class(cls):
7070
finalizer()
7171

7272
def test_resource_listdir(self):
73-
import mod
73+
import mod # pyright: ignore[reportMissingImports] # Temporary package for test
7474

7575
zp = pkg_resources.ZipProvider(mod)
7676

@@ -84,7 +84,7 @@ def test_resource_listdir(self):
8484
assert zp.resource_listdir('nonexistent') == []
8585
assert zp.resource_listdir('nonexistent/') == []
8686

87-
import mod2
87+
import mod2 # pyright: ignore[reportMissingImports] # Temporary package for test
8888

8989
zp2 = pkg_resources.ZipProvider(mod2)
9090

@@ -100,7 +100,7 @@ def test_resource_filename_rewrites_on_change(self):
100100
same size and modification time, it should not be overwritten on a
101101
subsequent call to get_resource_filename.
102102
"""
103-
import mod
103+
import mod # pyright: ignore[reportMissingImports] # Temporary package for test
104104

105105
manager = pkg_resources.ResourceManager()
106106
zp = pkg_resources.ZipProvider(mod)

pkg_resources/tests/test_resources.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -817,11 +817,11 @@ def test_two_levels_deep(self, symlinked_tmpdir):
817817
(pkg1 / '__init__.py').write_text(self.ns_str, encoding='utf-8')
818818
(pkg2 / '__init__.py').write_text(self.ns_str, encoding='utf-8')
819819
with pytest.warns(DeprecationWarning, match="pkg_resources.declare_namespace"):
820-
import pkg1
820+
import pkg1 # pyright: ignore[reportMissingImports] # Temporary package for test
821821
assert "pkg1" in pkg_resources._namespace_packages
822822
# attempt to import pkg2 from site-pkgs2
823823
with pytest.warns(DeprecationWarning, match="pkg_resources.declare_namespace"):
824-
import pkg1.pkg2
824+
import pkg1.pkg2 # pyright: ignore[reportMissingImports] # Temporary package for test
825825
# check the _namespace_packages dict
826826
assert "pkg1.pkg2" in pkg_resources._namespace_packages
827827
assert pkg_resources._namespace_packages["pkg1"] == ["pkg1.pkg2"]
@@ -862,8 +862,8 @@ def test_path_order(self, symlinked_tmpdir):
862862
(subpkg / '__init__.py').write_text(vers_str % number, encoding='utf-8')
863863

864864
with pytest.warns(DeprecationWarning, match="pkg_resources.declare_namespace"):
865-
import nspkg
866-
import nspkg.subpkg
865+
import nspkg # pyright: ignore[reportMissingImports] # Temporary package for test
866+
import nspkg.subpkg # pyright: ignore[reportMissingImports] # Temporary package for test
867867
expected = [str(site.realpath() / 'nspkg') for site in site_dirs]
868868
assert nspkg.__path__ == expected
869869
assert nspkg.subpkg.__version__ == 1

pyproject.toml

+5-4
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ test = [
3939

4040
# local
4141
"virtualenv>=13.0.0",
42-
"wheel>=0.44.0", # Consistent requirement normalisation in METADATA (see #4547)
42+
"wheel>=0.44.0", # Consistent requirement normalisation in METADATA (see #4547)
4343
"pip>=19.1", # For proper file:// URLs support.
4444
"packaging>=23.2",
4545
"jaraco.envs>=2.2",
@@ -52,11 +52,12 @@ test = [
5252
"pytest-timeout",
5353
'pytest-perf; sys_platform != "cygwin"', # workaround for jaraco/inflect#195, pydantic/pydantic-core#773 (see #3986)
5454
# for tools/finalize.py
55-
'jaraco.develop >= 7.21; python_version >= "3.9" and sys_platform != "cygwin"',
55+
'jaraco.develop >= 7.21; sys_platform != "cygwin"',
5656
"pytest-home >= 0.5",
57-
# pin mypy version so a new version doesn't suddenly cause the CI to fail,
57+
# pin type-checkers so a new version doesn't suddenly cause the CI to fail,
5858
# until types-setuptools is removed from typeshed.
59-
# For help with static-typing issues, or mypy update, ping @Avasam
59+
# For help with static-typing issues, or mypy/pyright update, ping @Avasam
60+
"pyright == 1.1.377",
6061
"mypy==1.11.*",
6162
# No Python 3.11 dependencies require tomli, but needed for type-checking since we import it directly
6263
"tomli",

pyrightconfig.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
3+
"exclude": [
4+
"build",
5+
".tox",
6+
".eggs",
7+
"**/_vendor", // Vendored
8+
"setuptools/_distutils", // Vendored
9+
"setuptools/config/_validate_pyproject/**", // Auto-generated
10+
],
11+
// Our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually.
12+
// "pythonVersion": "3.8",
13+
// For now we don't mind if mypy's `type: ignore` comments accidentally suppresses pyright issues
14+
"enableTypeIgnoreComments": true,
15+
"typeCheckingMode": "basic",
16+
// Too many issues caused by dynamic patching, still worth fixing when we can
17+
"reportAttributeAccessIssue": "warning",
18+
// Fails on Python 3.12 due to missing distutils and on cygwin CI tests
19+
"reportAssignmentType": "warning",
20+
"reportMissingImports": "warning",
21+
"reportOptionalCall": "warning",
22+
// FIXME: A handful of reportOperatorIssue spread throughout the codebase
23+
"reportOperatorIssue": "warning",
24+
// Deferred initialization (initialize_options/finalize_options) causes many "potentially None" issues
25+
// TODO: Fix with type-guards or by changing how it's initialized
26+
"reportArgumentType": "warning", // A lot of these are caused by jaraco.path.build's spec argument not being a Mapping https://github.com/jaraco/jaraco.path/pull/3
27+
"reportCallIssue": "warning",
28+
"reportGeneralTypeIssues": "warning",
29+
"reportOptionalIterable": "warning",
30+
"reportOptionalMemberAccess": "warning",
31+
"reportOptionalOperand": "warning",
32+
}

setuptools/__init__.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import re
88
import sys
99
from abc import abstractmethod
10+
from collections.abc import Mapping
1011
from typing import TYPE_CHECKING, TypeVar, overload
1112

1213
sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip
@@ -54,7 +55,7 @@ class MinimalDistribution(distutils.core.Distribution):
5455
fetch_build_eggs interface.
5556
"""
5657

57-
def __init__(self, attrs):
58+
def __init__(self, attrs: Mapping[str, object]):
5859
_incl = 'dependency_links', 'setup_requires'
5960
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
6061
super().__init__(filtered)
@@ -114,8 +115,10 @@ def setup(**attrs):
114115
setup.__doc__ = distutils.core.setup.__doc__
115116

116117
if TYPE_CHECKING:
118+
from typing_extensions import TypeAlias
119+
117120
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
118-
_Command = distutils.core.Command
121+
_Command: TypeAlias = distutils.core.Command
119122
else:
120123
_Command = monkey.get_unpatched(distutils.core.Command)
121124

setuptools/_reqs.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ def parse_strings(strs: _StrOrIter) -> Iterator[str]:
2828
return text.join_continuation(map(text.drop_comment, text.yield_lines(strs)))
2929

3030

31+
# These overloads are only needed because of a mypy false-positive, pyright gets it right
32+
# https://github.com/python/mypy/issues/3737
3133
@overload
3234
def parse(strs: _StrOrIter) -> Iterator[Requirement]: ...
33-
34-
3535
@overload
3636
def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ...
37-
38-
39-
def parse(strs, parser=parse_req):
37+
def parse(strs: _StrOrIter, parser: Callable[[str], _T] = parse_req) -> Iterator[_T]: # type: ignore[assignment]
4038
"""
4139
Replacement for ``pkg_resources.parse_requirements`` that uses ``packaging``.
4240
"""

setuptools/command/build.py

+6
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,15 @@ def finalize_options(self):
8787

8888
def initialize_options(self):
8989
"""(Required by the original :class:`setuptools.Command` interface)"""
90+
...
9091

9192
def finalize_options(self):
9293
"""(Required by the original :class:`setuptools.Command` interface)"""
94+
...
9395

9496
def run(self):
9597
"""(Required by the original :class:`setuptools.Command` interface)"""
98+
...
9699

97100
def get_source_files(self) -> list[str]:
98101
"""
@@ -104,6 +107,7 @@ def get_source_files(self) -> list[str]:
104107
with all the files necessary to build the distribution.
105108
All files should be strings relative to the project root directory.
106109
"""
110+
...
107111

108112
def get_outputs(self) -> list[str]:
109113
"""
@@ -117,6 +121,7 @@ def get_outputs(self) -> list[str]:
117121
in ``get_output_mapping()`` plus files that are generated during the build
118122
and don't correspond to any source file already present in the project.
119123
"""
124+
...
120125

121126
def get_output_mapping(self) -> dict[str, str]:
122127
"""
@@ -127,3 +132,4 @@ def get_output_mapping(self) -> dict[str, str]:
127132
Destination files should be strings in the form of
128133
``"{build_lib}/destination/file/path"``.
129134
"""
135+
...

setuptools/command/build_clib.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from distutils.errors import DistutilsSetupError
66

77
try:
8-
from distutils._modified import newer_pairwise_group
8+
from distutils._modified import ( # pyright: ignore[reportMissingImports]
9+
newer_pairwise_group,
10+
)
911
except ImportError:
1012
# fallback for SETUPTOOLS_USE_DISTUTILS=stdlib
1113
from .._distutils._modified import newer_pairwise_group

setuptools/command/build_ext.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -156,20 +156,18 @@ def _get_output_mapping(self) -> Iterator[tuple[str, str]]:
156156
yield (output_cache, inplace_cache)
157157

158158
def get_ext_filename(self, fullname):
159-
so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX')
159+
so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX', '')
160160
if so_ext:
161161
filename = os.path.join(*fullname.split('.')) + so_ext
162162
else:
163163
filename = _build_ext.get_ext_filename(self, fullname)
164-
so_ext = get_config_var('EXT_SUFFIX')
164+
so_ext = str(get_config_var('EXT_SUFFIX'))
165165

166166
if fullname in self.ext_map:
167167
ext = self.ext_map[fullname]
168-
use_abi3 = ext.py_limited_api and get_abi3_suffix()
169-
if use_abi3:
170-
filename = filename[: -len(so_ext)]
171-
so_ext = get_abi3_suffix()
172-
filename = filename + so_ext
168+
abi3_suffix = get_abi3_suffix()
169+
if ext.py_limited_api and abi3_suffix: # Use abi3
170+
filename = filename[: -len(so_ext)] + abi3_suffix
173171
if isinstance(ext, Library):
174172
fn, ext = os.path.splitext(filename)
175173
return self.shlib_compiler.library_filename(fn, libtype)

setuptools/command/easy_install.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1792,7 +1792,7 @@ def auto_chmod(func, arg, exc):
17921792
return func(arg)
17931793
et, ev, _ = sys.exc_info()
17941794
# TODO: This code doesn't make sense. What is it trying to do?
1795-
raise (ev[0], ev[1] + (" %s %s" % (func, arg)))
1795+
raise (ev[0], ev[1] + (" %s %s" % (func, arg))) # pyright: ignore[reportOptionalSubscript, reportIndexIssue]
17961796

17971797

17981798
def update_dist_caches(dist_path, fix_zipimporter_caches):
@@ -2018,7 +2018,9 @@ def is_python_script(script_text, filename):
20182018

20192019

20202020
try:
2021-
from os import chmod as _chmod
2021+
from os import (
2022+
chmod as _chmod, # pyright: ignore[reportAssignmentType] # Loosing type-safety w/ pyright, but that's ok
2023+
)
20222024
except ImportError:
20232025
# Jython compatibility
20242026
def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway

setuptools/command/editable_wheel.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from .install_scripts import install_scripts as install_scripts_cls
4040

4141
if TYPE_CHECKING:
42+
from typing_extensions import Self
43+
4244
from .._vendor.wheel.wheelfile import WheelFile
4345

4446
_P = TypeVar("_P", bound=StrPath)
@@ -379,7 +381,7 @@ def _select_strategy(
379381
class EditableStrategy(Protocol):
380382
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ...
381383

382-
def __enter__(self): ...
384+
def __enter__(self) -> Self: ...
383385

384386
def __exit__(self, _exc_type, _exc_value, _traceback): ...
385387

setuptools/config/_apply_pyprojecttoml.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from functools import partial, reduce
1717
from inspect import cleandoc
1818
from itertools import chain
19-
from types import MappingProxyType
19+
from types import MappingProxyType, ModuleType
2020
from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping, Union
2121

2222
from .._path import StrPath
@@ -275,7 +275,7 @@ def _valid_command_options(cmdclass: Mapping = EMPTY) -> dict[str, set[str]]:
275275
return valid_options
276276

277277

278-
def _load_ep(ep: metadata.EntryPoint) -> tuple[str, type] | None:
278+
def _load_ep(ep: metadata.EntryPoint) -> tuple[str, ModuleType] | None:
279279
if ep.value.startswith("wheel.bdist_wheel"):
280280
# Ignore deprecated entrypoint from wheel and avoid warning pypa/wheel#631
281281
# TODO: remove check when `bdist_wheel` has been fully removed from pypa/wheel

setuptools/config/pyprojecttoml.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,10 @@ def _obtain(self, dist: Distribution, field: str, package_dir: Mapping[str, str]
303303
def _obtain_version(self, dist: Distribution, package_dir: Mapping[str, str]):
304304
# Since plugins can set version, let's silently skip if it cannot be obtained
305305
if "version" in self.dynamic and "version" in self.dynamic_cfg:
306-
return _expand.version(self._obtain(dist, "version", package_dir))
306+
return _expand.version(
307+
# We already do an early check for the presence of "version"
308+
self._obtain(dist, "version", package_dir) # pyright: ignore[reportArgumentType]
309+
)
307310
return None
308311

309312
def _obtain_readme(self, dist: Distribution) -> dict[str, str] | None:
@@ -313,9 +316,10 @@ def _obtain_readme(self, dist: Distribution) -> dict[str, str] | None:
313316
dynamic_cfg = self.dynamic_cfg
314317
if "readme" in dynamic_cfg:
315318
return {
319+
# We already do an early check for the presence of "readme"
316320
"text": self._obtain(dist, "readme", {}),
317321
"content-type": dynamic_cfg["readme"].get("content-type", "text/x-rst"),
318-
}
322+
} # pyright: ignore[reportReturnType]
319323

320324
self._ensure_previously_set(dist, "readme")
321325
return None

0 commit comments

Comments
 (0)