From b19957dd2bd54909014ba95b1ee5bca55000561c Mon Sep 17 00:00:00 2001 From: John Sirois Date: Tue, 8 Jan 2019 08:55:44 -0800 Subject: [PATCH] Support PEP-503 name normalization. Fixes #614 --- pex/package.py | 12 ++++++------ pex/resolvable.py | 3 ++- pex/resolver.py | 5 ++--- pex/resolver_options.py | 6 +++--- pex/util.py | 12 ++++++++++++ tests/test_resolvable.py | 5 +++-- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/pex/package.py b/pex/package.py index cc90537c5..4e301fa57 100644 --- a/pex/package.py +++ b/pex/package.py @@ -9,8 +9,8 @@ from pex.base import maybe_requirement from pex.link import Link from pex.pep425tags import get_supported -from pex.third_party.pkg_resources import EGG_NAME, parse_version, safe_name, safe_version -from pex.util import Memoizer +from pex.third_party.pkg_resources import EGG_NAME, parse_version, safe_version +from pex.util import Memoizer, normalized_name class Package(Link): @@ -80,8 +80,8 @@ def satisfies(self, requirement, allow_prereleases=None): :returns: True if the package matches the requirement, otherwise False """ requirement = maybe_requirement(requirement) - link_name = safe_name(self.name).lower() - if link_name != requirement.key: + link_name = normalized_name(self.name) + if link_name != normalized_name(requirement.key): return False # NB: If we upgrade to setuptools>=34 the SpecifierSet used here (requirement.specifier) will @@ -136,7 +136,7 @@ def __init__(self, url, **kw): @property def name(self): - return safe_name(self._name) + return normalized_name(self._name) @property def raw_version(self): @@ -202,7 +202,7 @@ def __hash__(self): @property def name(self): - return safe_name(self._name) + return normalized_name(self._name) @property def raw_version(self): diff --git a/pex/resolvable.py b/pex/resolvable.py index 31f68a9e6..dfb94c204 100644 --- a/pex/resolvable.py +++ b/pex/resolvable.py @@ -14,6 +14,7 @@ from pex.package import Package from pex.resolver_options import ResolverOptionsBuilder, ResolverOptionsInterface from pex.third_party.pkg_resources import Requirement, safe_extra +from pex.util import normalized_name # Extract extras as specified per "declaring extras": # https://pythonhosted.org/setuptools/setuptools.html @@ -228,7 +229,7 @@ def packages(self): @property def name(self): - return self.requirement.key + return normalized_name(self.requirement.key) @property def exact(self): diff --git a/pex/resolver.py b/pex/resolver.py index 3fd9a764d..a0e1ded45 100644 --- a/pex/resolver.py +++ b/pex/resolver.py @@ -20,9 +20,8 @@ from pex.platforms import Platform from pex.resolvable import ResolvableRequirement, resolvables_from_iterable from pex.resolver_options import ResolverOptionsBuilder -from pex.third_party.pkg_resources import safe_name from pex.tracer import TRACER -from pex.util import DistributionHelper +from pex.util import DistributionHelper, normalized_name @contextmanager @@ -78,7 +77,7 @@ def merge(self, other): class _ResolvableSet(object): @classmethod def normalize(cls, name): - return safe_name(name).lower() + return normalized_name(name) def __init__(self, tuples=None): # A list of _ResolvedPackages diff --git a/pex/resolver_options.py b/pex/resolver_options.py index 4de803f20..2a7194d8f 100644 --- a/pex/resolver_options.py +++ b/pex/resolver_options.py @@ -10,8 +10,8 @@ from pex.iterator import Iterator from pex.package import EggPackage, SourcePackage, WheelPackage from pex.sorter import Sorter -from pex.third_party.pkg_resources import safe_name from pex.translator import ChainedTranslator, EggTranslator, SourceTranslator, WheelTranslator +from pex.util import normalized_name class ResolverOptionsInterface(object): @@ -92,11 +92,11 @@ def allow_all_external(self): return self def allow_external(self, key): - self._allow_external.add(safe_name(key).lower()) + self._allow_external.add(normalized_name(key)) return self def allow_unverified(self, key): - self._allow_unverified.add(safe_name(key).lower()) + self._allow_unverified.add(normalized_name(key)) return self def use_wheel(self): diff --git a/pex/util.py b/pex/util.py index 160ad514b..55c0f4eec 100644 --- a/pex/util.py +++ b/pex/util.py @@ -5,6 +5,7 @@ import contextlib import os +import re import shutil import tempfile import uuid @@ -264,3 +265,14 @@ def merge_split(*paths): """ filtered_paths = filter(None, paths) return [p for p in ':'.join(filtered_paths).split(':') if p] + + +def normalized_name(name): + """Return a PEP-503 normalized version of the given name. + + See: https://www.python.org/dev/peps/pep-0503/#normalized-names + + :param str name: The distribution name to normalize. + :rtype: str + """ + return re.sub(r"[-_.]+", "-", name).lower() diff --git a/tests/test_resolvable.py b/tests/test_resolvable.py index 831423f05..c6921833b 100644 --- a/tests/test_resolvable.py +++ b/tests/test_resolvable.py @@ -17,6 +17,7 @@ ) from pex.resolver_options import ResolverOptionsBuilder from pex.testing import make_source_dir +from pex.util import normalized_name try: from unittest import mock @@ -89,11 +90,11 @@ def test_resolvable_directory(): with make_source_dir(name='my_project') as td: rdir = ResolvableDirectory.from_string(td, builder, interpreter) - assert rdir.name == pkg_resources.safe_name('my_project') + assert rdir.name == normalized_name('my_project') assert rdir.extras() == [] rdir = ResolvableDirectory.from_string(td + '[extra1,extra2]', builder, interpreter) - assert rdir.name == pkg_resources.safe_name('my_project') + assert rdir.name == normalized_name('my_project') assert rdir.extras() == ['extra1', 'extra2']