diff options
| author | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2021-12-25 15:35:16 +0000 |
|---|---|---|
| committer | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2022-03-05 14:54:07 +0000 |
| commit | d3e62b109e0d6ec57dcf14207c7dd91610138666 (patch) | |
| tree | 709f3002e518c2b022541362de63cc964c811b81 /setuptools/config/expand.py | |
| parent | 441a1fa000bf66bc4ee6812bc7f3a039b85f2902 (diff) | |
| download | python-setuptools-git-d3e62b109e0d6ec57dcf14207c7dd91610138666.tar.gz | |
Add some type hints to config.expand
Diffstat (limited to 'setuptools/config/expand.py')
| -rw-r--r-- | setuptools/config/expand.py | 78 |
1 files changed, 52 insertions, 26 deletions
diff --git a/setuptools/config/expand.py b/setuptools/config/expand.py index 4778ffb6..cf034d69 100644 --- a/setuptools/config/expand.py +++ b/setuptools/config/expand.py @@ -22,18 +22,22 @@ import os import sys from glob import iglob from configparser import ConfigParser +from importlib.machinery import ModuleSpec from itertools import chain +from typing import Callable, Dict, Iterable, List, Optional, Tuple, Union, cast +from types import ModuleType from distutils.errors import DistutilsOptionError chain_iter = chain.from_iterable +_Path = Union[str, os.PathLike] class StaticModule: """Proxy to a module object that avoids executing arbitrary code.""" - def __init__(self, name, spec): - with open(spec.origin) as strm: + def __init__(self, name: str, spec: ModuleSpec): + with open(spec.origin) as strm: # type: ignore src = strm.read() module = ast.parse(src) vars(self).update(locals()) @@ -62,7 +66,9 @@ class StaticModule: raise AttributeError(f"{self.name} has no attribute {attr}") from e -def glob_relative(patterns, root_dir=None): +def glob_relative( + patterns: Iterable[str], root_dir: Optional[_Path] = None +) -> List[str]: """Expand the list of glob patterns, but preserving relative paths. :param list[str] patterns: List of glob patterns @@ -91,7 +97,7 @@ def glob_relative(patterns, root_dir=None): return expanded_values -def read_files(filepaths, root_dir=None): +def read_files(filepaths: Union[str, bytes, Iterable[_Path]], root_dir=None) -> str: """Return the content of the files concatenated using ``\n`` as str This function is sandboxed and won't reach anything outside ``root_dir`` @@ -99,7 +105,7 @@ def read_files(filepaths, root_dir=None): (By default ``root_dir`` is the current directory). """ if isinstance(filepaths, (str, bytes)): - filepaths = [filepaths] + filepaths = [filepaths] # type: ignore root_dir = os.path.abspath(root_dir or os.getcwd()) _filepaths = (os.path.join(root_dir, path) for path in filepaths) @@ -110,12 +116,12 @@ def read_files(filepaths, root_dir=None): ) -def _read_file(filepath): +def _read_file(filepath: Union[bytes, _Path]) -> str: with io.open(filepath, encoding='utf-8') as f: return f.read() -def _assert_local(filepath, root_dir): +def _assert_local(filepath: _Path, root_dir: str): if not os.path.abspath(filepath).startswith(root_dir): msg = f"Cannot access {filepath!r} (or anything outside {root_dir!r})" raise DistutilsOptionError(msg) @@ -123,7 +129,11 @@ def _assert_local(filepath, root_dir): return True -def read_attr(attr_desc, package_dir=None, root_dir=None): +def read_attr( + attr_desc: str, + package_dir: Optional[dict] = None, + root_dir: Optional[_Path] = None +): """Reads the value of an attribute from a module. This function will try to read the attributed statically first @@ -146,8 +156,8 @@ def read_attr(attr_desc, package_dir=None, root_dir=None): attr_name = attrs_path.pop() module_name = '.'.join(attrs_path) module_name = module_name or '__init__' - parent_path, path, module_name = _find_module(module_name, package_dir, root_dir) - spec = _find_spec(module_name, path, parent_path) + _parent_path, path, module_name = _find_module(module_name, package_dir, root_dir) + spec = _find_spec(module_name, path) try: return getattr(StaticModule(module_name, spec), attr_name) @@ -157,7 +167,7 @@ def read_attr(attr_desc, package_dir=None, root_dir=None): return getattr(module, attr_name) -def _find_spec(module_name, module_path, parent_path): +def _find_spec(module_name: str, module_path: Optional[_Path]) -> ModuleSpec: spec = importlib.util.spec_from_file_location(module_name, module_path) spec = spec or importlib.util.find_spec(module_name) @@ -167,17 +177,19 @@ def _find_spec(module_name, module_path, parent_path): return spec -def _load_spec(spec, module_name): +def _load_spec(spec: ModuleSpec, module_name: str) -> ModuleType: name = getattr(spec, "__name__", module_name) if name in sys.modules: return sys.modules[name] module = importlib.util.module_from_spec(spec) sys.modules[name] = module # cache (it also ensures `==` works on loaded items) - spec.loader.exec_module(module) + spec.loader.exec_module(module) # type: ignore return module -def _find_module(module_name, package_dir, root_dir): +def _find_module( + module_name: str, package_dir: Optional[dict], root_dir: _Path +) -> Tuple[_Path, Optional[str], str]: """Given a module (that could normally be imported by ``module_name`` after the build is complete), find the path to the parent directory where it is contained and the canonical name that could be used to import it @@ -209,26 +221,36 @@ def _find_module(module_name, package_dir, root_dir): return parent_path, module_path, module_name -def resolve_class(qualified_class_name, package_dir=None, root_dir=None): +def resolve_class( + qualified_class_name: str, + package_dir: Optional[dict] = None, + root_dir: Optional[_Path] = None +) -> Callable: """Given a qualified class name, return the associated class object""" root_dir = root_dir or os.getcwd() idx = qualified_class_name.rfind('.') class_name = qualified_class_name[idx + 1 :] pkg_name = qualified_class_name[:idx] - parent_path, path, module_name = _find_module(pkg_name, package_dir, root_dir) - module = _load_spec(_find_spec(module_name, path, parent_path), module_name) + _parent_path, path, module_name = _find_module(pkg_name, package_dir, root_dir) + module = _load_spec(_find_spec(module_name, path), module_name) return getattr(module, class_name) -def cmdclass(values, package_dir=None, root_dir=None): +def cmdclass( + values: Dict[str, str], + package_dir: Optional[dict] = None, + root_dir: Optional[_Path] = None +) -> Dict[str, Callable]: """Given a dictionary mapping command names to strings for qualified class names, apply :func:`resolve_class` to the dict values. """ return {k: resolve_class(v, package_dir, root_dir) for k, v in values.items()} -def find_packages(*, namespaces=False, root_dir=None, **kwargs): +def find_packages( + *, namespaces=False, root_dir: Optional[_Path] = None, **kwargs +) -> List[str]: """Works similarly to :func:`setuptools.find_packages`, but with all arguments given as keyword arguments. Moreover, ``where`` can be given as a list (the results will be simply concatenated). @@ -243,7 +265,7 @@ def find_packages(*, namespaces=False, root_dir=None, **kwargs): if namespaces: from setuptools import PEP420PackageFinder as PackageFinder else: - from setuptools import PackageFinder + from setuptools import PackageFinder # type: ignore root_dir = root_dir or "." where = kwargs.pop('where', ['.']) @@ -253,18 +275,20 @@ def find_packages(*, namespaces=False, root_dir=None, **kwargs): return list(chain_iter(PackageFinder.find(x, **kwargs) for x in target)) -def _nest_path(parent, path): +def _nest_path(parent: _Path, path: _Path) -> str: path = parent if path == "." else os.path.join(parent, path) return os.path.normpath(path) -def version(value): +def version(value: Union[Callable, Iterable[Union[str, int]], str]) -> str: """When getting the version directly from an attribute, it should be normalised to string. """ if callable(value): value = value() + value = cast(Iterable[Union[str, int]], value) + if not isinstance(value, str): if hasattr(value, '__iter__'): value = '.'.join(map(str, value)) @@ -274,13 +298,15 @@ def version(value): return value -def canonic_package_data(package_data): +def canonic_package_data(package_data: dict) -> dict: if "*" in package_data: package_data[""] = package_data.pop("*") return package_data -def canonic_data_files(data_files, root_dir=None): +def canonic_data_files( + data_files: Union[list, dict], root_dir: Optional[_Path] = None +) -> List[Tuple[str, List[str]]]: """For compatibility with ``setup.py``, ``data_files`` should be a list of pairs instead of a dict. @@ -295,14 +321,14 @@ def canonic_data_files(data_files, root_dir=None): ] -def entry_points(text, text_source="entry-points"): +def entry_points(text: str, text_source="entry-points") -> Dict[str, dict]: """Given the contents of entry-points file, process it into a 2-level dictionary (``dict[str, dict[str, str]]``). The first level keys are entry-point groups, the second level keys are entry-point names, and the second level values are references to objects (that correspond to the entry-point value). """ - parser = ConfigParser(default_section=None, delimiters=("=",)) + parser = ConfigParser(default_section=None, delimiters=("=",)) # type: ignore parser.optionxform = str # case sensitive parser.read_string(text, text_source) groups = {k: dict(v.items()) for k, v in parser.items()} |
