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

Don't ignore missing stubs in setuptools #10058

Merged
55 changes: 27 additions & 28 deletions stubs/setuptools/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
pkg_resources.Distribution.__cmp__
pkg_resources.Distribution.activate
pkg_resources.Distribution.get_entry_map
pkg_resources.EggMetadata.__init__
pkg_resources.Environment.best_match
pkg_resources.Environment.obtain
pkg_resources.FileMetadata.__init__
# These are used like protocols, but forgot to specify "self" as the first method param
pkg_resources.IResourceProvider.get_resource_filename
pkg_resources.IResourceProvider.get_resource_stream
pkg_resources.IResourceProvider.get_resource_string
pkg_resources.IResourceProvider.has_resource
pkg_resources.IResourceProvider.resource_isdir
pkg_resources.IResourceProvider.resource_listdir
pkg_resources.IMetadataProvider.get_metadata
pkg_resources.IMetadataProvider.get_metadata_lines
pkg_resources.IMetadataProvider.has_metadata
pkg_resources.IMetadataProvider.metadata_isdir
pkg_resources.IMetadataProvider.metadata_listdir
pkg_resources.IMetadataProvider.run_script
# @type_check_only
pkg_resources.IResourceManager
pkg_resources.Requirement.__init__
pkg_resources.WorkingSet.find_plugins
pkg_resources.WorkingSet.resolve
pkg_resources.WorkingSet.subscribe
pkg_resources.declare_namespace
pkg_resources.fixup_namespace_packages
pkg_resources.get_entry_map
pkg_resources.get_provider
pkg_resources.split_sections
pkg_resources.to_filename

# Is always set in __init__
pkg_resources.PathMetadata.egg_info
Expand Down Expand Up @@ -53,16 +44,24 @@ setuptools._distutils.dist.Distribution.get_requires
setuptools._distutils.dist.Distribution.get_provides
setuptools._distutils.dist.Distribution.get_obsoletes

# Uncomment once ignore_missing_stub is turned off
# # Not supported by typeshed
# setuptools.py34compat
# Not supported by typeshed
setuptools.py34compat
setuptools.command.py36compat

# # Private modules
# setuptools.config._validate_pyproject.*
# setuptools.build_meta._BuildMetaBackend.*
# Private modules
setuptools.config._validate_pyproject.*
setuptools.command.build_py.build_py.existing_egg_info_dir

# # Vendored and modified version of stdlib's distutils. Basically implementation details
# setuptools._distutils.*
# # Other vendored code
# setuptools._vendor.*
# pkg_resources._vendor.*
# Loop variable leak
setuptools.sandbox.AbstractSandbox.name

# Vendored and modified version of stdlib's distutils. Basically implementation details
setuptools._distutils.*
# Other vendored code
setuptools._vendor.*
pkg_resources._vendor.*
# Deprecated in favor of importlib.resources, importlib.metadata and their backports
# So like distutils, we only add what we need to reference.
pkg_resources.AvailableDistributions
pkg_resources.ResourceManager
pkg_resources.extern
2 changes: 2 additions & 0 deletions stubs/setuptools/@tests/stubtest_allowlist_darwin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Mock
setuptools.msvc.winreg
2 changes: 2 additions & 0 deletions stubs/setuptools/@tests/stubtest_allowlist_linux.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Mock
setuptools.msvc.winreg
3 changes: 2 additions & 1 deletion stubs/setuptools/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
version = "67.6.*"

[tool.stubtest]
ignore_missing_stub = true
# darwin is equivalent to linux for OS-specific methods
platforms = ["linux", "win32"]
89 changes: 63 additions & 26 deletions stubs/setuptools/pkg_resources/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ from abc import ABCMeta
from collections.abc import Callable, Generator, Iterable, Sequence
from io import BytesIO
from re import Pattern
from typing import IO, Any, ClassVar, TypeVar, overload
from typing import IO, Any, ClassVar, Protocol, TypeVar, overload, type_check_only
from typing_extensions import Literal, Self, TypeAlias

_Version: TypeAlias = Incomplete # from packaging.version
Expand All @@ -21,8 +21,8 @@ _PkgReqType: TypeAlias = str | Requirement
_DistFinderType: TypeAlias = Callable[[_Importer, str, bool], Generator[Distribution, None, None]]
_NSHandlerType: TypeAlias = Callable[[_Importer, str, str, types.ModuleType], str]

def declare_namespace(name: str) -> None: ...
def fixup_namespace_packages(path_item: str) -> None: ...
def declare_namespace(packageName: str) -> None: ...
def fixup_namespace_packages(path_item: str, parent=None) -> None: ...

