Skip to content

Commit

Permalink
Typecheck using pyright
Browse files Browse the repository at this point in the history
  • Loading branch information
Avasam committed Feb 27, 2024
1 parent 4d0c77f commit d9f74a7
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 15 deletions.
31 changes: 31 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/pyright/main/packages/vscode-pyright/schemas/pyrightconfig.schema.json",
"exclude": [
"build",
".tox",
"**/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
],
// CI should test for all versions, local development gets hints for oldest supported
"pythonVersion": "3.8",
// For now we don't mind if mypy's `type: ignore` comments accidentally suppresses pyright issues
"enableTypeIgnoreComments": true,
"typeCheckingMode": "basic",
// For now, align with mypy's default of skipping unnanotated functions, only care about public API which should be annotated
"analyzeUnannotatedFunctions": false,
// TODO: Test environment is not yet properly configured to install all imported packages
"reportMissingImports": "none",
// Too many issues caused by vendoring and dynamic patching, still worth fixing when we can
"reportAttributeAccessIssue": "warning",
// Defered 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",
}
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ testing =
# for tools/finalize.py
jaraco.develop >= 7.21; python_version >= "3.9" and sys_platform != "cygwin"
pytest-home >= 0.5
pyright
# No Python 3.12 dependencies require importlib_metadata, but we need it for type-checking
importlib_metadata; python_version >= "3.12"
trove_classifiers
Expand Down
6 changes: 6 additions & 0 deletions setuptools/command/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,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 @@ -115,6 +118,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 @@ -128,6 +132,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 @@ -138,3 +143,4 @@ def get_output_mapping(self) -> Dict[str, str]:
Destination files should be strings in the form of
``"{build_lib}/destination/file/path"``.
"""
...
2 changes: 1 addition & 1 deletion setuptools/command/easy_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2012,7 +2012,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
8 changes: 6 additions & 2 deletions setuptools/config/pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,10 @@ def _obtain(self, dist: "Distribution", field: str, package_dir: Mapping[str, st
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") -> Optional[Dict[str, str]]:
Expand All @@ -307,9 +310,10 @@ def _obtain_readme(self, dist: "Distribution") -> Optional[Dict[str, str]]:
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 @@ -8,12 +8,14 @@
import sys
import types
from importlib import import_module
from typing import List, TypeVar
from typing import List, Optional, Type, TypeVar, Union, cast, overload

import distutils.filelist


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


__all__: List[str] = []
"""
Expand All @@ -36,25 +38,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: Union[type, types.FunctionType, object],
) -> Optional[Union[type, types.FunctionType]]:
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
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ setenv =
SETUPTOOLS_ENFORCE_DEPRECATION = {env:SETUPTOOLS_ENFORCE_DEPRECATION:0}
# ^-- Temporarily disable enforcement so CI don't fail on due dates
commands =
pyright .
pytest --mypy {posargs}
usedevelop = True
extras =
Expand Down

0 comments on commit d9f74a7

Please sign in to comment.