-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[Waiting for distutils sync] Generate distutils-stubs
on install
#4861
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
base: main
Are you sure you want to change the base?
Changes from all commits
d9e413e
1ac7ac8
4e82d63
05e7d53
4b7408e
74e7ea8
87edb0f
48f0a67
570a4ae
a31c493
f2a1e41
96a6fcf
4df7381
d982abb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
"""Generate distutils stub files inside the source directory before packaging. | ||
We have to do this as a custom build backend for PEP 660 editable installs. | ||
""" | ||
|
||
from __future__ import annotations | ||
|
||
import os | ||
import shutil | ||
from pathlib import Path | ||
|
||
from setuptools._path import StrPath | ||
from setuptools.build_meta import * # noqa: F403 # expose everything | ||
from setuptools.build_meta import ( | ||
_ConfigSettings, | ||
build_editable as _build_editable, | ||
build_sdist as _build_sdist, | ||
build_wheel as _build_wheel, | ||
) | ||
|
||
_vendored_distutils_path = Path(__file__).parent / "setuptools" / "_distutils" | ||
_distutils_stubs_path = Path(__file__).parent / "distutils-stubs" | ||
|
||
|
||
def _regenerate_distutils_stubs() -> None: | ||
shutil.rmtree(_distutils_stubs_path, ignore_errors=True) | ||
_distutils_stubs_path.mkdir(parents=True) | ||
(_distutils_stubs_path / ".gitignore").write_text("*") | ||
(_distutils_stubs_path / "ruff.toml").write_text('[lint]\nignore = ["F403"]') | ||
(_distutils_stubs_path / "py.typed").write_text("\n") | ||
for path in _vendored_distutils_path.rglob("*.py"): | ||
relative_path = path.relative_to(_vendored_distutils_path) | ||
if "tests" in relative_path.parts: | ||
continue | ||
stub_path = _distutils_stubs_path / relative_path.with_suffix(".pyi") | ||
stub_path.parent.mkdir(parents=True, exist_ok=True) | ||
module = "setuptools._distutils." + str(relative_path.with_suffix("")).replace( | ||
os.sep, "." | ||
).removesuffix(".__init__") | ||
if str(relative_path) == "__init__.py": | ||
# Work around python/mypy#18775 | ||
stub_path.write_text("""\ | ||
from typing import Final | ||
|
||
__version__: Final[str] | ||
""") | ||
else: | ||
stub_path.write_text(f"from {module} import *\n") | ||
|
||
|
||
def build_wheel( # type: ignore[no-redef] | ||
wheel_directory: StrPath, | ||
config_settings: _ConfigSettings = None, | ||
metadata_directory: StrPath | None = None, | ||
) -> str: | ||
_regenerate_distutils_stubs() | ||
return _build_wheel(wheel_directory, config_settings, metadata_directory) | ||
|
||
|
||
def build_sdist( # type: ignore[no-redef] | ||
sdist_directory: StrPath, | ||
config_settings: _ConfigSettings = None, | ||
) -> str: | ||
_regenerate_distutils_stubs() | ||
return _build_sdist(sdist_directory, config_settings) | ||
|
||
|
||
def build_editable( # type: ignore[no-redef] | ||
wheel_directory: StrPath, | ||
config_settings: _ConfigSettings = None, | ||
metadata_directory: StrPath | None = None, | ||
) -> str: | ||
_regenerate_distutils_stubs() | ||
return _build_editable(wheel_directory, config_settings, metadata_directory) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
``setuptools`` now provide its own ``distutils-stubs`` instead of relying on typeshed -- by :user:`Avasam` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -133,7 +133,7 @@ def do_install_data(self) -> None: | |
site_packages = os.path.normcase(os.path.realpath(_get_purelib())) | ||
old, self.distribution.data_files = self.distribution.data_files, [] | ||
|
||
for item in old: | ||
for item in old or (): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would previously raise if |
||
if isinstance(item, tuple) and len(item) == 2: | ||
if os.path.isabs(item[0]): | ||
realpath = os.path.realpath(item[0]) | ||
|
@@ -235,7 +235,7 @@ def run(self) -> None: # noqa: C901 # is too complex (14) # FIXME | |
self.egg_output, | ||
archive_root, | ||
verbose=self.verbose, | ||
dry_run=self.dry_run, # type: ignore[arg-type] # Is an actual boolean in vendored _distutils | ||
dry_run=self.dry_run, | ||
mode=self.gen_header(), | ||
) | ||
if not self.keep_temp: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,11 +10,11 @@ | |
from functools import partial | ||
from glob import glob | ||
from pathlib import Path | ||
from typing import Any | ||
from typing import Any, Literal, overload | ||
|
||
from more_itertools import unique_everseen | ||
|
||
from .._path import StrPath, StrPathT | ||
from .._path import BytesPath, BytesPathT, StrPath, StrPathT | ||
from ..dist import Distribution | ||
from ..warnings import SetuptoolsDeprecationWarning | ||
|
||
|
@@ -50,21 +50,50 @@ def finalize_options(self) -> None: | |
if 'data_files' in self.__dict__: | ||
del self.__dict__['data_files'] | ||
|
||
def copy_file( # type: ignore[override] # No overload, no bytes support | ||
@overload # type: ignore[override] # Truthy link with bytes is not supported, unlike supertype | ||
def copy_file( | ||
self, | ||
infile: StrPath, | ||
outfile: StrPathT, | ||
preserve_mode: bool = True, | ||
preserve_times: bool = True, | ||
link: str | None = None, | ||
level: object = 1, | ||
) -> tuple[StrPathT | str, bool]: | ||
level: int = 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was typed as So either distutils should change it to |
||
) -> tuple[StrPathT | str, bool]: ... | ||
@overload | ||
def copy_file( | ||
self, | ||
infile: BytesPath, | ||
outfile: BytesPathT, | ||
preserve_mode: bool = True, | ||
preserve_times: bool = True, | ||
link: Literal[""] | None = None, | ||
level: int = 1, | ||
) -> tuple[BytesPathT | bytes, bool]: ... | ||
def copy_file( | ||
self, | ||
infile: StrPath | BytesPath, | ||
outfile: StrPath | BytesPath, | ||
preserve_mode: bool = True, | ||
preserve_times: bool = True, | ||
link: str | None = None, | ||
level: int = 1, | ||
) -> tuple[StrPath | BytesPath, bool]: | ||
# Overwrite base class to allow using links | ||
if link: | ||
infile = str(Path(infile).resolve()) | ||
outfile = str(Path(outfile).resolve()) # type: ignore[assignment] # Re-assigning a str when outfile is StrPath is ok | ||
return super().copy_file( # pyright: ignore[reportReturnType] # pypa/distutils#309 | ||
infile, outfile, preserve_mode, preserve_times, link, level | ||
# NOTE: Explanation for the type ignores: | ||
# 1. If link is truthy, then we only allow infile and outfile to be StrPath | ||
# 2. Re-assigning a str when outfile is StrPath is ok | ||
# We can't easily check for PathLike[str], so ignoring instead of asserting. | ||
infile = str(Path(infile).resolve()) # type: ignore[arg-type] | ||
outfile = str(Path(outfile).resolve()) # type: ignore[arg-type] | ||
return super().copy_file( # type: ignore[misc, type-var] # pyright: ignore[reportCallIssue] | ||
infile, # type: ignore[arg-type] # pyright: ignore[reportArgumentType] | ||
outfile, # pyright: ignore[reportArgumentType] | ||
preserve_mode, | ||
preserve_times, | ||
link, | ||
level, | ||
) | ||
|
||
def run(self) -> None: | ||
|
@@ -138,7 +167,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()) | ||
|
Uh oh!
There was an error while loading. Please reload this page.