Commit 74e47612 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge branch 'master' into bugfix/2232-adopt-distutils-default

parents c85bdd84 2df5d97f
[bumpversion] [bumpversion]
current_version = 49.3.0 current_version = 49.5.0
commit = True commit = True
tag = True tag = True
......
v49.5.0
-------
* #2306: When running as a PEP 517 backend, setuptools does not try to install
``setup_requires`` itself. They are reported as build requirements for the
frontend to install.
v49.4.0
-------
* #2310: Updated vendored packaging version to 20.4.
v49.3.2
-------
* #2300: Improve the ``safe_version`` function documentation
* #2297: Once again, in stubs prefer exec_module to the deprecated load_module.
v49.3.1
-------
* #2316: Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled.
v49.3.0 v49.3.0
------- -------
......
...@@ -16,9 +16,12 @@ def warn_distutils_present(): ...@@ -16,9 +16,12 @@ def warn_distutils_present():
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return return
warnings.warn( warnings.warn(
"Distutils was imported before Setuptools. This usage is discouraged " "Distutils was imported before Setuptools, but importing Setuptools "
"and may exhibit undesirable behaviors or errors. Please use " "also replaces the `distutils` module in `sys.modules`. This may lead "
"Setuptools' objects directly or at least import Setuptools first.") "to undesirable behaviors or errors. To avoid these issues, avoid "
"using distutils directly, ensure that setuptools is installed in the "
"traditional way (e.g. not an editable install), and/or make sure that "
"setuptools is always imported before distutils.")
def clear_distutils(): def clear_distutils():
...@@ -56,8 +59,8 @@ def do_override(): ...@@ -56,8 +59,8 @@ def do_override():
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
for more motivation. for more motivation.
""" """
warn_distutils_present()
if enabled(): if enabled():
warn_distutils_present()
ensure_local_distutils() ensure_local_distutils()
......
In pkg_resources, no longer detect any pathname ending in .egg as a Python egg. Now the path must be an unpacked egg or a zip file.
...@@ -1596,12 +1596,12 @@ Parsing Utilities ...@@ -1596,12 +1596,12 @@ Parsing Utilities
See ``to_filename()``. See ``to_filename()``.
``safe_version(version)`` ``safe_version(version)``
This will return the normalized form of any PEP 440 version, if the version This will return the normalized form of any PEP 440 version. If the version
string is not PEP 440 compatible than it is similar to ``safe_name()`` string is not PEP 440 compatible, this function behaves similar to
except that spaces in the input become dots, and dots are allowed to exist ``safe_name()`` except that spaces in the input become dots, and dots are
in the output. As with ``safe_name()``, if you are generating a filename allowed to exist in the output. As with ``safe_name()``, if you are
from this you should replace any "-" characters in the output with generating a filename from this you should replace any "-" characters in
underscores. the output with underscores.
``safe_extra(extra)`` ``safe_extra(extra)``
Return a "safe" form of an extra's name, suitable for use in a requirement Return a "safe" form of an extra's name, suitable for use in a requirement
......
...@@ -1988,11 +1988,11 @@ boilerplate code in some cases. ...@@ -1988,11 +1988,11 @@ boilerplate code in some cases.
include_package_data = True include_package_data = True
packages = find: packages = find:
scripts = scripts =
bin/first.py bin/first.py
bin/second.py bin/second.py
install_requires = install_requires =
requests requests
importlib; python_version == "2.6" importlib; python_version == "2.6"
[options.package_data] [options.package_data]
* = *.txt, *.rst * = *.txt, *.rst
...@@ -2028,8 +2028,8 @@ Metadata and options are set in the config sections of the same name. ...@@ -2028,8 +2028,8 @@ Metadata and options are set in the config sections of the same name.
[metadata] [metadata]
keywords = keywords =
one one
two two
* In some cases, complex values can be provided in dedicated subsections for * In some cases, complex values can be provided in dedicated subsections for
clarity. clarity.
......
...@@ -2056,7 +2056,10 @@ def find_on_path(importer, path_item, only=False): ...@@ -2056,7 +2056,10 @@ def find_on_path(importer, path_item, only=False):
) )
return return
entries = safe_listdir(path_item) entries = (
os.path.join(path_item, child)
for child in safe_listdir(path_item)
)
# for performance, before sorting by version, # for performance, before sorting by version,
# screen entries for only those that will yield # screen entries for only those that will yield
...@@ -2372,7 +2375,15 @@ def _is_egg_path(path): ...@@ -2372,7 +2375,15 @@ def _is_egg_path(path):
""" """
Determine if given path appears to be an egg. Determine if given path appears to be an egg.
""" """
return path.lower().endswith('.egg') return _is_zip_egg(path) or _is_unpacked_egg(path)
def _is_zip_egg(path):
return (
path.lower().endswith('.egg') and
os.path.isfile(path) and
zipfile.is_zipfile(path)
)
def _is_unpacked_egg(path): def _is_unpacked_egg(path):
...@@ -2380,7 +2391,7 @@ def _is_unpacked_egg(path): ...@@ -2380,7 +2391,7 @@ def _is_unpacked_egg(path):
Determine if given path appears to be an unpacked egg. Determine if given path appears to be an unpacked egg.
""" """
return ( return (
_is_egg_path(path) and path.lower().endswith('.egg') and
os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO'))
) )
......
...@@ -18,10 +18,10 @@ __title__ = "packaging" ...@@ -18,10 +18,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages" __summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging" __uri__ = "https://github.com/pypa/packaging"
__version__ = "19.2" __version__ = "20.4"
__author__ = "Donald Stufft and individual contributors" __author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io" __email__ = "donald@stufft.io"
__license__ = "BSD or Apache License, Version 2.0" __license__ = "BSD-2-Clause or Apache-2.0"
__copyright__ = "Copyright 2014-2019 %s" % __author__ __copyright__ = "Copyright 2014-2019 %s" % __author__
...@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function ...@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function
import sys import sys
from ._typing import TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Dict, Tuple, Type
PY2 = sys.version_info[0] == 2 PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info[0] == 3
...@@ -18,14 +23,16 @@ else: ...@@ -18,14 +23,16 @@ else:
def with_metaclass(meta, *bases): def with_metaclass(meta, *bases):
# type: (Type[Any], Tuple[Type[Any], ...]) -> Any
""" """
Create a base class with a metaclass. Create a base class with a metaclass.
""" """
# This requires a bit of explanation: the basic idea is to make a dummy # This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with # metaclass for one level of class instantiation that replaces itself with
# the actual metaclass. # the actual metaclass.
class metaclass(meta): class metaclass(meta): # type: ignore
def __new__(cls, name, this_bases, d): def __new__(cls, name, this_bases, d):
# type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
return meta(name, bases, d) return meta(name, bases, d)
return type.__new__(metaclass, "temporary_class", (), {}) return type.__new__(metaclass, "temporary_class", (), {})
...@@ -4,65 +4,83 @@ ...@@ -4,65 +4,83 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
class Infinity(object): class InfinityType(object):
def __repr__(self): def __repr__(self):
# type: () -> str
return "Infinity" return "Infinity"
def __hash__(self): def __hash__(self):
# type: () -> int
return hash(repr(self)) return hash(repr(self))
def __lt__(self, other): def __lt__(self, other):
# type: (object) -> bool
return False return False
def __le__(self, other): def __le__(self, other):
# type: (object) -> bool
return False return False
def __eq__(self, other): def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__) return isinstance(other, self.__class__)
def __ne__(self, other): def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__) return not isinstance(other, self.__class__)
def __gt__(self, other): def __gt__(self, other):
# type: (object) -> bool
return True return True
def __ge__(self, other): def __ge__(self, other):
# type: (object) -> bool
return True return True
def __neg__(self): def __neg__(self):
# type: (object) -> NegativeInfinityType
return NegativeInfinity return NegativeInfinity
Infinity = Infinity() Infinity = InfinityType()
class NegativeInfinity(object): class NegativeInfinityType(object):
def __repr__(self): def __repr__(self):
# type: () -> str
return "-Infinity" return "-Infinity"
def __hash__(self): def __hash__(self):
# type: () -> int
return hash(repr(self)) return hash(repr(self))
def __lt__(self, other): def __lt__(self, other):
# type: (object) -> bool
return True return True
def __le__(self, other): def __le__(self, other):
# type: (object) -> bool
return True return True
def __eq__(self, other): def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__) return isinstance(other, self.__class__)
def __ne__(self, other): def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__) return not isinstance(other, self.__class__)
def __gt__(self, other): def __gt__(self, other):
# type: (object) -> bool
return False return False
def __ge__(self, other): def __ge__(self, other):
# type: (object) -> bool
return False return False
def __neg__(self): def __neg__(self):
# type: (object) -> InfinityType
return Infinity return Infinity
NegativeInfinity = NegativeInfinity() NegativeInfinity = NegativeInfinityType()
"""For neatly implementing static typing in packaging.
`mypy` - the static type analysis tool we use - uses the `typing` module, which
provides core functionality fundamental to mypy's functioning.
Generally, `typing` would be imported at runtime and used in that fashion -
it acts as a no-op at runtime and does not have any run-time overhead by
design.
As it turns out, `typing` is not vendorable - it uses separate sources for
Python 2/Python 3. Thus, this codebase can not expect it to be present.
To work around this, mypy allows the typing import to be behind a False-y
optional to prevent it from running at runtime and type-comments can be used
to remove the need for the types to be accessible directly during runtime.
This module provides the False-y guard in a nicely named fashion so that a
curious maintainer can reach here to read this.
In packaging, all static-typing related imports should be guarded as follows:
from packaging._typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import ...
Ref: https://github.com/python/mypy/issues/3216
"""
__all__ = ["TYPE_CHECKING", "cast"]
# The TYPE_CHECKING constant defined by the typing module is False at runtime
# but True while type checking.
if False: # pragma: no cover
from typing import TYPE_CHECKING
else:
TYPE_CHECKING = False
# typing's cast syntax requires calling typing.cast at runtime, but we don't
# want to import typing at runtime. Here, we inform the type checkers that
# we're importing `typing.cast` as `cast` and re-implement typing.cast's
# runtime behavior in a block that is ignored by type checkers.
if TYPE_CHECKING: # pragma: no cover
# not executed at runtime
from typing import cast
else:
# executed at runtime
def cast(type_, value): # noqa
return value
...@@ -13,8 +13,14 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedStr ...@@ -13,8 +13,14 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedStr
from pkg_resources.extern.pyparsing import Literal as L # noqa from pkg_resources.extern.pyparsing import Literal as L # noqa
from ._compat import string_types from ._compat import string_types
from ._typing import TYPE_CHECKING
from .specifiers import Specifier, InvalidSpecifier from .specifiers import Specifier, InvalidSpecifier
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
Operator = Callable[[str, str], bool]
__all__ = [ __all__ = [
"InvalidMarker", "InvalidMarker",
...@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError): ...@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError):
class Node(object): class Node(object):
def __init__(self, value): def __init__(self, value):
# type: (Any) -> None
self.value = value self.value = value
def __str__(self): def __str__(self):
# type: () -> str
return str(self.value) return str(self.value)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
def serialize(self): def serialize(self):
# type: () -> str
raise NotImplementedError raise NotImplementedError
class Variable(Node): class Variable(Node):
def serialize(self): def serialize(self):
# type: () -> str
return str(self) return str(self)
class Value(Node): class Value(Node):
def serialize(self): def serialize(self):
# type: () -> str
return '"{0}"'.format(self) return '"{0}"'.format(self)
class Op(Node): class Op(Node):
def serialize(self): def serialize(self):
# type: () -> str
return str(self) return str(self)
...@@ -85,13 +98,13 @@ VARIABLE = ( ...@@ -85,13 +98,13 @@ VARIABLE = (
| L("python_version") | L("python_version")
| L("sys_platform") | L("sys_platform")
| L("os_name") | L("os_name")
| L("os.name") | L("os.name") # PEP-345
| L("sys.platform") # PEP-345 | L("sys.platform") # PEP-345
| L("platform.version") # PEP-345 | L("platform.version") # PEP-345
| L("platform.machine") # PEP-345 | L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345 | L("platform.python_implementation") # PEP-345
| L("python_implementation") # PEP-345 | L("python_implementation") # undocumented setuptools legacy
| L("extra") # undocumented setuptools legacy | L("extra") # PEP-508
) )
ALIASES = { ALIASES = {
"os.name": "os_name", "os.name": "os_name",
...@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd ...@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd
def _coerce_parse_result(results): def _coerce_parse_result(results):
# type: (Union[ParseResults, List[Any]]) -> List[Any]
if isinstance(results, ParseResults): if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results] return [_coerce_parse_result(i) for i in results]
else: else:
...@@ -138,6 +152,8 @@ def _coerce_parse_result(results): ...@@ -138,6 +152,8 @@ def _coerce_parse_result(results):
def _format_marker(marker, first=True): def _format_marker(marker, first=True):
# type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
assert isinstance(marker, (list, tuple, string_types)) assert isinstance(marker, (list, tuple, string_types))
# Sometimes we have a structure like [[...]] which is a single item list # Sometimes we have a structure like [[...]] which is a single item list
...@@ -172,10 +188,11 @@ _operators = { ...@@ -172,10 +188,11 @@ _operators = {
"!=": operator.ne, "!=": operator.ne,
">=": operator.ge, ">=": operator.ge,
">": operator.gt, ">": operator.gt,
} } # type: Dict[str, Operator]
def _eval_op(lhs, op, rhs): def _eval_op(lhs, op, rhs):
# type: (str, Op, str) -> bool
try: try:
spec = Specifier("".join([op.serialize(), rhs])) spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier: except InvalidSpecifier:
...@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs): ...@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs):
else: else:
return spec.contains(lhs) return spec.contains(lhs)
oper = _operators.get(op.serialize()) oper = _operators.get(op.serialize()) # type: Optional[Operator]
if oper is None: if oper is None:
raise UndefinedComparison( raise UndefinedComparison(
"Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
...@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs): ...@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs):
return oper(lhs, rhs) return oper(lhs, rhs)
_undefined = object() class Undefined(object):
pass
_undefined = Undefined()
def _get_env(environment, name): def _get_env(environment, name):
value = environment.get(name, _undefined) # type: (Dict[str, str], str) -> str
value = environment.get(name, _undefined) # type: Union[str, Undefined]
if value is _undefined: if isinstance(value, Undefined):
raise UndefinedEnvironmentName( raise UndefinedEnvironmentName(
"{0!r} does not exist in evaluation environment.".format(name) "{0!r} does not exist in evaluation environment.".format(name)
) )
...@@ -207,7 +229,8 @@ def _get_env(environment, name): ...@@ -207,7 +229,8 @@ def _get_env(environment, name):
def _evaluate_markers(markers, environment): def _evaluate_markers(markers, environment):
groups = [[]] # type: (List[Any], Dict[str, str]) -> bool
groups = [[]] # type: List[List[bool]]
for marker in markers: for marker in markers:
assert isinstance(marker, (list, tuple, string_types)) assert isinstance(marker, (list, tuple, string_types))
...@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment): ...@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment):
def format_full_version(info): def format_full_version(info):
# type: (sys._version_info) -> str
version = "{0.major}.{0.minor}.{0.micro}".format(info) version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel kind = info.releaselevel
if kind != "final": if kind != "final":
...@@ -242,9 +266,13 @@ def format_full_version(info): ...@@ -242,9 +266,13 @@ def format_full_version(info):
def default_environment(): def default_environment():
# type: () -> Dict[str, str]
if hasattr(sys, "implementation"): if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version) # Ignoring the `sys.implementation` reference for type checking due to
implementation_name = sys.implementation.name # mypy not liking that the attribute doesn't exist in Python 2.7 when
# run with the `--py27` flag.
iver = format_full_version(sys.implementation.version) # type: ignore
implementation_name = sys.implementation.name # type: ignore
else: else:
iver = "0" iver = "0"
implementation_name = "" implementation_name = ""
...@@ -266,6 +294,7 @@ def default_environment(): ...@@ -266,6 +294,7 @@ def default_environment():
class Marker(object): class Marker(object):
def __init__(self, marker): def __init__(self, marker):
# type: (str) -> None
try: try:
self._markers = _coerce_parse_result(MARKER.parseString(marker)) self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e: except ParseException as e:
...@@ -275,12 +304,15 @@ class Marker(object): ...@@ -275,12 +304,15 @@ class Marker(object):
raise InvalidMarker(err_str) raise InvalidMarker(err_str)
def __str__(self): def __str__(self):
# type: () -> str
return _format_marker(self._markers) return _format_marker(self._markers)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<Marker({0!r})>".format(str(self)) return "<Marker({0!r})>".format(str(self))
def evaluate(self, environment=None): def evaluate(self, environment=None):
# type: (Optional[Dict[str, str]]) -> bool
"""Evaluate a marker. """Evaluate a marker.
Return the boolean from evaluating the given marker against the Return the boolean from evaluating the given marker against the
......
...@@ -11,9 +11,13 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Co ...@@ -11,9 +11,13 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Co
from pkg_resources.extern.pyparsing import Literal as L # noqa from pkg_resources.extern.pyparsing import Literal as L # noqa
from pkg_resources.extern.six.moves.urllib import parse as urlparse from pkg_resources.extern.six.moves.urllib import parse as urlparse
from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet from .specifiers import LegacySpecifier, Specifier, SpecifierSet
if TYPE_CHECKING: # pragma: no cover
from typing import List
class InvalidRequirement(ValueError): class InvalidRequirement(ValueError):
""" """
...@@ -89,6 +93,7 @@ class Requirement(object): ...@@ -89,6 +93,7 @@ class Requirement(object):
# TODO: Can we normalize the name and extra name? # TODO: Can we normalize the name and extra name?
def __init__(self, requirement_string): def __init__(self, requirement_string):
# type: (str) -> None
try: try:
req = REQUIREMENT.parseString(requirement_string) req = REQUIREMENT.parseString(requirement_string)
except ParseException as e: except ParseException as e:
...@@ -116,7 +121,8 @@ class Requirement(object): ...@@ -116,7 +121,8 @@ class Requirement(object):
self.marker = req.marker if req.marker else None self.marker = req.marker if req.marker else None
def __str__(self): def __str__(self):
parts = [self.name] # type: () -> str
parts = [self.name] # type: List[str]
if self.extras: if self.extras:
parts.append("[{0}]".format(",".join(sorted(self.extras)))) parts.append("[{0}]".format(",".join(sorted(self.extras))))
...@@ -135,4 +141,5 @@ class Requirement(object): ...@@ -135,4 +141,5 @@ class Requirement(object):
return "".join(parts) return "".join(parts)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<Requirement({0!r})>".format(str(self)) return "<Requirement({0!r})>".format(str(self))
This diff is collapsed.
...@@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function ...@@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function
import re import re
from ._typing import TYPE_CHECKING, cast
from .version import InvalidVersion, Version from .version import InvalidVersion, Version
if TYPE_CHECKING: # pragma: no cover
from typing import NewType, Union
NormalizedName = NewType("NormalizedName", str)
_canonicalize_regex = re.compile(r"[-_.]+") _canonicalize_regex = re.compile(r"[-_.]+")
def canonicalize_name(name): def canonicalize_name(name):
# type: (str) -> NormalizedName
# This is taken from PEP 503. # This is taken from PEP 503.
return _canonicalize_regex.sub("-", name).lower() value = _canonicalize_regex.sub("-", name).lower()
return cast("NormalizedName", value)
def canonicalize_version(version): def canonicalize_version(_version):
# type: (str) -> Union[Version, str]
""" """
This is very similar to Version.__str__, but has one subtle differences This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment. with the way it handles the release segment.
""" """
try: try:
version = Version(version) version = Version(_version)
except InvalidVersion: except InvalidVersion:
# Legacy versions cannot be normalized # Legacy versions cannot be normalized
return version return _version
parts = [] parts = []
......
This diff is collapsed.
packaging==19.2 packaging==20.4
pyparsing==2.2.1 pyparsing==2.2.1
six==1.10.0 six==1.10.0
appdirs==1.4.3 appdirs==1.4.3
...@@ -16,7 +16,7 @@ formats = zip ...@@ -16,7 +16,7 @@ formats = zip
[metadata] [metadata]
name = setuptools name = setuptools
version = 49.3.0 version = 49.5.0
description = Easily download, build, install, upgrade, and uninstall Python packages description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority author = Python Packaging Authority
author_email = distutils-sig@python.org author_email = distutils-sig@python.org
......
...@@ -18,10 +18,10 @@ __title__ = "packaging" ...@@ -18,10 +18,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages" __summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging" __uri__ = "https://github.com/pypa/packaging"
__version__ = "19.2" __version__ = "20.4"
__author__ = "Donald Stufft and individual contributors" __author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io" __email__ = "donald@stufft.io"
__license__ = "BSD or Apache License, Version 2.0" __license__ = "BSD-2-Clause or Apache-2.0"
__copyright__ = "Copyright 2014-2019 %s" % __author__ __copyright__ = "Copyright 2014-2019 %s" % __author__
...@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function ...@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function
import sys import sys
from ._typing import TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Dict, Tuple, Type
PY2 = sys.version_info[0] == 2 PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3 PY3 = sys.version_info[0] == 3
...@@ -18,14 +23,16 @@ else: ...@@ -18,14 +23,16 @@ else:
def with_metaclass(meta, *bases): def with_metaclass(meta, *bases):
# type: (Type[Any], Tuple[Type[Any], ...]) -> Any
""" """
Create a base class with a metaclass. Create a base class with a metaclass.
""" """
# This requires a bit of explanation: the basic idea is to make a dummy # This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with # metaclass for one level of class instantiation that replaces itself with
# the actual metaclass. # the actual metaclass.
class metaclass(meta): class metaclass(meta): # type: ignore
def __new__(cls, name, this_bases, d): def __new__(cls, name, this_bases, d):
# type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
return meta(name, bases, d) return meta(name, bases, d)
return type.__new__(metaclass, "temporary_class", (), {}) return type.__new__(metaclass, "temporary_class", (), {})
...@@ -4,65 +4,83 @@ ...@@ -4,65 +4,83 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
class Infinity(object): class InfinityType(object):
def __repr__(self): def __repr__(self):
# type: () -> str
return "Infinity" return "Infinity"
def __hash__(self): def __hash__(self):
# type: () -> int
return hash(repr(self)) return hash(repr(self))
def __lt__(self, other): def __lt__(self, other):
# type: (object) -> bool
return False return False
def __le__(self, other): def __le__(self, other):
# type: (object) -> bool
return False return False
def __eq__(self, other): def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__) return isinstance(other, self.__class__)
def __ne__(self, other): def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__) return not isinstance(other, self.__class__)
def __gt__(self, other): def __gt__(self, other):
# type: (object) -> bool
return True return True
def __ge__(self, other): def __ge__(self, other):
# type: (object) -> bool
return True return True
def __neg__(self): def __neg__(self):
# type: (object) -> NegativeInfinityType
return NegativeInfinity return NegativeInfinity
Infinity = Infinity() Infinity = InfinityType()
class NegativeInfinity(object): class NegativeInfinityType(object):
def __repr__(self): def __repr__(self):
# type: () -> str
return "-Infinity" return "-Infinity"
def __hash__(self): def __hash__(self):
# type: () -> int
return hash(repr(self)) return hash(repr(self))
def __lt__(self, other): def __lt__(self, other):
# type: (object) -> bool
return True return True
def __le__(self, other): def __le__(self, other):
# type: (object) -> bool
return True return True
def __eq__(self, other): def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__) return isinstance(other, self.__class__)
def __ne__(self, other): def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__) return not isinstance(other, self.__class__)
def __gt__(self, other): def __gt__(self, other):
# type: (object) -> bool
return False return False
def __ge__(self, other): def __ge__(self, other):
# type: (object) -> bool
return False return False
def __neg__(self): def __neg__(self):
# type: (object) -> InfinityType
return Infinity return Infinity
NegativeInfinity = NegativeInfinity() NegativeInfinity = NegativeInfinityType()
"""For neatly implementing static typing in packaging.
`mypy` - the static type analysis tool we use - uses the `typing` module, which
provides core functionality fundamental to mypy's functioning.
Generally, `typing` would be imported at runtime and used in that fashion -
it acts as a no-op at runtime and does not have any run-time overhead by
design.
As it turns out, `typing` is not vendorable - it uses separate sources for
Python 2/Python 3. Thus, this codebase can not expect it to be present.
To work around this, mypy allows the typing import to be behind a False-y
optional to prevent it from running at runtime and type-comments can be used
to remove the need for the types to be accessible directly during runtime.
This module provides the False-y guard in a nicely named fashion so that a
curious maintainer can reach here to read this.
In packaging, all static-typing related imports should be guarded as follows:
from packaging._typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import ...
Ref: https://github.com/python/mypy/issues/3216
"""
__all__ = ["TYPE_CHECKING", "cast"]
# The TYPE_CHECKING constant defined by the typing module is False at runtime
# but True while type checking.
if False: # pragma: no cover
from typing import TYPE_CHECKING
else:
TYPE_CHECKING = False
# typing's cast syntax requires calling typing.cast at runtime, but we don't
# want to import typing at runtime. Here, we inform the type checkers that
# we're importing `typing.cast` as `cast` and re-implement typing.cast's
# runtime behavior in a block that is ignored by type checkers.
if TYPE_CHECKING: # pragma: no cover
# not executed at runtime
from typing import cast
else:
# executed at runtime
def cast(type_, value): # noqa
return value
...@@ -13,8 +13,14 @@ from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString ...@@ -13,8 +13,14 @@ from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString
from setuptools.extern.pyparsing import Literal as L # noqa from setuptools.extern.pyparsing import Literal as L # noqa
from ._compat import string_types from ._compat import string_types
from ._typing import TYPE_CHECKING
from .specifiers import Specifier, InvalidSpecifier from .specifiers import Specifier, InvalidSpecifier
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
Operator = Callable[[str, str], bool]
__all__ = [ __all__ = [
"InvalidMarker", "InvalidMarker",
...@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError): ...@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError):
class Node(object): class Node(object):
def __init__(self, value): def __init__(self, value):
# type: (Any) -> None
self.value = value self.value = value
def __str__(self): def __str__(self):
# type: () -> str
return str(self.value) return str(self.value)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
def serialize(self): def serialize(self):
# type: () -> str
raise NotImplementedError raise NotImplementedError
class Variable(Node): class Variable(Node):
def serialize(self): def serialize(self):
# type: () -> str
return str(self) return str(self)
class Value(Node): class Value(Node):
def serialize(self): def serialize(self):
# type: () -> str
return '"{0}"'.format(self) return '"{0}"'.format(self)
class Op(Node): class Op(Node):
def serialize(self): def serialize(self):
# type: () -> str
return str(self) return str(self)
...@@ -85,13 +98,13 @@ VARIABLE = ( ...@@ -85,13 +98,13 @@ VARIABLE = (
| L("python_version") | L("python_version")
| L("sys_platform") | L("sys_platform")
| L("os_name") | L("os_name")
| L("os.name") | L("os.name") # PEP-345
| L("sys.platform") # PEP-345 | L("sys.platform") # PEP-345
| L("platform.version") # PEP-345 | L("platform.version") # PEP-345
| L("platform.machine") # PEP-345 | L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345 | L("platform.python_implementation") # PEP-345
| L("python_implementation") # PEP-345 | L("python_implementation") # undocumented setuptools legacy
| L("extra") # undocumented setuptools legacy | L("extra") # PEP-508
) )
ALIASES = { ALIASES = {
"os.name": "os_name", "os.name": "os_name",
...@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd ...@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd
def _coerce_parse_result(results): def _coerce_parse_result(results):
# type: (Union[ParseResults, List[Any]]) -> List[Any]
if isinstance(results, ParseResults): if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results] return [_coerce_parse_result(i) for i in results]
else: else:
...@@ -138,6 +152,8 @@ def _coerce_parse_result(results): ...@@ -138,6 +152,8 @@ def _coerce_parse_result(results):
def _format_marker(marker, first=True): def _format_marker(marker, first=True):
# type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
assert isinstance(marker, (list, tuple, string_types)) assert isinstance(marker, (list, tuple, string_types))
# Sometimes we have a structure like [[...]] which is a single item list # Sometimes we have a structure like [[...]] which is a single item list
...@@ -172,10 +188,11 @@ _operators = { ...@@ -172,10 +188,11 @@ _operators = {
"!=": operator.ne, "!=": operator.ne,
">=": operator.ge, ">=": operator.ge,
">": operator.gt, ">": operator.gt,
} } # type: Dict[str, Operator]
def _eval_op(lhs, op, rhs): def _eval_op(lhs, op, rhs):
# type: (str, Op, str) -> bool
try: try:
spec = Specifier("".join([op.serialize(), rhs])) spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier: except InvalidSpecifier:
...@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs): ...@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs):
else: else:
return spec.contains(lhs) return spec.contains(lhs)
oper = _operators.get(op.serialize()) oper = _operators.get(op.serialize()) # type: Optional[Operator]
if oper is None: if oper is None:
raise UndefinedComparison( raise UndefinedComparison(
"Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
...@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs): ...@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs):
return oper(lhs, rhs) return oper(lhs, rhs)
_undefined = object() class Undefined(object):
pass
_undefined = Undefined()
def _get_env(environment, name): def _get_env(environment, name):
value = environment.get(name, _undefined) # type: (Dict[str, str], str) -> str
value = environment.get(name, _undefined) # type: Union[str, Undefined]
if value is _undefined: if isinstance(value, Undefined):
raise UndefinedEnvironmentName( raise UndefinedEnvironmentName(
"{0!r} does not exist in evaluation environment.".format(name) "{0!r} does not exist in evaluation environment.".format(name)
) )
...@@ -207,7 +229,8 @@ def _get_env(environment, name): ...@@ -207,7 +229,8 @@ def _get_env(environment, name):
def _evaluate_markers(markers, environment): def _evaluate_markers(markers, environment):
groups = [[]] # type: (List[Any], Dict[str, str]) -> bool
groups = [[]] # type: List[List[bool]]
for marker in markers: for marker in markers:
assert isinstance(marker, (list, tuple, string_types)) assert isinstance(marker, (list, tuple, string_types))
...@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment): ...@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment):
def format_full_version(info): def format_full_version(info):
# type: (sys._version_info) -> str
version = "{0.major}.{0.minor}.{0.micro}".format(info) version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel kind = info.releaselevel
if kind != "final": if kind != "final":
...@@ -242,9 +266,13 @@ def format_full_version(info): ...@@ -242,9 +266,13 @@ def format_full_version(info):
def default_environment(): def default_environment():
# type: () -> Dict[str, str]
if hasattr(sys, "implementation"): if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version) # Ignoring the `sys.implementation` reference for type checking due to
implementation_name = sys.implementation.name # mypy not liking that the attribute doesn't exist in Python 2.7 when
# run with the `--py27` flag.
iver = format_full_version(sys.implementation.version) # type: ignore
implementation_name = sys.implementation.name # type: ignore
else: else:
iver = "0" iver = "0"
implementation_name = "" implementation_name = ""
...@@ -266,6 +294,7 @@ def default_environment(): ...@@ -266,6 +294,7 @@ def default_environment():
class Marker(object): class Marker(object):
def __init__(self, marker): def __init__(self, marker):
# type: (str) -> None
try: try:
self._markers = _coerce_parse_result(MARKER.parseString(marker)) self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e: except ParseException as e:
...@@ -275,12 +304,15 @@ class Marker(object): ...@@ -275,12 +304,15 @@ class Marker(object):
raise InvalidMarker(err_str) raise InvalidMarker(err_str)
def __str__(self): def __str__(self):
# type: () -> str
return _format_marker(self._markers) return _format_marker(self._markers)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<Marker({0!r})>".format(str(self)) return "<Marker({0!r})>".format(str(self))
def evaluate(self, environment=None): def evaluate(self, environment=None):
# type: (Optional[Dict[str, str]]) -> bool
"""Evaluate a marker. """Evaluate a marker.
Return the boolean from evaluating the given marker against the Return the boolean from evaluating the given marker against the
......
...@@ -11,9 +11,13 @@ from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combi ...@@ -11,9 +11,13 @@ from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combi
from setuptools.extern.pyparsing import Literal as L # noqa from setuptools.extern.pyparsing import Literal as L # noqa
from setuptools.extern.six.moves.urllib import parse as urlparse from setuptools.extern.six.moves.urllib import parse as urlparse
from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet from .specifiers import LegacySpecifier, Specifier, SpecifierSet
if TYPE_CHECKING: # pragma: no cover
from typing import List
class InvalidRequirement(ValueError): class InvalidRequirement(ValueError):
""" """
...@@ -89,6 +93,7 @@ class Requirement(object): ...@@ -89,6 +93,7 @@ class Requirement(object):
# TODO: Can we normalize the name and extra name? # TODO: Can we normalize the name and extra name?
def __init__(self, requirement_string): def __init__(self, requirement_string):
# type: (str) -> None
try: try:
req = REQUIREMENT.parseString(requirement_string) req = REQUIREMENT.parseString(requirement_string)
except ParseException as e: except ParseException as e:
...@@ -116,7 +121,8 @@ class Requirement(object): ...@@ -116,7 +121,8 @@ class Requirement(object):
self.marker = req.marker if req.marker else None self.marker = req.marker if req.marker else None
def __str__(self): def __str__(self):
parts = [self.name] # type: () -> str
parts = [self.name] # type: List[str]
if self.extras: if self.extras:
parts.append("[{0}]".format(",".join(sorted(self.extras)))) parts.append("[{0}]".format(",".join(sorted(self.extras))))
...@@ -135,4 +141,5 @@ class Requirement(object): ...@@ -135,4 +141,5 @@ class Requirement(object):
return "".join(parts) return "".join(parts)
def __repr__(self): def __repr__(self):
# type: () -> str
return "<Requirement({0!r})>".format(str(self)) return "<Requirement({0!r})>".format(str(self))
This diff is collapsed.
This diff is collapsed.
...@@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function ...@@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function
import re import re
from ._typing import TYPE_CHECKING, cast
from .version import InvalidVersion, Version from .version import InvalidVersion, Version
if TYPE_CHECKING: # pragma: no cover
from typing import NewType, Union
NormalizedName = NewType("NormalizedName", str)
_canonicalize_regex = re.compile(r"[-_.]+") _canonicalize_regex = re.compile(r"[-_.]+")
def canonicalize_name(name): def canonicalize_name(name):
# type: (str) -> NormalizedName
# This is taken from PEP 503. # This is taken from PEP 503.
return _canonicalize_regex.sub("-", name).lower() value = _canonicalize_regex.sub("-", name).lower()
return cast("NormalizedName", value)
def canonicalize_version(version): def canonicalize_version(_version):
# type: (str) -> Union[Version, str]
""" """
This is very similar to Version.__str__, but has one subtle differences This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment. with the way it handles the release segment.
""" """
try: try:
version = Version(version) version = Version(_version)
except InvalidVersion: except InvalidVersion:
# Legacy versions cannot be normalized # Legacy versions cannot be normalized
return version return _version
parts = [] parts = []
......
This diff is collapsed.
packaging==19.2 packaging==20.4
pyparsing==2.2.1 pyparsing==2.2.1
six==1.10.0 six==1.10.0
ordered-set==3.1.1 ordered-set==3.1.1
...@@ -75,6 +75,22 @@ class Distribution(setuptools.dist.Distribution): ...@@ -75,6 +75,22 @@ class Distribution(setuptools.dist.Distribution):
distutils.core.Distribution = orig distutils.core.Distribution = orig
@contextlib.contextmanager
def no_install_setup_requires():
"""Temporarily disable installing setup_requires
Under PEP 517, the backend reports build dependencies to the frontend,
and the frontend is responsible for ensuring they're installed.
So setuptools (acting as a backend) should not try to install them.
"""
orig = setuptools._install_setup_requires
setuptools._install_setup_requires = lambda attrs: None
try:
yield
finally:
setuptools._install_setup_requires = orig
def _to_str(s): def _to_str(s):
""" """
Convert a filename to a string (on Python 2, explicitly Convert a filename to a string (on Python 2, explicitly
...@@ -154,7 +170,8 @@ class _BuildMetaBackend(object): ...@@ -154,7 +170,8 @@ class _BuildMetaBackend(object):
config_settings=None): config_settings=None):
sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', sys.argv = sys.argv[:1] + ['dist_info', '--egg-base',
_to_str(metadata_directory)] _to_str(metadata_directory)]
self.run_setup() with no_install_setup_requires():
self.run_setup()
dist_info_directory = metadata_directory dist_info_directory = metadata_directory
while True: while True:
...@@ -194,7 +211,8 @@ class _BuildMetaBackend(object): ...@@ -194,7 +211,8 @@ class _BuildMetaBackend(object):
sys.argv = (sys.argv[:1] + setup_command + sys.argv = (sys.argv[:1] + setup_command +
['--dist-dir', tmp_dist_dir] + ['--dist-dir', tmp_dist_dir] +
config_settings["--global-option"]) config_settings["--global-option"])
self.run_setup() with no_install_setup_requires():
self.run_setup()
result_basename = _file_with_extension( result_basename = _file_with_extension(
tmp_dist_dir, result_extension) tmp_dist_dir, result_extension)
......
...@@ -55,11 +55,12 @@ def write_stub(resource, pyfile): ...@@ -55,11 +55,12 @@ def write_stub(resource, pyfile):
_stub_template = textwrap.dedent(""" _stub_template = textwrap.dedent("""
def __bootstrap__(): def __bootstrap__():
global __bootstrap__, __loader__, __file__ global __bootstrap__, __loader__, __file__
import sys, pkg_resources import sys, pkg_resources, importlib.util
from importlib.machinery import ExtensionFileLoader
__file__ = pkg_resources.resource_filename(__name__, %r) __file__ = pkg_resources.resource_filename(__name__, %r)
__loader__ = None; del __bootstrap__, __loader__ __loader__ = None; del __bootstrap__, __loader__
ExtensionFileLoader(__name__,__file__).load_module() spec = importlib.util.spec_from_file_location(__name__,__file__)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
__bootstrap__() __bootstrap__()
""").lstrip() """).lstrip()
with open(pyfile, 'w') as f: with open(pyfile, 'w') as f:
......
...@@ -254,8 +254,8 @@ class build_ext(_build_ext): ...@@ -254,8 +254,8 @@ class build_ext(_build_ext):
'\n'.join([ '\n'.join([
"def __bootstrap__():", "def __bootstrap__():",
" global __bootstrap__, __file__, __loader__", " global __bootstrap__, __file__, __loader__",
" import sys, os, pkg_resources" + if_dl(", dl"), " import sys, os, pkg_resources, importlib.util" +
" from importlib.machinery import ExtensionFileLoader", if_dl(", dl"),
" __file__ = pkg_resources.resource_filename" " __file__ = pkg_resources.resource_filename"
"(__name__,%r)" "(__name__,%r)"
% os.path.basename(ext._file_name), % os.path.basename(ext._file_name),
...@@ -267,8 +267,10 @@ class build_ext(_build_ext): ...@@ -267,8 +267,10 @@ class build_ext(_build_ext):
" try:", " try:",
" os.chdir(os.path.dirname(__file__))", " os.chdir(os.path.dirname(__file__))",
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
" ExtensionFileLoader(__name__,", " spec = importlib.util.spec_from_file_location(",
" __file__).load_module()", " __name__, __file__)",
" mod = importlib.util.module_from_spec(spec)",
" spec.loader.exec_module(mod)",
" finally:", " finally:",
if_dl(" sys.setdlopenflags(old_flags)"), if_dl(" sys.setdlopenflags(old_flags)"),
" os.chdir(old_dir)", " os.chdir(old_dir)",
......
...@@ -380,7 +380,7 @@ class TestBuildMetaBackend: ...@@ -380,7 +380,7 @@ class TestBuildMetaBackend:
setup( setup(
name="qux", name="qux",
version="0.0.0", version="0.0.0",
py_modules=["hello.py"], py_modules=["hello"],
setup_requires={setup_literal}, setup_requires={setup_literal},
) )
""").format(setup_literal=setup_literal), """).format(setup_literal=setup_literal),
...@@ -407,6 +407,35 @@ class TestBuildMetaBackend: ...@@ -407,6 +407,35 @@ class TestBuildMetaBackend:
assert expected == sorted(actual) assert expected == sorted(actual)
def test_dont_install_setup_requires(self, tmpdir_cwd):
files = {
'setup.py': DALS("""
from setuptools import setup
setup(
name="qux",
version="0.0.0",
py_modules=["hello"],
setup_requires=["does-not-exist >99"],
)
"""),
'hello.py': DALS("""
def run():
print('hello')
"""),
}
build_files(files)
build_backend = self.get_build_backend()
dist_dir = os.path.abspath('pip-dist-info')
os.makedirs(dist_dir)
# does-not-exist can't be satisfied, so if it attempts to install
# setup_requires, it will fail.
build_backend.prepare_metadata_for_build_wheel(dist_dir)
_sys_argv_0_passthrough = { _sys_argv_0_passthrough = {
'setup.py': DALS(""" 'setup.py': DALS("""
import os import os
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment