Skip to content

Commit

Permalink
Initial pyright config
Browse files Browse the repository at this point in the history
  • Loading branch information
Avasam committed Jun 17, 2024
1 parent d1bea1b commit 6c7ccd4
Show file tree
Hide file tree
Showing 11 changed files with 80 additions and 27 deletions.
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ testing = [
# for tools/finalize.py
'jaraco.develop >= 7.21; python_version >= "3.9" and sys_platform != "cygwin"',
"pytest-home >= 0.5",
"mypy==1.10.0", # pin mypy version so a new version doesn't suddenly cause the CI to fail
# pin type-checkers so a new version doesn't suddenly cause the CI to fail
"pyright == 1.1.364",
"mypy == 1.10.0",
# No Python 3.11 dependencies require tomli, but needed for type-checking since we import it directly
"tomli",
# No Python 3.12 dependencies require importlib_metadata, but needed for type-checking since we import it directly
Expand Down
33 changes: 33 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
"exclude": [
"build",
".tox",
".eggs",
"**/extern", // Vendored
"**/_vendor", // Vendored
"setuptools/_distutils", // Vendored
"**/tests", // Disabled as long as analyzeUnannotatedFunctions=false to reduce log spam
"**/_*", // Disabled as long as analyzeUnannotatedFunctions=false to reduce log spam
],
// Our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually.
// "pythonVersion": "3.8",
// For now we don't mind if mypy's `type: ignore` comments accidentally suppresses pyright issues
"enableTypeIgnoreComments": true,
"typeCheckingMode": "basic",
// Avoid raising issues when importing from "extern" modules, as those are added to path dynamically.
// https://github.com/pypa/setuptools/pull/3979#discussion_r1367968993
"reportMissingImports": "none",
// Too many issues caused by vendoring and dynamic patching, still worth fixing when we can
"reportAttributeAccessIssue": "warning",
// FIXME: A handful of reportOperatorIssue spread throughout the codebase
"reportOperatorIssue": "warning",
// Deferred initialization (initialize_options/finalize_options) causes many "potentially None" issues
// TODO: Fix with type-guards or by changing how it's initialized
"reportCallIssue": "warning",
"reportArgumentType": "warning",
"reportOptionalIterable": "warning",
"reportOptionalMemberAccess": "warning",
"reportGeneralTypeIssues": "warning",
"reportOptionalOperand": "warning",
}
6 changes: 6 additions & 0 deletions setuptools/command/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,15 @@ def finalize_options(self):

def initialize_options(self):
"""(Required by the original :class:`setuptools.Command` interface)"""
...

def finalize_options(self):
"""(Required by the original :class:`setuptools.Command` interface)"""
...

def run(self):
"""(Required by the original :class:`setuptools.Command` interface)"""
...

def get_source_files(self) -> list[str]:
"""
Expand All @@ -99,6 +102,7 @@ def get_source_files(self) -> list[str]:
with all the files necessary to build the distribution.
All files should be strings relative to the project root directory.
"""
...

def get_outputs(self) -> list[str]:
"""
Expand All @@ -112,6 +116,7 @@ def get_outputs(self) -> list[str]:
in ``get_output_mapping()`` plus files that are generated during the build
and don't correspond to any source file already present in the project.
"""
...

def get_output_mapping(self) -> dict[str, str]:
"""
Expand All @@ -122,3 +127,4 @@ def get_output_mapping(self) -> dict[str, str]:
Destination files should be strings in the form of
``"{build_lib}/destination/file/path"``.
"""
...
12 changes: 5 additions & 7 deletions setuptools/command/build_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,20 +153,18 @@ def _get_output_mapping(self) -> Iterator[tuple[str, str]]:
yield (output_cache, inplace_cache)

def get_ext_filename(self, fullname):
so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX')
so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX', '')
if so_ext:
filename = os.path.join(*fullname.split('.')) + so_ext
else:
filename = _build_ext.get_ext_filename(self, fullname)
so_ext = get_config_var('EXT_SUFFIX')
so_ext = str(get_config_var('EXT_SUFFIX'))

if fullname in self.ext_map:
ext = self.ext_map[fullname]
use_abi3 = ext.py_limited_api and get_abi3_suffix()
if use_abi3:
filename = filename[: -len(so_ext)]
so_ext = get_abi3_suffix()
filename = filename + so_ext
abi3_suffix = get_abi3_suffix()
if ext.py_limited_api and abi3_suffix: # Use abi3
filename = filename[: -len(so_ext)] + abi3_suffix
if isinstance(ext, Library):
fn, ext = os.path.splitext(filename)
return self.shlib_compiler.library_filename(fn, libtype)
Expand Down
4 changes: 2 additions & 2 deletions setuptools/command/easy_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -1792,7 +1792,7 @@ def auto_chmod(func, arg, exc):
return func(arg)
et, ev, _ = sys.exc_info()
# TODO: This code doesn't make sense. What is it trying to do?
raise (ev[0], ev[1] + (" %s %s" % (func, arg)))
raise (ev[0], ev[1] + (" %s %s" % (func, arg))) # pyright: ignore[reportOptionalSubscript, reportIndexIssue]


