From 504ddd85d5315dab6d0411f1d49437080f867b7e Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 18 Dec 2025 12:13:59 -0500 Subject: [PATCH 1/4] Fully typed setuptools setup method --- stubs/setuptools/setuptools/__init__.pyi | 150 ++++++++++++++++------- 1 file changed, 104 insertions(+), 46 deletions(-) diff --git a/stubs/setuptools/setuptools/__init__.pyi b/stubs/setuptools/setuptools/__init__.pyi index d8d0b22c3048..58fb1d70bf7f 100644 --- a/stubs/setuptools/setuptools/__init__.pyi +++ b/stubs/setuptools/setuptools/__init__.pyi @@ -1,10 +1,12 @@ -from _typeshed import Incomplete +from _typeshed import StrPath from abc import abstractmethod -from collections.abc import Mapping, Sequence -from typing import Any, Literal, TypedDict, TypeVar, overload, type_check_only -from typing_extensions import NotRequired +from collections.abc import ItemsView, Iterable, Mapping, Sequence +from typing import Any, Literal, Protocol, TypedDict, TypeVar, overload, type_check_only +from typing_extensions import Never, NotRequired from ._distutils.cmd import Command as _Command +from ._distutils.dist import Distribution as _Distribution +from ._distutils.extension import Extension as _Extension from .command.alias import alias from .command.bdist_egg import bdist_egg from .command.bdist_rpm import bdist_rpm @@ -33,6 +35,11 @@ from .extension import Extension as Extension from .warnings import SetuptoolsDeprecationWarning as SetuptoolsDeprecationWarning _CommandT = TypeVar("_CommandT", bound=_Command) +_DistributionT = TypeVar("_DistributionT", bound=_Distribution, default=Distribution) +_T = TypeVar("_T") +_KT = TypeVar("_KT") +_VT = TypeVar("_VT") +_VT_co = TypeVar("_VT_co", covariant=True) __all__ = [ "setup", @@ -47,6 +54,23 @@ __all__ = [ __version__: str +@type_check_only +class _DictLike(Protocol[_KT, _VT]): + @overload + def get(self, key: _KT, /) -> _VT | None: ... + @overload + def get(self, key: _KT, default: _VT, /) -> _VT: ... + @overload + def get(self, key: _KT, default: _T, /) -> _VT | _T: ... + def items(self) -> ItemsView[_KT, _VT]: ... + def keys(self) -> Iterable[_KT]: ... + def __getitem__(self, key: _KT, /) -> _VT: ... + def __contains__(self, x: Any, /) -> bool: ... + +@type_check_only +class _MutableDictLike(_DictLike[_KT, _VT], Protocol): + def __setitem__(self, key: _KT, value: _VT, /) -> None: ... + @type_check_only class _BuildInfo(TypedDict): sources: list[str] | tuple[str, ...] @@ -60,49 +84,83 @@ find_namespace_packages = _Finder.find def setup( *, - name: str = ..., - version: str = ..., - description: str = ..., - long_description: str = ..., - long_description_content_type: str = ..., - author: str = ..., - author_email: str = ..., - maintainer: str = ..., - maintainer_email: str = ..., - url: str = ..., - download_url: str = ..., - packages: list[str] = ..., - py_modules: list[str] = ..., - scripts: list[str] = ..., - ext_modules: Sequence[Extension] = ..., - classifiers: list[str] = ..., - distclass: type[Distribution] = ..., - script_name: str = ..., - script_args: list[str] = ..., - options: Mapping[str, Incomplete] = ..., - license: str = ..., - keywords: list[str] | str = ..., - platforms: list[str] | str = ..., - cmdclass: Mapping[str, type[_Command]] = ..., - data_files: list[tuple[str, list[str]]] = ..., - package_dir: Mapping[str, str] = ..., - obsoletes: list[str] = ..., - provides: list[str] = ..., - requires: list[str] = ..., - command_packages: list[str] = ..., - command_options: Mapping[str, Mapping[str, tuple[Incomplete, Incomplete]]] = ..., - package_data: Mapping[str, list[str]] = ..., - include_package_data: bool = ..., - # libraries for `Distribution` or `build_clib`, not `Extension`, `build_ext` or `CCompiler` - libraries: list[tuple[str, _BuildInfo]] = ..., - headers: list[str] = ..., - ext_package: str = ..., - include_dirs: list[str] = ..., - password: str = ..., - fullname: str = ..., + # Attributes from distutils.dist.DistributionMetadata.set_* + # These take priority over attributes from distutils.dist.DistributionMetadata.__init__ + keywords: str | Iterable[str] = ..., + platforms: str | Iterable[str] = ..., + classifiers: str | Iterable[str] = ..., + requires: Iterable[str] = ..., + provides: Iterable[str] = ..., + obsoletes: Iterable[str] = ..., + # Attributes from distutils.dist.DistributionMetadata.__init__ + # These take priority over attributes from distutils.dist.Distribution.__init__ + name: str | None = None, + version: str | None = None, + author: str | None = None, + author_email: str | None = None, + maintainer: str | None = None, + maintainer_email: str | None = None, + url: str | None = None, + license: str | None = None, + description: str | None = None, + long_description: str | None = None, + download_url: str | None = None, + # Attributes from distutils.dist.Distribution.__init__ (except self.metadata) + # These take priority over attributes from distutils.dist.Distribution.display_option_names + verbose=True, + dry_run=False, + help=False, + cmdclass: _MutableDictLike[str, type[_Command]] = {}, + command_packages: str | list[str] | None = None, + script_name: StrPath | None = ..., # default is actually set in distutils.core.setup + script_args: list[str] | None = ..., # default is actually set in distutils.core.setup + command_options: _MutableDictLike[str, _DictLike[str, tuple[str, str]]] = {}, + packages: list[str] | None = None, + package_dir: Mapping[str, str] | None = None, + py_modules: list[str] | None = None, + libraries: list[tuple[str, _BuildInfo]] | None = None, + headers: list[str] | None = None, + ext_modules: Sequence[_Extension] | None = None, + ext_package: str | None = None, + include_dirs: list[str] | None = None, + extra_path=None, + scripts: list[str] | None = None, + data_files: list[tuple[str, Sequence[str]]] | None = None, + password: str = "", + command_obj: _MutableDictLike[str, _Command] = {}, + have_run: _MutableDictLike[str, bool] = {}, + # kwargs used directly in distutils.dist.Distribution.__init__ + options: Mapping[str, Mapping[str, str]] | None = None, + licence: Never = ..., # Deprecated + # Attributes from distutils.dist.Distribution.display_option_names + # (this can more easily be copied from the `if TYPE_CHECKING` block) + help_commands: bool = False, + fullname: str | Literal[False] = False, + contact: str | Literal[False] = False, + contact_email: str | Literal[False] = False, + # kwargs used directly in setuptools.dist.Distribution.__init__ + # and attributes from setuptools.dist.Distribution.__init__ + package_data: _DictLike[str, list[str]] = {}, + dist_files: list[tuple[str, str, str]] = [], + include_package_data: bool | None = None, + exclude_package_data: _DictLike[str, list[str]] | None = None, + src_root: str | None = None, + dependency_links: list[str] = [], + setup_requires: list[str] = [], + # From Distribution._DISTUTILS_UNSUPPORTED_METADATA set in Distribution._set_metadata_defaults + long_description_content_type: str | None = None, + project_urls={}, + provides_extras={}, + license_expression=None, + license_file=None, + license_files=None, + install_requires=[], + extras_require={}, + # kwargs used directly in distutils.core.setup + distclass: type[_DistributionT] = Distribution, # type: ignore[assignment] # noqa: Y011 # Custom Distributions could accept more params - **attrs: Any, -) -> Distribution: ... + **attrs: object, +) -> _DistributionT: ... class Command(_Command): command_consumes_arguments: bool From 323ceed092cf85a4b0fc2750b7c36a38daa85ad5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 18 Dec 2025 12:59:04 -0500 Subject: [PATCH 2/4] Back to _VT_co as original --- stubs/setuptools/setuptools/__init__.pyi | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stubs/setuptools/setuptools/__init__.pyi b/stubs/setuptools/setuptools/__init__.pyi index 58fb1d70bf7f..82212792ff90 100644 --- a/stubs/setuptools/setuptools/__init__.pyi +++ b/stubs/setuptools/setuptools/__init__.pyi @@ -55,16 +55,16 @@ __all__ = [ __version__: str @type_check_only -class _DictLike(Protocol[_KT, _VT]): +class _DictLike(Protocol[_KT, _VT_co]): @overload - def get(self, key: _KT, /) -> _VT | None: ... + def get(self, key: _KT, /) -> _VT_co | None: ... @overload - def get(self, key: _KT, default: _VT, /) -> _VT: ... + def get(self, key: _KT, default: _VT_co, /) -> _VT_co: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] # Covariant type as parameter @overload - def get(self, key: _KT, default: _T, /) -> _VT | _T: ... - def items(self) -> ItemsView[_KT, _VT]: ... + def get(self, key: _KT, default: _T, /) -> _VT_co | _T: ... + def items(self) -> ItemsView[_KT, _VT_co]: ... def keys(self) -> Iterable[_KT]: ... - def __getitem__(self, key: _KT, /) -> _VT: ... + def __getitem__(self, key: _KT, /) -> _VT_co: ... def __contains__(self, x: Any, /) -> bool: ... @type_check_only From da5f4d2ea2236113a64f9d436fd1e9406a5923f0 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 18 Dec 2025 13:11:21 -0500 Subject: [PATCH 3/4] ignore mypy misc for Covariant type as parameter --- stubs/setuptools/setuptools/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/setuptools/setuptools/__init__.pyi b/stubs/setuptools/setuptools/__init__.pyi index 82212792ff90..99c830f4fea1 100644 --- a/stubs/setuptools/setuptools/__init__.pyi +++ b/stubs/setuptools/setuptools/__init__.pyi @@ -55,7 +55,7 @@ __all__ = [ __version__: str @type_check_only -class _DictLike(Protocol[_KT, _VT_co]): +class _DictLike(Protocol[_KT, _VT_co]): # type: ignore[misc] # Covariant type as parameter @overload def get(self, key: _KT, /) -> _VT_co | None: ... @overload From 2b7488042f025c7b6c832c844685f00cd4ad03b5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 19 Dec 2025 19:45:10 -0500 Subject: [PATCH 4/4] Update stubs/setuptools/setuptools/__init__.pyi Co-authored-by: Sebastian Rittau --- stubs/setuptools/setuptools/__init__.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/setuptools/setuptools/__init__.pyi b/stubs/setuptools/setuptools/__init__.pyi index 99c830f4fea1..eaea1f558070 100644 --- a/stubs/setuptools/setuptools/__init__.pyi +++ b/stubs/setuptools/setuptools/__init__.pyi @@ -159,7 +159,7 @@ def setup( # kwargs used directly in distutils.core.setup distclass: type[_DistributionT] = Distribution, # type: ignore[assignment] # noqa: Y011 # Custom Distributions could accept more params - **attrs: object, + **attrs: Any, ) -> _DistributionT: ... class Command(_Command):