class WorkingSet:
entries: list[str]
Expand All @@ -35,18 +35,24 @@ class WorkingSet:
def __iter__(self) -> Generator[Distribution, None, None]: ...
def find(self, req: Requirement) -> Distribution | None: ...
def resolve(
self, requirements: Iterable[Requirement], env: Environment | None = None, installer: _InstallerType | None = None
self,
requirements: Iterable[Requirement],
env: Environment | None = None,
installer: _InstallerType | None = None,
replace_conflicting=False,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
replace_conflicting=False,
replace_conflicting: bool = False,

extras=None,
) -> list[Distribution]: ...
def add(self, dist: Distribution, entry: str | None = None, insert: bool = True, replace: bool = False) -> None: ...
def subscribe(self, callback: Callable[[Distribution], object]) -> None: ...
def subscribe(self, callback: Callable[[Distribution], object], existing=True) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def subscribe(self, callback: Callable[[Distribution], object], existing=True) -> None: ...
def subscribe(self, callback: Callable[[Distribution], object], existing: bool = True) -> None: ...

def find_plugins(
self, plugin_env: Environment, full_env: Environment | None = None, fallback: bool = True
self, plugin_env: Environment, full_env: Environment | None = None, installer=None, fallback: bool = True
) -> tuple[list[Distribution], dict[Distribution, Exception]]: ...

working_set: WorkingSet

require = working_set.require
run_script = working_set.run_script
run_main = run_script
iter_entry_points = working_set.iter_entry_points
add_activation_listener = working_set.subscribe

Expand All @@ -62,13 +68,15 @@ class Environment:
def __add__(self, other: Distribution | Environment) -> Environment: ...
def __iadd__(self, other: Distribution | Environment) -> Self: ...
@overload
def best_match(self, req: Requirement, working_set: WorkingSet, *, replace_conflicting: bool = False) -> Distribution: ...
def best_match(
self, req: Requirement, working_set: WorkingSet, installer: None = None, replace_conflicting: bool = False
) -> Distribution: ...
@overload
def best_match(
self, req: Requirement, working_set: WorkingSet, installer: Callable[[Requirement], _T], replace_conflicting: bool = False
) -> _T: ...
@overload
def obtain(self, requirement: Requirement) -> None: ...
def obtain(self, requirement: Requirement, installer: None = None) -> None: ...
@overload
def obtain(self, requirement: Requirement, installer: Callable[[Requirement], _T]) -> _T: ...
def scan(self, search_path: Sequence[str] | None = None) -> None: ...
Expand All @@ -89,6 +97,7 @@ class Requirement:
# TODO: change this to packaging.markers.Marker | None once we can import
# packaging.markers
marker: Incomplete | None
def __init__(self, requirement_string) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

It'll be the same as https://github.com/pypa/packaging/blob/55584fb5ca327ba38b74ca5c668125caaebd9a5d/src/packaging/requirements.py#L33

Suggested change
def __init__(self, requirement_string) -> None: ...
def __init__(self, requirement_string: str) -> None: ...

@staticmethod
def parse(s: str | Iterable[str]) -> Requirement: ...
def __contains__(self, item: Distribution | str | tuple[str, ...]) -> bool: ...
Expand All @@ -97,11 +106,12 @@ class Requirement:
def load_entry_point(dist: _EPDistType, group: str, name: str) -> Any: ...
def get_entry_info(dist: _EPDistType, group: str, name: str) -> EntryPoint | None: ...
@overload
def get_entry_map(dist: _EPDistType) -> dict[str, dict[str, EntryPoint]]: ...
def get_entry_map(dist: _EPDistType, group: None = None) -> dict[str, dict[str, EntryPoint]]: ...
@overload
def get_entry_map(dist: _EPDistType, group: str) -> dict[str, EntryPoint]: ...

class EntryPoint:
pattern: ClassVar[Pattern[str]]
name: str
module_name: str
attrs: tuple[str, ...]
Expand Down Expand Up @@ -133,11 +143,21 @@ def get_distribution(dist: _D) -> _D: ...
@overload
def get_distribution(dist: _PkgReqType) -> Distribution: ...

class Distribution(NullProvider, IResourceProvider, IMetadataProvider):
# Doesn't actually extend NullProvider
class Distribution(NullProvider):
PKG_INFO: ClassVar[str]
location: str
project_name: str
@property
def hashcmp(self) -> tuple[Incomplete, int, str, Incomplete | None, str, str]: ...
def __hash__(self) -> int: ...
def __lt__(self, other) -> bool: ...
def __le__(self, other) -> bool: ...
def __gt__(self, other) -> bool: ...
def __ge__(self, other) -> bool: ...
def __eq__(self, other) -> bool: ...
def __ne__(self, other) -> bool: ...
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def __lt__(self, other) -> bool: ...
def __le__(self, other) -> bool: ...
def __gt__(self, other) -> bool: ...
def __ge__(self, other) -> bool: ...
def __eq__(self, other) -> bool: ...
def __ne__(self, other) -> bool: ...
def __lt__(self, other: Distribution) -> bool: ...
def __le__(self, other: Distribution) -> bool: ...
def __gt__(self, other: Distribution) -> bool: ...
def __ge__(self, other: Distribution) -> bool: ...
def __eq__(self, other: object) -> bool: ...
def __ne__(self, other: object) -> bool: ...

