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]
current_version = 49.3.0
current_version = 49.5.0
commit = 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
-------
......
......@@ -16,9 +16,12 @@ def warn_distutils_present():
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return
warnings.warn(
"Distutils was imported before Setuptools. This usage is discouraged "
"and may exhibit undesirable behaviors or errors. Please use "
"Setuptools' objects directly or at least import Setuptools first.")
"Distutils was imported before Setuptools, but importing Setuptools "
"also replaces the `distutils` module in `sys.modules`. This may lead "
"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():
......@@ -56,8 +59,8 @@ def do_override():
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
for more motivation.
"""
warn_distutils_present()
if enabled():
warn_distutils_present()
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
See ``to_filename()``.
``safe_version(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()``
except that spaces in the input become dots, and dots are allowed to exist
in the output. As with ``safe_name()``, if you are generating a filename
from this you should replace any "-" characters in the output with
underscores.
This will return the normalized form of any PEP 440 version. If the version
string is not PEP 440 compatible, this function behaves similar to
``safe_name()`` except that spaces in the input become dots, and dots are
allowed to exist in the output. As with ``safe_name()``, if you are
generating a filename from this you should replace any "-" characters in
the output with underscores.
``safe_extra(extra)``
Return a "safe" form of an extra's name, suitable for use in a requirement
......
......@@ -1988,11 +1988,11 @@ boilerplate code in some cases.
include_package_data = True
packages = find:
scripts =
bin/first.py
bin/second.py
bin/first.py
bin/second.py
install_requires =
requests
importlib; python_version == "2.6"
requests
importlib; python_version == "2.6"
[options.package_data]
* = *.txt, *.rst
......@@ -2028,8 +2028,8 @@ Metadata and options are set in the config sections of the same name.
[metadata]
keywords =
one
two
one
two
* In some cases, complex values can be provided in dedicated subsections for
clarity.
......
......@@ -2056,7 +2056,10 @@ def find_on_path(importer, path_item, only=False):
)
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,
# screen entries for only those that will yield
......@@ -2372,7 +2375,15 @@ def _is_egg_path(path):
"""
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):
......@@ -2380,7 +2391,7 @@ def _is_unpacked_egg(path):
Determine if given path appears to be an unpacked egg.
"""
return (
_is_egg_path(path) and
path.lower().endswith('.egg') and
os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO'))
)
......
......@@ -18,10 +18,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "19.2"
__version__ = "20.4"
__author__ = "Donald Stufft and individual contributors"
__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__
......@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function
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
PY3 = sys.version_info[0] == 3
......@@ -18,14 +23,16 @@ else:
def with_metaclass(meta, *bases):
# type: (Type[Any], Tuple[Type[Any], ...]) -> Any
"""
Create a base class with a metaclass.
"""
# 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
# the actual metaclass.
class metaclass(meta):
class metaclass(meta): # type: ignore
def __new__(cls, name, this_bases, d):
# type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
return meta(name, bases, d)
return type.__new__(metaclass, "temporary_class", (), {})
......@@ -4,65 +4,83 @@
from __future__ import absolute_import, division, print_function
class Infinity(object):
class InfinityType(object):
def __repr__(self):
# type: () -> str
return "Infinity"
def __hash__(self):
# type: () -> int
return hash(repr(self))
def __lt__(self, other):
# type: (object) -> bool
return False
def __le__(self, other):
# type: (object) -> bool
return False
def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__)
def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__)
def __gt__(self, other):
# type: (object) -> bool
return True
def __ge__(self, other):
# type: (object) -> bool
return True
def __neg__(self):
# type: (object) -> NegativeInfinityType
return NegativeInfinity
Infinity = Infinity()
Infinity = InfinityType()
class NegativeInfinity(object):
class NegativeInfinityType(object):
def __repr__(self):
# type: () -> str
return "-Infinity"
def __hash__(self):
# type: () -> int
return hash(repr(self))
def __lt__(self, other):
# type: (object) -> bool
return True
def __le__(self, other):
# type: (object) -> bool
return True
def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__)
def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__)
def __gt__(self, other):
# type: (object) -> bool
return False
def __ge__(self, other):
# type: (object) -> bool
return False
def __neg__(self):
# type: (object) -> InfinityType
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
from pkg_resources.extern.pyparsing import Literal as L # noqa
from ._compat import string_types
from ._typing import TYPE_CHECKING
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__ = [
"InvalidMarker",
......@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError):
class Node(object):
def __init__(self, value):
# type: (Any) -> None
self.value = value
def __str__(self):
# type: () -> str
return str(self.value)
def __repr__(self):
# type: () -> str
return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
def serialize(self):
# type: () -> str
raise NotImplementedError
class Variable(Node):
def serialize(self):
# type: () -> str
return str(self)
class Value(Node):
def serialize(self):
# type: () -> str
return '"{0}"'.format(self)
class Op(Node):
def serialize(self):
# type: () -> str
return str(self)
......@@ -85,13 +98,13 @@ VARIABLE = (
| L("python_version")
| L("sys_platform")
| L("os_name")
| L("os.name")
| L("os.name") # PEP-345
| L("sys.platform") # PEP-345
| L("platform.version") # PEP-345
| L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345
| L("python_implementation") # PEP-345
| L("extra") # undocumented setuptools legacy
| L("python_implementation") # undocumented setuptools legacy
| L("extra") # PEP-508
)
ALIASES = {
"os.name": "os_name",
......@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd
def _coerce_parse_result(results):
# type: (Union[ParseResults, List[Any]]) -> List[Any]
if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results]
else:
......@@ -138,6 +152,8 @@ def _coerce_parse_result(results):
def _format_marker(marker, first=True):
# type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
assert isinstance(marker, (list, tuple, string_types))
# Sometimes we have a structure like [[...]] which is a single item list
......@@ -172,10 +188,11 @@ _operators = {
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
}
} # type: Dict[str, Operator]
def _eval_op(lhs, op, rhs):
# type: (str, Op, str) -> bool
try:
spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier:
......@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs):
else:
return spec.contains(lhs)
oper = _operators.get(op.serialize())
oper = _operators.get(op.serialize()) # type: Optional[Operator]
if oper is None:
raise UndefinedComparison(
"Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
......@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs):
return oper(lhs, rhs)
_undefined = object()
class Undefined(object):
pass
_undefined = Undefined()
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(
"{0!r} does not exist in evaluation environment.".format(name)
)
......@@ -207,7 +229,8 @@ def _get_env(environment, name):
def _evaluate_markers(markers, environment):
groups = [[]]
# type: (List[Any], Dict[str, str]) -> bool
groups = [[]] # type: List[List[bool]]
for marker in markers:
assert isinstance(marker, (list, tuple, string_types))
......@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment):
def format_full_version(info):
# type: (sys._version_info) -> str
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
......@@ -242,9 +266,13 @@ def format_full_version(info):
def default_environment():
# type: () -> Dict[str, str]
if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
# Ignoring the `sys.implementation` reference for type checking due to
# 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:
iver = "0"
implementation_name = ""
......@@ -266,6 +294,7 @@ def default_environment():
class Marker(object):
def __init__(self, marker):
# type: (str) -> None
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
......@@ -275,12 +304,15 @@ class Marker(object):
raise InvalidMarker(err_str)
def __str__(self):
# type: () -> str
return _format_marker(self._markers)
def __repr__(self):
# type: () -> str
return "<Marker({0!r})>".format(str(self))
def evaluate(self, environment=None):
# type: (Optional[Dict[str, str]]) -> bool
"""Evaluate a marker.
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
from pkg_resources.extern.pyparsing import Literal as L # noqa
from pkg_resources.extern.six.moves.urllib import parse as urlparse
from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
if TYPE_CHECKING: # pragma: no cover
from typing import List
class InvalidRequirement(ValueError):
"""
......@@ -89,6 +93,7 @@ class Requirement(object):
# TODO: Can we normalize the name and extra name?
def __init__(self, requirement_string):
# type: (str) -> None
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
......@@ -116,7 +121,8 @@ class Requirement(object):
self.marker = req.marker if req.marker else None
def __str__(self):
parts = [self.name]
# type: () -> str
parts = [self.name] # type: List[str]
if self.extras:
parts.append("[{0}]".format(",".join(sorted(self.extras))))
......@@ -135,4 +141,5 @@ class Requirement(object):
return "".join(parts)
def __repr__(self):
# type: () -> str
return "<Requirement({0!r})>".format(str(self))
This diff is collapsed.
......@@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function
import re
from ._typing import TYPE_CHECKING, cast
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"[-_.]+")
def canonicalize_name(name):
# type: (str) -> NormalizedName
# 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.
"""
try:
version = Version(version)
version = Version(_version)
except InvalidVersion:
# Legacy versions cannot be normalized
return version
return _version
parts = []
......
This diff is collapsed.
packaging==19.2
packaging==20.4
pyparsing==2.2.1
six==1.10.0
appdirs==1.4.3
......@@ -16,7 +16,7 @@ formats = zip
[metadata]
name = setuptools
version = 49.3.0
version = 49.5.0
description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority
author_email = distutils-sig@python.org
......
......@@ -18,10 +18,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "19.2"
__version__ = "20.4"
__author__ = "Donald Stufft and individual contributors"
__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__
......@@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function
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
PY3 = sys.version_info[0] == 3
......@@ -18,14 +23,16 @@ else:
def with_metaclass(meta, *bases):
# type: (Type[Any], Tuple[Type[Any], ...]) -> Any
"""
Create a base class with a metaclass.
"""
# 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
# the actual metaclass.
class metaclass(meta):
class metaclass(meta): # type: ignore
def __new__(cls, name, this_bases, d):
# type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
return meta(name, bases, d)
return type.__new__(metaclass, "temporary_class", (), {})
......@@ -4,65 +4,83 @@
from __future__ import absolute_import, division, print_function
class Infinity(object):
class InfinityType(object):
def __repr__(self):
# type: () -> str
return "Infinity"
def __hash__(self):
# type: () -> int
return hash(repr(self))
def __lt__(self, other):
# type: (object) -> bool
return False
def __le__(self, other):
# type: (object) -> bool
return False
def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__)
def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__)
def __gt__(self, other):
# type: (object) -> bool
return True
def __ge__(self, other):
# type: (object) -> bool
return True
def __neg__(self):
# type: (object) -> NegativeInfinityType
return NegativeInfinity
Infinity = Infinity()
Infinity = InfinityType()
class NegativeInfinity(object):
class NegativeInfinityType(object):
def __repr__(self):
# type: () -> str
return "-Infinity"
def __hash__(self):
# type: () -> int
return hash(repr(self))
def __lt__(self, other):
# type: (object) -> bool
return True
def __le__(self, other):
# type: (object) -> bool
return True
def __eq__(self, other):
# type: (object) -> bool
return isinstance(other, self.__class__)
def __ne__(self, other):
# type: (object) -> bool
return not isinstance(other, self.__class__)
def __gt__(self, other):
# type: (object) -> bool
return False
def __ge__(self, other):
# type: (object) -> bool
return False
def __neg__(self):
# type: (object) -> InfinityType
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
from setuptools.extern.pyparsing import Literal as L # noqa
from ._compat import string_types
from ._typing import TYPE_CHECKING
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__ = [
"InvalidMarker",
......@@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError):
class Node(object):
def __init__(self, value):
# type: (Any) -> None
self.value = value
def __str__(self):
# type: () -> str
return str(self.value)
def __repr__(self):
# type: () -> str
return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
def serialize(self):
# type: () -> str
raise NotImplementedError
class Variable(Node):
def serialize(self):
# type: () -> str
return str(self)
class Value(Node):
def serialize(self):
# type: () -> str
return '"{0}"'.format(self)
class Op(Node):
def serialize(self):
# type: () -> str
return str(self)
......@@ -85,13 +98,13 @@ VARIABLE = (
| L("python_version")
| L("sys_platform")
| L("os_name")
| L("os.name")
| L("os.name") # PEP-345
| L("sys.platform") # PEP-345
| L("platform.version") # PEP-345
| L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345
| L("python_implementation") # PEP-345
| L("extra") # undocumented setuptools legacy
| L("python_implementation") # undocumented setuptools legacy
| L("extra") # PEP-508
)
ALIASES = {
"os.name": "os_name",
......@@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd
def _coerce_parse_result(results):
# type: (Union[ParseResults, List[Any]]) -> List[Any]
if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results]
else:
......@@ -138,6 +152,8 @@ def _coerce_parse_result(results):
def _format_marker(marker, first=True):
# type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
assert isinstance(marker, (list, tuple, string_types))
# Sometimes we have a structure like [[...]] which is a single item list
......@@ -172,10 +188,11 @@ _operators = {
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
}
} # type: Dict[str, Operator]
def _eval_op(lhs, op, rhs):
# type: (str, Op, str) -> bool
try:
spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier:
......@@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs):
else:
return spec.contains(lhs)
oper = _operators.get(op.serialize())
oper = _operators.get(op.serialize()) # type: Optional[Operator]
if oper is None:
raise UndefinedComparison(
"Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
......@@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs):
return oper(lhs, rhs)
_undefined = object()
class Undefined(object):
pass
_undefined = Undefined()
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(
"{0!r} does not exist in evaluation environment.".format(name)
)
......@@ -207,7 +229,8 @@ def _get_env(environment, name):
def _evaluate_markers(markers, environment):
groups = [[]]
# type: (List[Any], Dict[str, str]) -> bool
groups = [[]] # type: List[List[bool]]
for marker in markers:
assert isinstance(marker, (list, tuple, string_types))
......@@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment):
def format_full_version(info):
# type: (sys._version_info) -> str
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
......@@ -242,9 +266,13 @@ def format_full_version(info):
def default_environment():
# type: () -> Dict[str, str]
if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
# Ignoring the `sys.implementation` reference for type checking due to
# 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:
iver = "0"
implementation_name = ""
......@@ -266,6 +294,7 @@ def default_environment():
class Marker(object):
def __init__(self, marker):
# type: (str) -> None
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
......@@ -275,12 +304,15 @@ class Marker(object):
raise InvalidMarker(err_str)
def __str__(self):
# type: () -> str
return _format_marker(self._markers)
def __repr__(self):
# type: () -> str
return "<Marker({0!r})>".format(str(self))
def evaluate(self, environment=None):
# type: (Optional[Dict[str, str]]) -> bool
"""Evaluate a marker.
Return the boolean from evaluating the given marker against the
......
......@@ -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.six.moves.urllib import parse as urlparse
from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
if TYPE_CHECKING: # pragma: no cover
from typing import List
class InvalidRequirement(ValueError):
"""
......@@ -89,6 +93,7 @@ class Requirement(object):
# TODO: Can we normalize the name and extra name?
def __init__(self, requirement_string):
# type: (str) -> None
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
......@@ -116,7 +121,8 @@ class Requirement(object):
self.marker = req.marker if req.marker else None
def __str__(self):
parts = [self.name]
# type: () -> str
parts = [self.name] # type: List[str]
if self.extras:
parts.append("[{0}]".format(",".join(sorted(self.extras))))
......@@ -135,4 +141,5 @@ class Requirement(object):
return "".join(parts)
def __repr__(self):
# type: () -> str
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
import re
from ._typing import TYPE_CHECKING, cast
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"[-_.]+")
def canonicalize_name(name):
# type: (str) -> NormalizedName
# 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.
"""
try:
version = Version(version)
version = Version(_version)
except InvalidVersion:
# Legacy versions cannot be normalized
return version
return _version
parts = []
......
This diff is collapsed.
packaging==19.2
packaging==20.4
pyparsing==2.2.1
six==1.10.0
ordered-set==3.1.1
......@@ -75,6 +75,22 @@ class Distribution(setuptools.dist.Distribution):
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):
"""
Convert a filename to a string (on Python 2, explicitly
......@@ -154,7 +170,8 @@ class _BuildMetaBackend(object):
config_settings=None):
sys.argv = sys.argv[:1] + ['dist_info', '--egg-base',
_to_str(metadata_directory)]
self.run_setup()
with no_install_setup_requires():
self.run_setup()
dist_info_directory = metadata_directory
while True:
......@@ -194,7 +211,8 @@ class _BuildMetaBackend(object):
sys.argv = (sys.argv[:1] + setup_command +
['--dist-dir', tmp_dist_dir] +
config_settings["--global-option"])
self.run_setup()
with no_install_setup_requires():
self.run_setup()
result_basename = _file_with_extension(
tmp_dist_dir, result_extension)
......
......@@ -55,11 +55,12 @@ def write_stub(resource, pyfile):
_stub_template = textwrap.dedent("""
def __bootstrap__():
global __bootstrap__, __loader__, __file__
import sys, pkg_resources
from importlib.machinery import ExtensionFileLoader
import sys, pkg_resources, importlib.util
__file__ = pkg_resources.resource_filename(__name__, %r)
__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__()
""").lstrip()
with open(pyfile, 'w') as f:
......
......@@ -254,8 +254,8 @@ class build_ext(_build_ext):
'\n'.join([
"def __bootstrap__():",
" global __bootstrap__, __file__, __loader__",
" import sys, os, pkg_resources" + if_dl(", dl"),
" from importlib.machinery import ExtensionFileLoader",
" import sys, os, pkg_resources, importlib.util" +
if_dl(", dl"),
" __file__ = pkg_resources.resource_filename"
"(__name__,%r)"
% os.path.basename(ext._file_name),
......@@ -267,8 +267,10 @@ class build_ext(_build_ext):
" try:",
" os.chdir(os.path.dirname(__file__))",
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
" 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)",
" finally:",
if_dl(" sys.setdlopenflags(old_flags)"),
" os.chdir(old_dir)",
......
......@@ -380,7 +380,7 @@ class TestBuildMetaBackend:
setup(
name="qux",
version="0.0.0",
py_modules=["hello.py"],
py_modules=["hello"],
setup_requires={setup_literal},
)
""").format(setup_literal=setup_literal),
......@@ -407,6 +407,35 @@ class TestBuildMetaBackend:
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 = {
'setup.py': DALS("""
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