Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Statically alias distutils-stubs to setuptools._distutils #4704

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
strict = False

# Early opt-in even when strict = False
# warn_unused_ignores = True # Disabled until we have distutils stubs for Python 3.12+
warn_unused_ignores = True
warn_redundant_casts = True
enable_error_code = ignore-without-code

Expand All @@ -18,6 +18,9 @@ disable_error_code =

## local

# Use our custom stubs for distutils
mypy_path = $MYPY_CONFIG_FILE_DIR/typings

# CI should test for all versions, local development gets hints for oldest supported
# But our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually.
# python_version = 3.8
Expand Down Expand Up @@ -48,17 +51,10 @@ disable_error_code =
[mypy-pkg_resources.tests.*]
disable_error_code = import-not-found

# - distutils doesn't exist on Python 3.12, unfortunately, this means typing
# will be missing for subclasses of distutils on Python 3.12 until either:
# - support for `SETUPTOOLS_USE_DISTUTILS=stdlib` is dropped (#3625)
# for setuptools to import `_distutils` directly
# - or non-stdlib distutils typings are exposed
[mypy-distutils.*]
ignore_missing_imports = True

# - wheel: does not intend on exposing a programmatic API https://github.com/pypa/wheel/pull/610#issuecomment-2081687671
[mypy-wheel.*]
ignore_missing_imports = True

# - The following are not marked as py.typed:
# - jaraco: Since mypy 1.12, the root name of the untyped namespace package gets called-out too
# - jaraco.develop: https://github.com/jaraco/jaraco.develop/issues/22
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ type = [

# local

# Referenced in distutils-stubs
"types-docutils",
# pin mypy version so a new version doesn't suddenly cause the CI to fail,
# until types-setuptools is removed from typeshed.
# For help with static-typing issues, or mypy update, ping @Avasam
Expand Down Expand Up @@ -203,6 +205,8 @@ include-package-data = true
include = [
"setuptools*",
"pkg_resources*",
# TODO: Include distutils stubs with package once we're confident in them
# "typings/distutils-stubs",
"_distutils_hack*",
]
exclude = [
Expand Down
2 changes: 2 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
],
// Our testing setup doesn't allow passing CLI arguments, so local devs have to set this manually.
// "pythonVersion": "3.8",
// Allow using distutils-stubs on Python 3.12+
"reportMissingModuleSource": false,
// For now we don't mind if mypy's `type: ignore` comments accidentally suppresses pyright issues
"enableTypeIgnoreComments": true,
"typeCheckingMode": "basic",
Expand Down
3 changes: 3 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ ignore = [
# Only enforcing return type annotations for public functions
"ANN202", # missing-return-type-private-function
"ANN204", # missing-return-type-special-method
# Typeshed doesn't want complex or non-literal defaults for maintenance and testing reasons.
# This doesn't affect us, let's have more complete stubs.
"PYI011", # typed-argument-default-in-stub

# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
"W191",
Expand Down
7 changes: 1 addition & 6 deletions setuptools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
"""Extensions to the 'distutils' for large or complex distributions"""
# mypy: disable_error_code=override
# Command.reinitialize_command has an extra **kw param that distutils doesn't have
# Can't disable on the exact line because distutils doesn't exists on Python 3.12
# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any,
# and a [unused-ignore] to be raised on 3.12+

from __future__ import annotations

Expand Down Expand Up @@ -226,7 +221,7 @@ def reinitialize_command(
) -> _Command:
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
vars(cmd).update(kw)
return cmd
return cmd # pyright: ignore[reportReturnType] # pypa/distutils#307
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


@abstractmethod
def initialize_options(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions setuptools/command/build_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def finalize_options(self):
del self.__dict__['data_files']
self.__updated_files = []

def copy_file( # type: ignore[override] # No overload, str support only
def copy_file(
self,
infile: StrPath,
outfile: StrPath,
Expand Down Expand Up @@ -143,7 +143,7 @@ def find_data_files(self, package, src_dir):
)
return self.exclude_data_files(package, src_dir, files)

def get_outputs(self, include_bytecode: bool = True) -> list[str]: # type: ignore[override] # Using a real boolean instead of 0|1
def get_outputs(self, include_bytecode: bool = True) -> list[str]:
"""See :class:`setuptools.commands.build.SubCommand`"""
if self.editable_mode:
return list(self.get_output_mapping().keys())
Expand Down
3 changes: 2 additions & 1 deletion setuptools/command/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,5 @@ def do_egg_install(self):
# XXX Python 3.1 doesn't see _nc if this is inside the class
install.sub_commands = [
cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc
] + install.new_commands
] + install.new_commands # type: ignore[operator]
# TODO: Type sub_commands/new_commands to avoid variance issues in pypa/distutils (like python/typeshed#11951)
7 changes: 3 additions & 4 deletions setuptools/command/install_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ def copy_tree(
self,
infile: StrPath,
outfile: str,
# override: Using actual booleans
preserve_mode: bool = True, # type: ignore[override]
preserve_times: bool = True, # type: ignore[override]
preserve_symlinks: bool = False, # type: ignore[override]
preserve_mode: bool = True,
preserve_times: bool = True,
preserve_symlinks: bool = False,
level: object = 1,
) -> list[str]:
assert preserve_mode and preserve_times and not preserve_symlinks
Expand Down
6 changes: 4 additions & 2 deletions setuptools/command/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ class sdist(orig.sdist):
]

distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
negative_opt: ClassVar[dict[str, str]] = {}
# TODO: Mark class-level mutables as ClassVars in pypa/distutils (like python/typeshed#12403)
negative_opt: ClassVar[dict[str, str]] = {} # type: ignore[misc]

README_EXTENSIONS = ['', '.rst', '.txt', '.md']
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
# TODO: Mark some class-level tuples as as not fixed-length pypa/distutils (like python/typeshed#12403)
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) # type: ignore[assignment]

def run(self):
self.run_command('egg_info')
Expand Down
5 changes: 1 addition & 4 deletions setuptools/config/setupcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@
Generic,
Iterable,
Iterator,
List,
Tuple,
TypeVar,
cast,
)