@property
def key(self) -> str: ...
@property
def extras(self) -> list[str]: ...
Expand All @@ -164,15 +184,17 @@ class Distribution(NullProvider, IResourceProvider, IMetadataProvider):
) -> Distribution: ...
@classmethod
def from_filename(cls, filename: str, metadata: _MetadataType = None, **kw: str | None | int) -> Distribution: ...
def activate(self, path: list[str] | None = None) -> None: ...
def activate(self, path: list[str] | None = None, replace=False) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
def activate(self, path: list[str] | None = None, replace=False) -> None: ...
def activate(self, path: list[str] | None = None, replace: bool = False) -> None: ...

def as_requirement(self) -> Requirement: ...
def requires(self, extras: tuple[str, ...] = ()) -> list[Requirement]: ...
def check_version_conflict(self) -> None: ...
def has_version(self) -> bool: ...
def clone(self, **kw: str | int | None) -> Requirement: ...
def egg_name(self) -> str: ... # type: ignore[override] # supertype's egg_name is a variable, not a method
def __cmp__(self, other: Any) -> bool: ...
def get_entry_info(self, group: str, name: str) -> EntryPoint | None: ...
def insert_on(self, path, loc: Incomplete | None = None, replace: bool = False) -> None: ...
@overload
def get_entry_map(self) -> dict[str, dict[str, EntryPoint]]: ...
def get_entry_map(self, group: None = None) -> dict[str, dict[str, EntryPoint]]: ...
@overload
def get_entry_map(self, group: str) -> dict[str, EntryPoint]: ...
def load_entry_point(self, group: str, name: str) -> Any: ...
Expand All @@ -191,8 +213,8 @@ def resource_listdir(package_or_requirement: _PkgReqType, resource_name: str) ->
def resource_filename(package_or_requirement: _PkgReqType, resource_name: str) -> str: ...
def set_extraction_path(path: str) -> None: ...
def cleanup_resources(force: bool = False) -> list[str]: ...

class IResourceManager:
@type_check_only
class IResourceManager(Protocol):
Copy link
Member

Choose a reason for hiding this comment

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

If this class doesn't exist at runtime, should it maybe be private?

Suggested change
class IResourceManager(Protocol):
class _IResourceManager(Protocol):

Copy link
Collaborator Author

@Avasam Avasam Apr 28, 2023

Choose a reason for hiding this comment

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

Isn't that what @type_check_only is for? Granted support is still limited in mypy (python/mypy#15146, python/mypy#9531)

The source docstrings do call it IResourceManager, so it's the right name, makes it easier to find and import, whilst being safer with checkers that support the annotation.

Copy link
Member

Choose a reason for hiding this comment

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

True, mypy has been pretty slow to implement this feature :)

Elsewhere in typeshed, though, we generally don't rely on a feature unless it's supported by all major type checkers. We use @type_check_only in a few places already, but those are basically all very special cases (builtins.pyi, typing.pyi) where our standard option of making the name private isn't available.

def resource_exists(self, package_or_requirement: _PkgReqType, resource_name: str) -> bool: ...
def resource_stream(self, package_or_requirement: _PkgReqType, resource_name: str) -> IO[bytes]: ...
def resource_string(self, package_or_requirement: _PkgReqType, resource_name: str) -> bytes: ...
Expand All @@ -206,11 +228,11 @@ class IResourceManager:
def postprocess(self, tempname: str, filename: str) -> None: ...

@overload
def get_provider(package_or_requirement: str) -> IResourceProvider: ...
def get_provider(moduleOrReq: str) -> IResourceProvider: ...
@overload
def get_provider(package_or_requirement: Requirement) -> Distribution: ...
def get_provider(moduleOrReq: Requirement) -> Distribution: ...

class IMetadataProvider:
class IMetadataProvider(Protocol):
def has_metadata(self, name: str) -> bool | None: ...
def metadata_isdir(self, name: str) -> bool: ...
def metadata_listdir(self, name: str) -> list[str]: ...
Expand Down Expand Up @@ -254,7 +276,16 @@ def register_finder(importer_type: type, distribution_finder: _DistFinderType) -
def register_loader_type(loader_type: type, provider_factory: Callable[[types.ModuleType], IResourceProvider]) -> None: ...
def register_namespace_handler(importer_type: type, namespace_handler: _NSHandlerType) -> None: ...

