From c8b49cac1efa967065c418f93222419d109b06e5 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 17 Apr 2023 17:46:47 +0100 Subject: [PATCH] Revert "Upgrade setuptools to 67.6.1" This reverts commit d7e02483264703d6210e8c28937dcf1d9f547796. --- news/setuptools.vendor.rst | 1 + src/pip/_vendor/pkg_resources/__init__.py | 599 +++++++++----------- src/pip/_vendor/vendor.txt | 2 +- tools/vendoring/patches/pkg_resources.patch | 22 + 4 files changed, 293 insertions(+), 331 deletions(-) create mode 100644 news/setuptools.vendor.rst diff --git a/news/setuptools.vendor.rst b/news/setuptools.vendor.rst new file mode 100644 index 00000000000..2d6480a898b --- /dev/null +++ b/news/setuptools.vendor.rst @@ -0,0 +1 @@ +Revert pkg_resources (via setuptools) back to 65.6.3 diff --git a/src/pip/_vendor/pkg_resources/__init__.py b/src/pip/_vendor/pkg_resources/__init__.py index a85aca10f7c..0ec74f8a6ef 100644 --- a/src/pip/_vendor/pkg_resources/__init__.py +++ b/src/pip/_vendor/pkg_resources/__init__.py @@ -12,12 +12,6 @@ .egg files, and unpacked .egg files. It can also work in a limited way with .zip files and with custom PEP 302 loaders that support the ``get_data()`` method. - -This module is deprecated. Users are directed to -`importlib.resources `_ -and -`importlib.metadata `_ -instead. """ import sys @@ -40,6 +34,7 @@ import errno import tempfile import textwrap +import itertools import inspect import ntpath import posixpath @@ -59,10 +54,8 @@ # capture these to bypass sandboxing from os import utime - try: from os import mkdir, rename, unlink - WRITE_SUPPORT = True except ImportError: # no write support, probably under GAE @@ -73,7 +66,6 @@ try: import importlib.machinery as importlib_machinery - # access attribute to force import under delayed import mechanisms. importlib_machinery.__name__ except ImportError: @@ -87,7 +79,6 @@ from pip._vendor import platformdirs from pip._vendor import packaging - __import__('pip._vendor.packaging.version') __import__('pip._vendor.packaging.specifiers') __import__('pip._vendor.packaging.requirements') @@ -118,12 +109,6 @@ _namespace_packages = None -warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning) - - -_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) - - class PEP440Warning(RuntimeWarning): """ Used when there is an issue with a version or specifier not complying with @@ -131,7 +116,16 @@ class PEP440Warning(RuntimeWarning): """ -parse_version = packaging.version.Version +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + warnings.warn( + f"{v} is an invalid version and will not be supported in " + "a future release", + PkgResourcesDeprecationWarning, + ) + return packaging.version.LegacyVersion(v) _state_vars = {} @@ -203,87 +197,51 @@ def get_supported_platform(): __all__ = [ # Basic resource access and distribution/entry point discovery - 'require', - 'run_script', - 'get_provider', - 'get_distribution', - 'load_entry_point', - 'get_entry_map', - 'get_entry_info', + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points', - 'resource_string', - 'resource_stream', - 'resource_filename', - 'resource_listdir', - 'resource_exists', - 'resource_isdir', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + # Environmental control - 'declare_namespace', - 'working_set', - 'add_activation_listener', - 'find_distributions', - 'set_extraction_path', - 'cleanup_resources', + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', 'get_default_cache', + # Primary implementation classes - 'Environment', - 'WorkingSet', - 'ResourceManager', - 'Distribution', - 'Requirement', - 'EntryPoint', + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + # Exceptions - 'ResolutionError', - 'VersionConflict', - 'DistributionNotFound', - 'UnknownExtra', - 'ExtractionError', + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + # Warnings 'PEP440Warning', + # Parsing functions and string utilities - 'parse_requirements', - 'parse_version', - 'safe_name', - 'safe_version', - 'get_platform', - 'compatible_platforms', - 'yield_lines', - 'split_sections', - 'safe_extra', - 'to_filename', - 'invalid_marker', - 'evaluate_marker', + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + # filesystem utilities - 'ensure_directory', - 'normalize_path', + 'ensure_directory', 'normalize_path', + # Distribution "precedence" constants - 'EGG_DIST', - 'BINARY_DIST', - 'SOURCE_DIST', - 'CHECKOUT_DIST', - 'DEVELOP_DIST', + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + # "Provider" interfaces, implementations, and registration/lookup APIs - 'IMetadataProvider', - 'IResourceProvider', - 'FileMetadata', - 'PathMetadata', - 'EggMetadata', - 'EmptyProvider', - 'empty_provider', - 'NullProvider', - 'EggProvider', - 'DefaultProvider', - 'ZipProvider', - 'register_finder', - 'register_namespace_handler', - 'register_loader_type', - 'fixup_namespace_packages', - 'get_importer', + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + # Warnings 'PkgResourcesDeprecationWarning', + # Deprecated/backward compatibility only - 'run_main', - 'AvailableDistributions', + 'run_main', 'AvailableDistributions', ] @@ -342,10 +300,8 @@ def required_by(self): class DistributionNotFound(ResolutionError): """A requested distribution was not found""" - _template = ( - "The '{self.req}' distribution was not found " - "and is required by {self.requirers_str}" - ) + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") @property def req(self): @@ -439,8 +395,7 @@ def get_build_platform(): version = _macos_vers() machine = os.uname()[4].replace(" ", "_") return "macosx-%d.%d-%s" % ( - int(version[0]), - int(version[1]), + int(version[0]), int(version[1]), _macos_arch(machine), ) except ValueError: @@ -481,18 +436,15 @@ def compatible_platforms(provided, required): if provDarwin: dversion = int(provDarwin.group(1)) macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) - if ( - dversion == 7 - and macosversion >= "10.3" - or dversion == 8 - and macosversion >= "10.4" - ): + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": return True # egg isn't macOS or legacy darwin return False # are they the same major version and machine type? - if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3): + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): return False # is the required OS major update >= the provided one? @@ -554,8 +506,8 @@ def get_metadata(name): def get_metadata_lines(name): """Yield named metadata resource as list of non-blank non-comment lines - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted.""" + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" def metadata_isdir(name): """Is the named metadata a directory? (like ``os.path.isdir()``)""" @@ -768,14 +720,9 @@ def add(self, dist, entry=None, insert=True, replace=False): keys2.append(dist.key) self._added_new(dist) - def resolve( - self, - requirements, - env=None, - installer=None, - replace_conflicting=False, - extras=None, - ): + # FIXME: 'WorkingSet.resolve' is too complex (11) + def resolve(self, requirements, env=None, installer=None, # noqa: C901 + replace_conflicting=False, extras=None): """List all distributions needed to (recursively) meet `requirements` `requirements` must be a sequence of ``Requirement`` objects. `env`, @@ -824,9 +771,33 @@ def resolve( if not req_extras.markers_pass(req, extras): continue - dist = self._resolve_dist( - req, best, replace_conflicting, env, installer, required_by, to_activate - ) + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, + replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) # push the new requirements onto the stack new_requirements = dist.requires(req.extras)[::-1] @@ -842,38 +813,8 @@ def resolve( # return list of distros to activate return to_activate - def _resolve_dist( - self, req, best, replace_conflicting, env, installer, required_by, to_activate - ): - dist = best.get(req.key) - if dist is None: - # Find the best distribution and add it to the map - dist = self.by_key.get(req.key) - if dist is None or (dist not in req and replace_conflicting): - ws = self - if env is None: - if dist is None: - env = Environment(self.entries) - else: - # Use an empty environment and workingset to avoid - # any further conflicts with the conflicting - # distribution - env = Environment([]) - ws = WorkingSet([]) - dist = best[req.key] = env.best_match( - req, ws, installer, replace_conflicting=replace_conflicting - ) - if dist is None: - requirers = required_by.get(req, None) - raise DistributionNotFound(req, requirers) - to_activate.append(dist) - if dist not in req: - # Oops, the "best" so far conflicts with a dependency - dependent_req = required_by[req] - raise VersionConflict(dist, req).with_context(dependent_req) - return dist - - def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True): + def find_plugins( + self, plugin_env, full_env=None, installer=None, fallback=True): """Find all activatable distributions in `plugin_env` Example usage:: @@ -926,7 +867,9 @@ def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True) list(map(shadow_set.add, self)) for project_name in plugin_projects: + for dist in plugin_env[project_name]: + req = [dist.as_requirement()] try: @@ -990,11 +933,8 @@ def _added_new(self, dist): def __getstate__(self): return ( - self.entries[:], - self.entry_keys.copy(), - self.by_key.copy(), - self.normalized_to_canonical_keys.copy(), - self.callbacks[:], + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.normalized_to_canonical_keys.copy(), self.callbacks[:] ) def __setstate__(self, e_k_b_n_c): @@ -1030,8 +970,8 @@ class Environment: """Searchable snapshot of distributions on a search path""" def __init__( - self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR - ): + self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): """Snapshot distributions available on a search path Any distributions found on `search_path` are added to the environment. @@ -1098,14 +1038,16 @@ def __getitem__(self, project_name): return self._distmap.get(distribution_key, []) def add(self, dist): - """Add `dist` if we ``can_add()`` it and it has not already been added""" + """Add `dist` if we ``can_add()`` it and it has not already been added + """ if self.can_add(dist) and dist.has_version(): dists = self._distmap.setdefault(dist.key, []) if dist not in dists: dists.append(dist) dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) - def best_match(self, req, working_set, installer=None, replace_conflicting=False): + def best_match( + self, req, working_set, installer=None, replace_conflicting=False): """Find distribution best matching `req` and usable on `working_set` This calls the ``find(req)`` method of the `working_set` to see if a @@ -1192,7 +1134,6 @@ class ExtractionError(RuntimeError): class ResourceManager: """Manage resource extraction and packages""" - extraction_path = None def __init__(self): @@ -1204,7 +1145,9 @@ def resource_exists(self, package_or_requirement, resource_name): def resource_isdir(self, package_or_requirement, resource_name): """Is the named resource an existing directory?""" - return get_provider(package_or_requirement).resource_isdir(resource_name) + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) def resource_filename(self, package_or_requirement, resource_name): """Return a true filesystem path for specified resource""" @@ -1226,7 +1169,9 @@ def resource_string(self, package_or_requirement, resource_name): def resource_listdir(self, package_or_requirement, resource_name): """List the contents of the named resource directory""" - return get_provider(package_or_requirement).resource_listdir(resource_name) + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) def extraction_error(self): """Give an error message for problems extracting file(s)""" @@ -1234,8 +1179,7 @@ def extraction_error(self): old_exc = sys.exc_info()[1] cache_path = self.extraction_path or get_default_cache() - tmpl = textwrap.dedent( - """ + tmpl = textwrap.dedent(""" Can't extract file(s) to egg cache The following error occurred while trying to extract file(s) @@ -1250,8 +1194,7 @@ def extraction_error(self): Perhaps your account does not have write access to this directory? You can change the cache directory by setting the PYTHON_EGG_CACHE environment variable to point to an accessible directory. - """ - ).lstrip() + """).lstrip() err = ExtractionError(tmpl.format(**locals())) err.manager = self err.cache_path = cache_path @@ -1350,7 +1293,9 @@ def set_extraction_path(self, path): ``cleanup_resources()``.) """ if self.cached_files: - raise ValueError("Can't change extraction path, files already extracted") + raise ValueError( + "Can't change extraction path, files already extracted" + ) self.extraction_path = path @@ -1374,8 +1319,9 @@ def get_default_cache(): or a platform-relevant user cache dir for an app named "Python-Eggs". """ - return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir( - appname='Python-Eggs' + return ( + os.environ.get('PYTHON_EGG_CACHE') + or platformdirs.user_cache_dir(appname='Python-Eggs') ) @@ -1399,38 +1345,6 @@ def safe_version(version): return re.sub('[^A-Za-z0-9.]+', '-', version) -def _forgiving_version(version): - """Fallback when ``safe_version`` is not safe enough - >>> parse_version(_forgiving_version('0.23ubuntu1')) - - >>> parse_version(_forgiving_version('0.23-')) - - >>> parse_version(_forgiving_version('0.-_')) - - >>> parse_version(_forgiving_version('42.+?1')) - - >>> parse_version(_forgiving_version('hello world')) - - """ - version = version.replace(' ', '.') - match = _PEP440_FALLBACK.search(version) - if match: - safe = match["safe"] - rest = version[len(safe):] - else: - safe = "0" - rest = version - local = f"sanitized.{_safe_segment(rest)}".strip(".") - return f"{safe}.dev0+{local}" - - -def _safe_segment(segment): - """Convert an arbitrary string into a safe segment""" - segment = re.sub('[^A-Za-z0-9.]+', '-', segment) - segment = re.sub('-[^A-Za-z0-9]+', '-', segment) - return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") - - def safe_extra(extra): """Convert an arbitrary string to a standard 'extra' name @@ -1544,9 +1458,8 @@ def run_script(self, script_name, namespace): script = 'scripts/' + script_name if not self.has_metadata(script): raise ResolutionError( - "Script {script!r} not found in metadata at {self.egg_info!r}".format( - **locals() - ), + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), ) script_text = self.get_metadata(script).replace('\r\n', '\n') script_text = script_text.replace('\r', '\n') @@ -1559,12 +1472,8 @@ def run_script(self, script_name, namespace): exec(code, namespace, namespace) else: from linecache import cache - cache[script_filename] = ( - len(script_text), - 0, - script_text.split('\n'), - script_filename, + len(script_text), 0, script_text.split('\n'), script_filename ) script_code = compile(script_text, script_filename, 'exec') exec(script_code, namespace, namespace) @@ -1644,9 +1553,9 @@ def _validate_resource_path(path): AttributeError: ... """ invalid = ( - os.path.pardir in path.split(posixpath.sep) - or posixpath.isabs(path) - or ntpath.isabs(path) + os.path.pardir in path.split(posixpath.sep) or + posixpath.isabs(path) or + ntpath.isabs(path) ) if not invalid: return @@ -1728,10 +1637,7 @@ def _get(self, path): @classmethod def _register(cls): - loader_names = ( - 'SourceFileLoader', - 'SourcelessFileLoader', - ) + loader_names = 'SourceFileLoader', 'SourcelessFileLoader', for name in loader_names: loader_cls = getattr(importlib_machinery, name, type(None)) register_loader_type(loader_cls, cls) @@ -1791,7 +1697,6 @@ class MemoizedZipManifests(ZipManifests): """ Memoized zipfile manifests. """ - manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') def load(self, path): @@ -1825,16 +1730,20 @@ def _zipinfo_name(self, fspath): if fspath == self.loader.archive: return '' if fspath.startswith(self.zip_pre): - return fspath[len(self.zip_pre) :] - raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre)) + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) def _parts(self, zip_path): # Convert a zipfile subpath into an egg-relative path part list. # pseudo-fs path fspath = self.zip_pre + zip_path if fspath.startswith(self.egg_root + os.sep): - return fspath[len(self.egg_root) + 1 :].split(os.sep) - raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root)) + return fspath[len(self.egg_root) + 1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) @property def zipinfo(self): @@ -1864,20 +1773,25 @@ def _get_date_and_size(zip_stat): # FIXME: 'ZipProvider._extract_resource' is too complex (12) def _extract_resource(self, manager, zip_path): # noqa: C901 + if zip_path in self._index(): for name in self._index()[zip_path]: - last = self._extract_resource(manager, os.path.join(zip_path, name)) + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) # return the extracted directory name return os.path.dirname(last) timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) if not WRITE_SUPPORT: - raise IOError( - '"os.rename" and "os.unlink" are not supported ' 'on this platform' - ) + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') try: - real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path)) + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) if self._is_current(real_path, zip_path): return real_path @@ -2113,21 +2027,70 @@ def find_nothing(importer, path_item, only=False): register_finder(object, find_nothing) +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'bar', 'foo'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def try_parse(name): + """ + Attempt to parse as a version or return a null version. + """ + try: + return packaging.version.Version(name) + except Exception: + return packaging.version.Version('0') + + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [try_parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + def find_on_path(importer, path_item, only=False): """Yield distributions accessible on a sys.path directory""" path_item = _normalize_cached(path_item) if _is_unpacked_egg(path_item): yield Distribution.from_filename( - path_item, - metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')), + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) ) return - entries = (os.path.join(path_item, child) for child in 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 + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) # scan for .egg and .egg-info in directory - for entry in sorted(entries): + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: fullpath = os.path.join(path_item, entry) factory = dist_factory(path_item, entry, only) for dist in factory(fullpath): @@ -2138,18 +2101,19 @@ def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() 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_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 find_distributions - if not only and _is_egg_path(entry) - else resolve_egg_link - if not only and lower.endswith('.egg-link') - else NoDists() + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() ) @@ -2161,7 +2125,6 @@ class NoDists: >>> list(NoDists()('anything')) [] """ - def __bool__(self): return False @@ -2196,10 +2159,7 @@ def distributions_from_metadata(path): metadata = FileMetadata(path) entry = os.path.basename(path) yield Distribution.from_location( - root, - entry, - metadata, - precedence=DEVELOP_DIST, + root, entry, metadata, precedence=DEVELOP_DIST, ) @@ -2221,16 +2181,17 @@ def resolve_egg_link(path): """ referenced_paths = non_empty_lines(path) resolved_paths = ( - os.path.join(os.path.dirname(path), ref) for ref in referenced_paths + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths ) dist_groups = map(find_distributions, resolved_paths) return next(dist_groups, ()) -if hasattr(pkgutil, 'ImpImporter'): - register_finder(pkgutil.ImpImporter, find_on_path) +register_finder(pkgutil.ImpImporter, find_on_path) -register_finder(importlib_machinery.FileFinder, find_on_path) +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) _declare_state('dict', _namespace_handlers={}) _declare_state('dict', _namespace_packages={}) @@ -2328,15 +2289,6 @@ def position_in_sys_path(path): def declare_namespace(packageName): """Declare that package 'packageName' is a namespace package""" - msg = ( - f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n" - "Implementing implicit namespace packages (as specified in PEP 420) " - "is preferred to `pkg_resources.declare_namespace`. " - "See https://setuptools.pypa.io/en/latest/references/" - "keywords.html#keyword-namespace-packages" - ) - warnings.warn(msg, DeprecationWarning, stacklevel=2) - _imp.acquire_lock() try: if packageName in _namespace_packages: @@ -2393,11 +2345,11 @@ def file_ns_handler(importer, path_item, packageName, module): return subpath -if hasattr(pkgutil, 'ImpImporter'): - register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) - +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) register_namespace_handler(zipimport.zipimporter, file_ns_handler) -register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) def null_ns_handler(importer, path_item, packageName, module): @@ -2409,7 +2361,8 @@ def null_ns_handler(importer, path_item, packageName, module): 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 @@ -2440,9 +2393,9 @@ def _is_egg_path(path): def _is_zip_egg(path): return ( - path.lower().endswith('.egg') - and os.path.isfile(path) - and zipfile.is_zipfile(path) + path.lower().endswith('.egg') and + os.path.isfile(path) and + zipfile.is_zipfile(path) ) @@ -2450,8 +2403,9 @@ def _is_unpacked_egg(path): """ Determine if given path appears to be an unpacked egg. """ - return path.lower().endswith('.egg') and os.path.isfile( - os.path.join(path, 'EGG-INFO', 'PKG-INFO') + return ( + path.lower().endswith('.egg') and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) ) @@ -2615,10 +2569,8 @@ def _version_from_file(lines): Given an iterable of lines from a Metadata file, return the value of the Version field, if present, or None otherwise. """ - def is_version_line(line): return line.lower().startswith('version:') - version_lines = filter(is_version_line, lines) line = next(iter(version_lines), '') _, _, value = line.partition(':') @@ -2627,19 +2579,12 @@ def is_version_line(line): class Distribution: """Wrap an actual or potential sys.path entry w/metadata""" - PKG_INFO = 'PKG-INFO' def __init__( - self, - location=None, - metadata=None, - project_name=None, - version=None, - py_version=PY_MAJOR, - platform=None, - precedence=EGG_DIST, - ): + self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): self.project_name = safe_name(project_name or 'Unknown') if version is not None: self._version = safe_version(version) @@ -2662,13 +2607,8 @@ def from_location(cls, location, basename, metadata=None, **kw): 'name', 'ver', 'pyver', 'plat' ) return cls( - location, - metadata, - project_name=project_name, - version=version, - py_version=py_version, - platform=platform, - **kw, + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw )._reload_version() def _reload_version(self): @@ -2677,7 +2617,7 @@ def _reload_version(self): @property def hashcmp(self): return ( - self._forgiving_parsed_version, + self.parsed_version, self.precedence, self.key, self.location, @@ -2724,42 +2664,35 @@ def key(self): @property def parsed_version(self): if not hasattr(self, "_parsed_version"): - try: - self._parsed_version = parse_version(self.version) - except packaging.version.InvalidVersion as ex: - info = f"(package: {self.project_name})" - if hasattr(ex, "add_note"): - ex.add_note(info) # PEP 678 - raise - raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None + self._parsed_version = parse_version(self.version) return self._parsed_version - @property - def _forgiving_parsed_version(self): - try: - return self.parsed_version - except packaging.version.InvalidVersion as ex: - self._parsed_version = parse_version(_forgiving_version(self.version)) - - notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 - msg = f"""!!\n\n - ************************************************************************* - {str(ex)}\n{notes} + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return - This is a long overdue deprecation. - For the time being, `pkg_resources` will use `{self._parsed_version}` - as a replacement to avoid breaking existing environments, - but no future compatibility is guaranteed. + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return - If you maintain package {self.project_name} you should implement - the relevant changes to adequate the project to PEP 440 immediately. - ************************************************************************* - \n\n!! - """ - warnings.warn(msg, DeprecationWarning) + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') - return self._parsed_version + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) @property def version(self): @@ -2769,9 +2702,9 @@ def version(self): version = self._get_version() if version is None: path = self._get_metadata_path_for_display(self.PKG_INFO) - msg = ("Missing 'Version:' header and/or {} file at path: {}").format( - self.PKG_INFO, path - ) + msg = ( + "Missing 'Version:' header and/or {} file at path: {}" + ).format(self.PKG_INFO, path) raise ValueError(msg, self) from e return version @@ -2800,7 +2733,8 @@ def _filter_extras(dm): reqs = dm.pop(extra) new_extra, _, marker = extra.partition(':') fails_marker = marker and ( - invalid_marker(marker) or not evaluate_marker(marker) + invalid_marker(marker) + or not evaluate_marker(marker) ) if fails_marker: reqs = [] @@ -2872,9 +2806,8 @@ def activate(self, path=None, replace=False): def egg_name(self): """Return what this distribution's standard .egg filename should be""" filename = "%s-%s-py%s" % ( - to_filename(self.project_name), - to_filename(self.version), - self.py_version or PY_MAJOR, + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR ) if self.platform: @@ -2904,13 +2837,17 @@ def __getattr__(self, attr): def __dir__(self): return list( set(super(Distribution, self).__dir__()) - | set(attr for attr in self._provider.__dir__() if not attr.startswith('_')) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) ) @classmethod def from_filename(cls, filename, metadata=None, **kw): return cls.from_location( - _normalize_cached(filename), os.path.basename(filename), metadata, **kw + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw ) def as_requirement(self): @@ -3022,18 +2959,14 @@ def check_version_conflict(self): nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) loc = normalize_path(self.location) for modname in self._get_metadata('top_level.txt'): - if ( - modname not in sys.modules - or modname in nsp - or modname in _namespace_packages - ): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): continue if modname in ('pkg_resources', 'setuptools', 'site'): continue fn = getattr(sys.modules[modname], '__file__', None) - if fn and ( - normalize_path(fn).startswith(loc) or fn.startswith(self.location) - ): + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): continue issue_warning( "Module %s was already imported from %s, but %s is being added" @@ -3085,7 +3018,6 @@ class DistInfoDistribution(Distribution): Wrap an actual or potential sys.path entry w/metadata, .dist-info style. """ - PKG_INFO = 'METADATA' EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") @@ -3171,7 +3103,8 @@ def __init__(self, requirement_string): self.unsafe_name = self.name project_name = safe_name(self.name) self.project_name, self.key = project_name, project_name.lower() - self.specs = [(spec.operator, spec.version) for spec in self.specifier] + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] self.extras = tuple(map(safe_extra, self.extras)) self.hashCmp = ( self.key, @@ -3183,7 +3116,10 @@ def __init__(self, requirement_string): self.__hash = hash(self.hashCmp) def __eq__(self, other): - return isinstance(other, Requirement) and self.hashCmp == other.hashCmp + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) def __ne__(self, other): return not self == other @@ -3208,7 +3144,7 @@ def __repr__(self): @staticmethod def parse(s): - (req,) = parse_requirements(s) + req, = parse_requirements(s) return req @@ -3346,7 +3282,10 @@ def _initialize_master_working_set(): # ensure that all distributions added to the working set in the future # (e.g. by calling ``require()``) will get activated as well, # with higher priority (replace=True). - tuple(dist.activate(replace=False) for dist in working_set) + tuple( + dist.activate(replace=False) + for dist in working_set + ) add_activation_listener( lambda dist: dist.activate(replace=True), existing=False, diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 3974df3f11b..f15208b8bcd 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -16,7 +16,7 @@ rich==13.3.3 pygments==2.14.0 typing_extensions==4.5.0 resolvelib==1.0.1 -setuptools==67.6.1 +setuptools==65.6.3 six==1.16.0 tenacity==8.2.2 tomli==2.0.1 diff --git a/tools/vendoring/patches/pkg_resources.patch b/tools/vendoring/patches/pkg_resources.patch index 48ae954311b..39bb2eac253 100644 --- a/tools/vendoring/patches/pkg_resources.patch +++ b/tools/vendoring/patches/pkg_resources.patch @@ -1,3 +1,25 @@ +diff --git a/src/pip/_vendor/pkg_resources/__init__.py b/src/pip/_vendor/pkg_resources/__init__.py +index d59226af9..3b9565893 100644 +--- a/src/pip/_vendor/pkg_resources/__init__.py ++++ b/src/pip/_vendor/pkg_resources/__init__.py +@@ -77,7 +77,7 @@ + join_continuation, + ) + +-from pkg_resources.extern import appdirs ++from pkg_resources.extern import platformdirs + from pkg_resources.extern import packaging + __import__('pkg_resources.extern.packaging.version') + __import__('pkg_resources.extern.packaging.specifiers') +@@ -1321,7 +1321,7 @@ def get_default_cache(): + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') +- or appdirs.user_cache_dir(appname='Python-Eggs') ++ or platformdirs.user_cache_dir(appname='Python-Eggs') + ) + + diff --git a/src/pip/_vendor/pkg_resources/__init__.py b/src/pip/_vendor/pkg_resources/__init__.py index 3f2476a0c..8d5727d35 100644 --- a/src/pip/_vendor/pkg_resources/__init__.py