from packaging.markers import default_environment as marker_env
Expand Down Expand Up @@ -112,8 +110,7 @@ def _apply(
filenames = [*other_files, filepath]

try:
# TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed
_Distribution.parse_config_files(dist, filenames=cast(List[str], filenames))
_Distribution.parse_config_files(dist, filenames=filenames)
handlers = parse_configuration(
dist, dist.command_options, ignore_option_errors=ignore_option_errors
)
Expand Down
8 changes: 4 additions & 4 deletions setuptools/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
BaseError = _distutils_errors.DistutilsError


class InvalidConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
class InvalidConfigError(OptionError):
"""Error used for invalid configurations."""


class RemovedConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
class RemovedConfigError(OptionError):
"""Error used for configurations that were deprecated and removed."""


class RemovedCommandError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
class RemovedCommandError(BaseError, RuntimeError):
"""Error used for commands that have been removed in setuptools.

Since ``setuptools`` is built on ``distutils``, simply removing a command
Expand All @@ -48,7 +48,7 @@ class RemovedCommandError(BaseError, RuntimeError): # type: ignore[valid-type,
"""


class PackageDiscoveryError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
class PackageDiscoveryError(BaseError, RuntimeError):
"""Impossible to perform automatic discovery of packages and/or modules.

The current project layout or given discovery options can lead to problems when
Expand Down
7 changes: 1 addition & 6 deletions setuptools/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,7 @@ def __init__(
# The *args is needed for compatibility as calls may use positional
# arguments. py_limited_api may be set only via keyword.
self.py_limited_api = py_limited_api
super().__init__(
name,
sources, # type: ignore[arg-type] # Vendored version of setuptools supports PathLike
*args,
**kw,
)
super().__init__(name, sources, *args, **kw)

def _convert_pyx_sources_to_lang(self):
"""
Expand Down
28 changes: 28 additions & 0 deletions tools/update_distutils_stubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import os
import shutil
from pathlib import Path

_vendored_distutils_path = Path(__file__).parent.parent / "setuptools" / "_distutils"
_distutils_stubs_path = Path(__file__).parent.parent / "typings" / "distutils-stubs"


def main():
if _distutils_stubs_path.exists():
shutil.rmtree(_distutils_stubs_path)
_distutils_stubs_path.mkdir(parents=True)
(_distutils_stubs_path / "ruff.toml").write_text('[lint]\nignore = ["F403"]')
for path in _vendored_distutils_path.rglob("*.py"):
relative_path = path.relative_to(_vendored_distutils_path)
if relative_path.parts[0] == "tests":
continue
stub_path = (_distutils_stubs_path / relative_path).with_suffix(".pyi")
stub_path.parent.mkdir(exist_ok=True)
module = (
"setuptools._distutils."
+ str(relative_path.with_suffix("")).replace(os.sep, ".")
).removesuffix(".__init__")
stub_path.write_text(f"from {module} import *\n")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions typings/distutils-stubs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/_log.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils._log import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/_macos_compat.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils._macos_compat import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/_modified.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils._modified import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/_msvccompiler.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils._msvccompiler import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/archive_util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.archive_util import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/ccompiler.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.ccompiler import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/cmd.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.cmd import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/_framework_compat.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command._framework_compat import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/bdist.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.bdist import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/bdist_dumb.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.bdist_dumb import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/bdist_rpm.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.bdist_rpm import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/build.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.build import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/build_clib.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.build_clib import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/build_ext.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.build_ext import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/build_py.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.build_py import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/build_scripts.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.build_scripts import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/check.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.check import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/clean.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.clean import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/config.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.config import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install_data.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install_data import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install_egg_info.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install_egg_info import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install_headers.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install_headers import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install_lib.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install_lib import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/install_scripts.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.install_scripts import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/command/sdist.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.command.sdist import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/compat/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.compat import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/compat/py38.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.compat.py38 import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/compat/py39.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.compat.py39 import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/core.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.core import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/cygwinccompiler.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.cygwinccompiler import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/debug.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.debug import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/dep_util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.dep_util import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/dir_util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.dir_util import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/dist.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.dist import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/errors.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.errors import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/extension.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.extension import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/fancy_getopt.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.fancy_getopt import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/file_util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.file_util import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/filelist.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.filelist import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/log.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.log import *
2 changes: 2 additions & 0 deletions typings/distutils-stubs/ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[lint]
ignore = ["F403"]
1 change: 1 addition & 0 deletions typings/distutils-stubs/spawn.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.spawn import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/sysconfig.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.sysconfig import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/text_file.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.text_file import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/unixccompiler.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.unixccompiler import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/util.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.util import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/version.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.version import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/versionpredicate.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.versionpredicate import *
1 change: 1 addition & 0 deletions typings/distutils-stubs/zosccompiler.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from setuptools._distutils.zosccompiler import *
Loading