class IResourceProvider(IMetadataProvider): ...
class IResourceProvider(IMetadataProvider, Protocol):
def get_resource_filename(self, manager, resource_name): ...
def get_resource_stream(self, manager, resource_name): ...
def get_resource_string(self, manager, resource_name): ...
def has_resource(self, resource_name): ...
def resource_isdir(self, resource_name): ...
def resource_listdir(self, resource_name): ...

def invalid_marker(text) -> SyntaxError | Literal[False]: ...
def evaluate_marker(text, extra: Incomplete | None = None): ...

class NullProvider:
egg_name: str | None
Expand All @@ -281,37 +312,41 @@ class EggProvider(NullProvider):

class DefaultProvider(EggProvider): ...

class PathMetadata(DefaultProvider, IResourceProvider):
class PathMetadata(DefaultProvider):
egg_info: str
module_path: str
def __init__(self, path: str, egg_info: str) -> None: ...

class ZipProvider(EggProvider):
eagers: list[str] | None
zip_pre: str
@property
def zipinfo(self): ...

class EggMetadata(ZipProvider, IResourceProvider):
class EggMetadata(ZipProvider):
JelleZijlstra marked this conversation as resolved.
Show resolved Hide resolved
loader: zipimport.zipimporter
module_path: str
def __init__(self, zipimporter: zipimport.zipimporter) -> None: ...
def __init__(self, importer: zipimport.zipimporter) -> None: ...

class EmptyProvider(NullProvider):
module_path: None
def __init__(self) -> None: ...

empty_provider: EmptyProvider

class FileMetadata(EmptyProvider, IResourceProvider):
def __init__(self, path_to_pkg_info: str) -> None: ...
class FileMetadata(EmptyProvider):
def __init__(self, path: str) -> None: ...

class PEP440Warning(RuntimeWarning): ...

parse_version = _Version

def yield_lines(iterable: _NestedStr) -> Generator[str, None, None]: ...
def split_sections(strs: _NestedStr) -> Generator[tuple[str | None, list[str]], None, None]: ...
def split_sections(s: _NestedStr) -> Generator[tuple[str | None, list[str]], None, None]: ...
def safe_name(name: str) -> str: ...
def safe_version(version: str) -> str: ...
def safe_extra(extra: str) -> str: ...
def to_filename(name_or_version: str) -> str: ...
def to_filename(name: str) -> str: ...
def get_build_platform() -> str: ...
def get_platform() -> str: ...
def get_supported_platform() -> str: ...
Expand All @@ -320,3 +355,5 @@ def get_default_cache() -> str: ...
def get_importer(path_item: str) -> _Importer: ...
def ensure_directory(path: str) -> None: ...
def normalize_path(filename: str) -> str: ...

class PkgResourcesDeprecationWarning(Warning): ...
35 changes: 21 additions & 14 deletions stubs/setuptools/setuptools/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@ from abc import abstractmethod
from collections.abc import Iterable, Mapping, Sequence
from typing import Any

from setuptools._deprecation_warning import SetuptoolsDeprecationWarning as SetuptoolsDeprecationWarning
from setuptools.depends import Require as Require
from setuptools.dist import Distribution as Distribution
from setuptools.extension import Extension as Extension

from ._deprecation_warning import SetuptoolsDeprecationWarning as SetuptoolsDeprecationWarning
from ._distutils.cmd import Command as _Command
from .depends import Require as Require
from .discovery import _Path
from .dist import Distribution as Distribution
from .extension import Extension as Extension

__version__: str

class PackageFinder:
@classmethod
def find(cls, where: str = ".", exclude: Iterable[str] = (), include: Iterable[str] = ("*",)) -> list[str]: ...
__all__ = [
"setup",
"Distribution",
"Command",
"Extension",
"Require",
"SetuptoolsDeprecationWarning",
"find_packages",
"find_namespace_packages",
]

class PEP420PackageFinder(PackageFinder): ...

find_packages = PackageFinder.find
find_namespace_packages = PEP420PackageFinder.find
__version__: str

# Pytype fails with the following:
# find_packages = PackageFinder.find
# find_namespace_packages = PEP420PackageFinder.find
def find_packages(where: _Path = ".", exclude: Iterable[str] = (), include: Iterable[str] = ("*",)) -> list[str]: ...
def find_namespace_packages(where: _Path = ".", exclude: Iterable[str] = (), include: Iterable[str] = ("*",)) -> list[str]: ...
def setup(
*,
name: str = ...,
Expand Down
Loading