Skip to content

Commit d9e413e

Browse files
committed
Generate distutils-stubs on install
1 parent 0cffd61 commit d9e413e

File tree

15 files changed

+44
-34
lines changed

15 files changed

+44
-34
lines changed

mypy.ini

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
strict = False
66

77
# Early opt-in even when strict = False
8-
# warn_unused_ignores = True # Disabled until we have distutils stubs for Python 3.12+
8+
# warn_unused_ignores = True # Disabled as long as there's inconsistent typing issues between pypa and stdlib's distutils
99
warn_redundant_casts = True
1010
enable_error_code = ignore-without-code
1111

@@ -48,14 +48,6 @@ disable_error_code =
4848
[mypy-pkg_resources.tests.*]
4949
disable_error_code = import-not-found
5050

51-
# - distutils doesn't exist on Python 3.12, unfortunately, this means typing
52-
# will be missing for subclasses of distutils on Python 3.12 until either:
53-
# - support for `SETUPTOOLS_USE_DISTUTILS=stdlib` is dropped (#3625)
54-
# for setuptools to import `_distutils` directly
55-
# - or non-stdlib distutils typings are exposed
56-
[mypy-distutils.*]
57-
ignore_missing_imports = True
58-
5951
# - wheel: does not intend on exposing a programmatic API https://github.com/pypa/wheel/pull/610#issuecomment-2081687671
6052
[mypy-wheel.*]
6153
follow_untyped_imports = True

newsfragments/4861.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``setuptools`` now provide its own ``distutils-stubs`` instead of relying on typeshed -- by :user:`Avasam`

pyrightconfig.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
],
1313
// Our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually.
1414
// "pythonVersion": "3.9",
15+
// Allow using distutils-stubs on Python 3.12+
16+
"reportMissingModuleSource": false,
1517
// For now we don't mind if mypy's `type: ignore` comments accidentally suppresses pyright issues
1618
"enableTypeIgnoreComments": true,
1719
"typeCheckingMode": "basic",

setup.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import os
44
import sys
55
import textwrap
6+
from pathlib import Path
67

78
import setuptools
89
from setuptools.command.install import install
@@ -37,6 +38,22 @@ def pypi_link(pkg_filename):
3738
return '/'.join(parts)
3839

3940

41+
vendored_distutils_path = Path(here) / "setuptools" / "_distutils"
42+
43+
44+
def generate_distutils_stubs(destination: Path) -> None:
45+
for path in vendored_distutils_path.rglob("*.py"):
46+
relative_path = path.relative_to(vendored_distutils_path)
47+
if relative_path.parts[0] == "tests":
48+
continue
49+
stub_path = (destination / relative_path).with_suffix(".pyi")
50+
stub_path.parent.mkdir(parents=True, exist_ok=True)
51+
module = "setuptools._distutils." + str(relative_path.with_suffix("")).replace(
52+
os.sep, "."
53+
).removesuffix(".__init__")
54+
stub_path.write_text(f"from {module} import *\n")
55+
56+
4057
class install_with_pth(install):
4158
"""
4259
Custom install command to install a .pth file for distutils patching.
@@ -68,6 +85,10 @@ def initialize_options(self):
6885
install.initialize_options(self)
6986
self.extra_path = self._pth_name, self._pth_contents
7087

88+
def run(self):
89+
install.run(self)
90+
generate_distutils_stubs(Path(self.install_lib) / 'distutils-stubs')
91+
7192
def finalize_options(self):
7293
install.finalize_options(self)
7394
self._restore_install_lib()

setuptools/__init__.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
"""Extensions to the 'distutils' for large or complex distributions"""
2-
# mypy: disable_error_code=override
3-
# Command.reinitialize_command has an extra **kw param that distutils doesn't have
4-
# Can't disable on the exact line because distutils doesn't exists on Python 3.12
5-
# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any,
6-
# and a [unused-ignore] to be raised on 3.12+
72

83
from __future__ import annotations
94

@@ -211,7 +206,7 @@ def ensure_string_list(self, option: str) -> None:
211206
f"'{option}' must be a list of strings (got {val!r})"
212207
)
213208

214-
@overload
209+
@overload # type: ignore[override] # extra **kw param that distutils doesn't have
215210
def reinitialize_command(
216211
self, command: str, reinit_subcommands: bool = False, **kw
217212
) -> _Command: ...
@@ -224,7 +219,7 @@ def reinitialize_command(
224219
) -> _Command:
225220
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
226221
vars(cmd).update(kw)
227-
return cmd # pyright: ignore[reportReturnType] # pypa/distutils#307
222+
return cmd
228223

229224
@abstractmethod
230225
def initialize_options(self) -> None:

setuptools/build_meta.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ def patch(cls):
9191
for the duration of this context.
9292
"""
9393
orig = distutils.core.Distribution
94-
distutils.core.Distribution = cls # type: ignore[misc] # monkeypatching
94+
distutils.core.Distribution = cls
9595
try:
9696
yield
9797
finally:
98-
distutils.core.Distribution = orig # type: ignore[misc] # monkeypatching
98+
distutils.core.Distribution = orig
9999

100100

101101
@contextlib.contextmanager

setuptools/command/build_ext.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def setup_shlib_compiler(self):
248248
compiler.set_link_objects(self.link_objects)
249249

250250
# hack so distutils' build_extension() builds a library instead
251-
compiler.link_shared_object = link_shared_object.__get__(compiler) # type: ignore[method-assign]
251+
compiler.link_shared_object = link_shared_object.__get__(compiler)
252252

253253
def get_export_symbols(self, ext):
254254
if isinstance(ext, Library):

setuptools/command/sdist.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class sdist(orig.sdist):
5050
]
5151

5252
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
53-
negative_opt: ClassVar[dict[str, str]] = {}
53+
negative_opt: ClassVar[dict[str, str]] = {} # type: ignore[misc] # TODO: Fix upstream
5454

5555
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
5656
READMES = tuple(f'README{ext}' for ext in README_EXTENSIONS)

setuptools/command/setopt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def edit_config(filename, settings, dry_run=False):
3737
"""
3838
log.debug("Reading configuration from %s", filename)
3939
opts = configparser.RawConfigParser()
40-
opts.optionxform = lambda optionstr: optionstr # type: ignore[method-assign] # overriding method
40+
opts.optionxform = lambda optionstr: optionstr
4141
_cfg_read_utf8_with_fallback(opts, filename)
4242

4343
for section, options in settings.items():

setuptools/config/setupcfg.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from collections import defaultdict
1818
from collections.abc import Iterable, Iterator
1919
from functools import partial, wraps
20-
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar, cast
20+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar
2121

2222
from packaging.markers import default_environment as marker_env
2323
from packaging.requirements import InvalidRequirement, Requirement
@@ -101,8 +101,7 @@ def _apply(
101101
filenames = [*other_files, filepath]
102102

103103
try:
104-
# TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed
105-
_Distribution.parse_config_files(dist, filenames=cast(list[str], filenames))
104+
_Distribution.parse_config_files(dist, filenames=filenames) # type: ignore[arg-type] # Vendored version of distutils supports PathLike
106105
handlers = parse_configuration(
107106
dist, dist.command_options, ignore_option_errors=ignore_option_errors
108107
)

0 commit comments

Comments
 (0)