def update_dist_caches(dist_path, fix_zipimporter_caches):
Expand Down Expand Up @@ -2018,7 +2018,7 @@ def is_python_script(script_text, filename):


try:
from os import chmod as _chmod
from os import chmod as _chmod # pyright: ignore[reportAssignmentType] # Loosing type-safety w/ pyright, but that's ok
except ImportError:
# Jython compatibility
def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway
Expand Down
3 changes: 2 additions & 1 deletion setuptools/command/editable_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
from .install_scripts import install_scripts as install_scripts_cls

if TYPE_CHECKING:
from typing_extensions import Self
from .._vendor.wheel.wheelfile import WheelFile

_P = TypeVar("_P", bound=StrPath)
Expand Down Expand Up @@ -396,7 +397,7 @@ def _select_strategy(
class EditableStrategy(Protocol):
def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ...

def __enter__(self): ...
def __enter__(self) -> Self: ...

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

Expand Down
8 changes: 6 additions & 2 deletions setuptools/config/pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,10 @@ def _obtain(self, dist: Distribution, field: str, package_dir: Mapping[str, str]
def _obtain_version(self, dist: Distribution, package_dir: Mapping[str, str]):
# Since plugins can set version, let's silently skip if it cannot be obtained
if "version" in self.dynamic and "version" in self.dynamic_cfg:
return _expand.version(self._obtain(dist, "version", package_dir))
return _expand.version(
# We already do an early check for the presence of "version"
self._obtain(dist, "version", package_dir) # pyright: ignore[reportArgumentType]
)
return None

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

self._ensure_previously_set(dist, "readme")
return None
Expand Down
31 changes: 19 additions & 12 deletions setuptools/monkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import sys
import types
from importlib import import_module
from typing import TypeVar
from typing import Type, TypeVar, cast, overload

import distutils.filelist


_T = TypeVar("_T")
_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType)


__all__: list[str] = []
"""
Expand All @@ -38,25 +40,30 @@ def _get_mro(cls):
return inspect.getmro(cls)


def get_unpatched(item: _T) -> _T:
lookup = (
get_unpatched_class
if isinstance(item, type)
else get_unpatched_function
if isinstance(item, types.FunctionType)
else lambda item: None
)
return lookup(item)
@overload
def get_unpatched(item: _UnpatchT) -> _UnpatchT: ... # type: ignore[overload-overlap]
@overload
def get_unpatched(item: object) -> None: ...
def get_unpatched(
item: type | types.FunctionType | object,
) -> type | types.FunctionType | None:
if isinstance(item, type):
return get_unpatched_class(item)
if isinstance(item, types.FunctionType):
return get_unpatched_function(item)
return None


def get_unpatched_class(cls):
def get_unpatched_class(cls: type[_T]) -> type[_T]:
"""Protect against re-patching the distutils if reloaded
Also ensures that no other distutils extension monkeypatched the distutils
first.
"""
external_bases = (
cls for cls in _get_mro(cls) if not cls.__module__.startswith('setuptools')
cast(Type[_T], cls)
for cls in _get_mro(cls)
if not cls.__module__.startswith('setuptools')
)
base = next(external_bases)
if not base.__module__.startswith('distutils'):
Expand Down
2 changes: 1 addition & 1 deletion setuptools/package_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -1071,7 +1071,7 @@ def open_with_auth(url, opener=urllib.request.urlopen):
if scheme in ('http', 'https'):
auth, address = _splituser(netloc)
else:
auth = None
auth, address = (None, None)

if not auth:
cred = PyPIConfig().find_credential(url)
Expand Down
3 changes: 2 additions & 1 deletion setuptools/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ def _violation(self, operation, *args, **kw):
def _file(self, path, mode='r', *args, **kw):
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
self._violation("file", path, mode, *args, **kw)
return _file(path, mode, *args, **kw)
# Self-referential, can't be None
return _file(path, mode, *args, **kw) # pyright: ignore[reportOptionalCall]

def _open(self, path, mode='r', *args, **kw):
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ setenv =
PYTHONWARNDEFAULTENCODING = 1
SETUPTOOLS_ENFORCE_DEPRECATION = {env:SETUPTOOLS_ENFORCE_DEPRECATION:1}
commands =
pyright .
pytest {posargs}
usedevelop = True
extras =
Expand Down

0 comments on commit 6c7ccd4

Please sign in to comment.