diff options
Diffstat (limited to 'pkg_resources')
16 files changed, 141 insertions, 104 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 1f170cfd..edd3d2e8 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -55,7 +55,7 @@ except NameError: FileExistsError = OSError from pkg_resources.extern import six -from pkg_resources.extern.six.moves import urllib, map, filter +from pkg_resources.extern.six.moves import map, filter # capture these to bypass sandboxing from os import utime @@ -83,13 +83,14 @@ __import__('pkg_resources.extern.packaging.version') __import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.requirements') __import__('pkg_resources.extern.packaging.markers') +__import__('pkg_resources.py2_warn') __metaclass__ = type -if (3, 0) < sys.version_info < (3, 4): - raise RuntimeError("Python 3.4 or later is required") +if (3, 0) < sys.version_info < (3, 5): + raise RuntimeError("Python 3.5 or later is required") if six.PY2: # Those builtin exceptions are only defined in Python 3 @@ -178,10 +179,10 @@ def get_supported_platform(): """Return this platform's maximum compatible version. distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by + of macOS that would be required to *use* extensions produced by distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, we must also know the current version of the OS. If this condition occurs for any other platform with a version in its @@ -191,9 +192,9 @@ def get_supported_platform(): m = macosVersionString.match(plat) if m is not None and sys.platform == "darwin": try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3)) except ValueError: - # not Mac OS X + # not macOS pass return plat @@ -333,7 +334,7 @@ class UnknownExtra(ResolutionError): _provider_factories = {} -PY_MAJOR = sys.version[:3] +PY_MAJOR = '{}.{}'.format(*sys.version_info) EGG_DIST = 3 BINARY_DIST = 2 SOURCE_DIST = 1 @@ -364,7 +365,7 @@ def get_provider(moduleOrReq): return _find_adapter(_provider_factories, loader)(module) -def _macosx_vers(_cache=[]): +def _macos_vers(_cache=[]): if not _cache: version = platform.mac_ver()[0] # fallback for MacPorts @@ -380,7 +381,7 @@ def _macosx_vers(_cache=[]): return _cache[0] -def _macosx_arch(machine): +def _macos_arch(machine): return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) @@ -388,18 +389,18 @@ def get_build_platform(): """Return this platform's string for platform-specific distributions XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. + needs some hacks for Linux and macOS. """ from sysconfig import get_platform plat = get_platform() if sys.platform == "darwin" and not plat.startswith('macosx-'): try: - version = _macosx_vers() + version = _macos_vers() machine = os.uname()[4].replace(" ", "_") return "macosx-%d.%d-%s" % ( int(version[0]), int(version[1]), - _macosx_arch(machine), + _macos_arch(machine), ) except ValueError: # if someone is running a non-Mac darwin system, this will fall @@ -425,7 +426,7 @@ def compatible_platforms(provided, required): # easy case return True - # Mac OS X special cases + # macOS special cases reqMac = macosVersionString.match(required) if reqMac: provMac = macosVersionString.match(provided) @@ -434,7 +435,7 @@ def compatible_platforms(provided, required): if not provMac: # this is backwards compatibility for packages built before # setuptools 0.6. All packages built after this point will - # use the new macosx designation. + # use the new macOS designation. provDarwin = darwinVersionString.match(provided) if provDarwin: dversion = int(provDarwin.group(1)) @@ -442,7 +443,7 @@ def compatible_platforms(provided, required): if dversion == 7 and macosversion >= "10.3" or \ dversion == 8 and macosversion >= "10.4": return True - # egg isn't macosx or legacy darwin + # egg isn't macOS or legacy darwin return False # are they the same major version and machine type? @@ -1234,12 +1235,13 @@ class ResourceManager: mode = os.stat(path).st_mode if mode & stat.S_IWOTH or mode & stat.S_IWGRP: msg = ( - "%s is writable by group/others and vulnerable to attack " - "when " - "used with get_resource_filename. Consider a more secure " + "Extraction path is writable by group/others " + "and vulnerable to attack when " + "used with get_resource_filename ({path}). " + "Consider a more secure " "location (set with .set_extraction_path or the " - "PYTHON_EGG_CACHE environment variable)." % path - ) + "PYTHON_EGG_CACHE environment variable)." + ).format(**locals()) warnings.warn(msg, UserWarning) def postprocess(self, tempname, filename): @@ -2067,11 +2069,14 @@ def find_on_path(importer, path_item, only=False): def dist_factory(path_item, entry, only): - """ - Return a dist_factory for a path_item and entry - """ + """Return a dist_factory for the given entry.""" lower = entry.lower() - is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + is_egg_info = lower.endswith('.egg-info') + is_dist_info = ( + lower.endswith('.dist-info') and + os.path.isdir(os.path.join(path_item, entry)) + ) + is_meta = is_egg_info or is_dist_info return ( distributions_from_metadata if is_meta else @@ -2195,10 +2200,14 @@ def _handle_ns(packageName, path_item): if importer is None: return None - # capture warnings due to #1111 - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - loader = importer.find_module(packageName) + # use find_spec (PEP 451) and fall-back to find_module (PEP 302) + try: + loader = importer.find_spec(packageName).loader + except AttributeError: + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) if loader is None: return None @@ -2328,7 +2337,8 @@ register_namespace_handler(object, null_ns_handler) def normalize_path(filename): """Normalize a file/dir name for comparison purposes""" - return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + return os.path.normcase(os.path.realpath(os.path.normpath( + _cygwin_patch(filename)))) def _cygwin_patch(filename): # pragma: nocover @@ -2536,15 +2546,6 @@ class EntryPoint: return maps -def _remove_md5_fragment(location): - if not location: - return '' - parsed = urllib.parse.urlparse(location) - if parsed[-1].startswith('md5='): - return urllib.parse.urlunparse(parsed[:-1] + ('',)) - return location - - def _version_from_file(lines): """ Given an iterable of lines from a Metadata file, return @@ -2601,7 +2602,7 @@ class Distribution: self.parsed_version, self.precedence, self.key, - _remove_md5_fragment(self.location), + self.location, self.py_version or '', self.platform or '', ) @@ -3109,6 +3110,7 @@ class Requirement(packaging.requirements.Requirement): self.extras = tuple(map(safe_extra, self.extras)) self.hashCmp = ( self.key, + self.url, self.specifier, frozenset(self.extras), str(self.marker) if self.marker else None, @@ -3286,6 +3288,7 @@ def _initialize_master_working_set(): list(map(working_set.add_entry, sys.path)) globals().update(locals()) + class PkgResourcesDeprecationWarning(Warning): """ Base class for warning about deprecations in ``pkg_resources`` diff --git a/pkg_resources/api_tests.txt b/pkg_resources/api_tests.txt index 0a75170e..ded18800 100644 --- a/pkg_resources/api_tests.txt +++ b/pkg_resources/api_tests.txt @@ -36,7 +36,7 @@ Distributions have various introspectable attributes:: >>> dist.version '0.9' - >>> dist.py_version == sys.version[:3] + >>> dist.py_version == '{}.{}'.format(*sys.version_info) True >>> print(dist.platform) @@ -290,8 +290,8 @@ Platform Compatibility Rules ---------------------------- On the Mac, there are potential compatibility issues for modules compiled -on newer versions of Mac OS X than what the user is running. Additionally, -Mac OS X will soon have two platforms to contend with: Intel and PowerPC. +on newer versions of macOS than what the user is running. Additionally, +macOS will soon have two platforms to contend with: Intel and PowerPC. Basic equality works as on other platforms:: diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index c1eb9e99..bf98d8f2 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -43,13 +43,6 @@ class VendorImporter: __import__(extant) mod = sys.modules[extant] sys.modules[fullname] = mod - # mysterious hack: - # Remove the reference to the extant package/module - # on later Python versions to cause relative imports - # in the vendor package to resolve the same modules - # as those going through this importer. - if prefix and sys.version_info > (3, 3): - del sys.modules[extant] return mod except ImportError: pass diff --git a/pkg_resources/py2_warn.py b/pkg_resources/py2_warn.py new file mode 100644 index 00000000..bfc35234 --- /dev/null +++ b/pkg_resources/py2_warn.py @@ -0,0 +1,21 @@ +import sys +import warnings +import textwrap + + +msg = textwrap.dedent(""" + You are running Setuptools on Python 2, which is no longer + supported and + >>> SETUPTOOLS WILL STOP WORKING <<< + in a subsequent release (no sooner than 2020-04-20). + Please ensure you are installing + Setuptools using pip 9.x or later or pin to `setuptools<45` + in your environment. + If you have done those things and are still encountering + this message, please follow up at + https://bit.ly/setuptools-py2-warning. + """) + +pre = "Setuptools will stop working on Python 2\n" + +sys.version_info < (3,) and warnings.warn(pre + "*" * 60 + msg + "*" * 60) diff --git a/pkg_resources/tests/data/my-test-package-source/setup.cfg b/pkg_resources/tests/data/my-test-package-source/setup.cfg new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pkg_resources/tests/data/my-test-package-source/setup.cfg diff --git a/pkg_resources/tests/data/my-test-package-source/setup.py b/pkg_resources/tests/data/my-test-package-source/setup.py new file mode 100644 index 00000000..fe80d28f --- /dev/null +++ b/pkg_resources/tests/data/my-test-package-source/setup.py @@ -0,0 +1,6 @@ +import setuptools +setuptools.setup( + name="my-test-package", + version="1.0", + zip_safe=True, +) diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO new file mode 100644 index 00000000..7328e3f7 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: my-test-package +Version: 1.0 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 00000000..3c4ee167 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,7 @@ +setup.cfg +setup.py +my_test_package.egg-info/PKG-INFO +my_test_package.egg-info/SOURCES.txt +my_test_package.egg-info/dependency_links.txt +my_test_package.egg-info/top_level.txt +my_test_package.egg-info/zip-safe
\ No newline at end of file diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ + diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe @@ -0,0 +1 @@ + diff --git a/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg b/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg Binary files differnew file mode 100644 index 00000000..5115b895 --- /dev/null +++ b/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg diff --git a/pkg_resources/tests/test_find_distributions.py b/pkg_resources/tests/test_find_distributions.py index d735c590..f9594422 100644 --- a/pkg_resources/tests/test_find_distributions.py +++ b/pkg_resources/tests/test_find_distributions.py @@ -1,17 +1,9 @@ -import subprocess -import sys - +import py import pytest import pkg_resources -SETUP_TEMPLATE = """ -import setuptools -setuptools.setup( - name="my-test-package", - version="1.0", - zip_safe=True, -) -""".lstrip() + +TESTS_DATA_DIR = py.path.local(__file__).dirpath('data') class TestFindDistributions: @@ -21,46 +13,22 @@ class TestFindDistributions: target_dir = tmpdir.mkdir('target') # place a .egg named directory in the target that is not an egg: target_dir.mkdir('not.an.egg') - return str(target_dir) - - @pytest.fixture - def project_dir(self, tmpdir): - project_dir = tmpdir.mkdir('my-test-package') - (project_dir / "setup.py").write(SETUP_TEMPLATE) - return str(project_dir) + return target_dir def test_non_egg_dir_named_egg(self, target_dir): - dists = pkg_resources.find_distributions(target_dir) + dists = pkg_resources.find_distributions(str(target_dir)) assert not list(dists) - def test_standalone_egg_directory(self, project_dir, target_dir): - # install this distro as an unpacked egg: - args = [ - sys.executable, - '-c', 'from setuptools.command.easy_install import main; main()', - '-mNx', - '-d', target_dir, - '--always-unzip', - project_dir, - ] - subprocess.check_call(args) - dists = pkg_resources.find_distributions(target_dir) + def test_standalone_egg_directory(self, target_dir): + (TESTS_DATA_DIR / 'my-test-package_unpacked-egg').copy(target_dir) + dists = pkg_resources.find_distributions(str(target_dir)) assert [dist.project_name for dist in dists] == ['my-test-package'] - dists = pkg_resources.find_distributions(target_dir, only=True) + dists = pkg_resources.find_distributions(str(target_dir), only=True) assert not list(dists) - def test_zipped_egg(self, project_dir, target_dir): - # install this distro as an unpacked egg: - args = [ - sys.executable, - '-c', 'from setuptools.command.easy_install import main; main()', - '-mNx', - '-d', target_dir, - '--zip-ok', - project_dir, - ] - subprocess.check_call(args) - dists = pkg_resources.find_distributions(target_dir) + def test_zipped_egg(self, target_dir): + (TESTS_DATA_DIR / 'my-test-package_zipped-egg').copy(target_dir) + dists = pkg_resources.find_distributions(str(target_dir)) assert [dist.project_name for dist in dists] == ['my-test-package'] - dists = pkg_resources.find_distributions(target_dir, only=True) + dists = pkg_resources.find_distributions(str(target_dir), only=True) assert not list(dists) diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 5960868a..189a8668 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -17,7 +17,9 @@ try: except ImportError: import mock -from pkg_resources import DistInfoDistribution, Distribution, EggInfoDistribution +from pkg_resources import ( + DistInfoDistribution, Distribution, EggInfoDistribution, +) from setuptools.extern import six from pkg_resources.extern.six.moves import map from pkg_resources.extern.six import text_type, string_types @@ -279,8 +281,8 @@ def make_distribution_no_version(tmpdir, basename): ('dist-info', 'METADATA', DistInfoDistribution), ], ) -def test_distribution_version_missing(tmpdir, suffix, expected_filename, - expected_dist_type): +def test_distribution_version_missing( + tmpdir, suffix, expected_filename, expected_dist_type): """ Test Distribution.version when the "Version" header is missing. """ @@ -328,6 +330,14 @@ def test_distribution_version_missing_undetected_path(): assert msg == expected +@pytest.mark.parametrize('only', [False, True]) +def test_dist_info_is_not_dir(tmp_path, only): + """Test path containing a file with dist-info extension.""" + dist_info = tmp_path / 'foobar.dist-info' + dist_info.touch() + assert not pkg_resources.dist_factory(str(tmp_path), str(dist_info), only) + + class TestDeepVersionLookupDistutils: @pytest.fixture def env(self, tmpdir): diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index 86afcf74..ed7cdfcc 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -15,7 +15,7 @@ import pkg_resources from pkg_resources import ( parse_requirements, VersionConflict, parse_version, Distribution, EntryPoint, Requirement, safe_version, safe_name, - WorkingSet, PkgResourcesDeprecationWarning) + WorkingSet) # from Python 3.6 docs. @@ -116,7 +116,7 @@ class TestDistro: self.checkFooPkg(d) d = Distribution("/some/path") - assert d.py_version == sys.version[:3] + assert d.py_version == '{}.{}'.format(*sys.version_info) assert d.platform is None def testDistroParse(self): @@ -501,7 +501,6 @@ class TestEntryPoints: ep.load(require=False) - class TestRequirements: def testBasics(self): r = Requirement.parse("Twisted>=1.2") @@ -520,6 +519,11 @@ class TestRequirements: assert r1 == r2 assert str(r1) == str(r2) assert str(r2) == "Twisted==1.2c1,>=1.2" + assert ( + Requirement("Twisted") + != + Requirement("Twisted @ https://localhost/twisted.zip") + ) def testBasicContains(self): r = Requirement("Twisted>=1.2") @@ -546,11 +550,23 @@ class TestRequirements: == hash(( "twisted", + None, packaging.specifiers.SpecifierSet(">=1.2"), frozenset(["foo", "bar"]), None )) ) + assert ( + hash(Requirement.parse("Twisted @ https://localhost/twisted.zip")) + == + hash(( + "twisted", + "https://localhost/twisted.zip", + packaging.specifiers.SpecifierSet(), + frozenset(), + None + )) + ) def testVersionEquality(self): r1 = Requirement.parse("foo==0.3a2") diff --git a/pkg_resources/tests/test_working_set.py b/pkg_resources/tests/test_working_set.py index b3ca4ea8..7a759bbb 100644 --- a/pkg_resources/tests/test_working_set.py +++ b/pkg_resources/tests/test_working_set.py @@ -14,8 +14,8 @@ __metaclass__ = type def strip_comments(s): return '\n'.join( - l for l in s.split('\n') - if l.strip() and not l.strip().startswith('#') + line for line in s.split('\n') + if line.strip() and not line.strip().startswith('#') ) |
