Repository: pypa/setuptools (Python)

Issue:
[BUG] `import pkg_resources` emits `DeprecationWarning`
### setuptools version

setuptools==59.1.0

### Python version

3.9.5

### OS

Ubuntu

### Additional environment information

_No response_

### Description

`DeprecationWarning` is emitted just by importing `pkg_resources`.
This seems like a regression in 59.1.0; this issue did not reproduce with 59.0.1.

### Expected behavior

No warnings were emitted by import.

### How to Reproduce

```
python -Werror -c 'import pkg_resources'
```

### Output

```
Traceback (most recent call last):
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py", line 49, in parse
    return Version(version)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py", line 266, in __init__
    raise InvalidVersion(f"Invalid version: '{version}'")
pkg_resources.extern.packaging.version.InvalidVersion: Invalid version: '/home/maehashi/.pyenv/versions/3.9.5/envs/local'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3257, in <module>
    def _initialize_master_working_set():
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3231, in _call_aside
    f(*args, **kwargs)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 3269, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 564, in _build_master
    ws = cls()
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 557, in __init__
    self.add_entry(entry)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 613, in add_entry
    for dist in find_distributions(entry, True):
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2064, in find_on_path
    path_item_entries = _by_version_descending(filtered)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2034, in _by_version_descending
    return sorted(names, key=_by_version, reverse=True)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2032, in _by_version
    return [packaging.version.parse(part) for part in parts]
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2032, in <listcomp>
    return [packaging.version.parse(part) for part in parts]
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py", line 51, in parse
    return LegacyVersion(version)
  File "/home/maehashi/.pyenv/versions/local-3.9.5/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py", line 111, in __init__
    warnings.warn(
DeprecationWarning: Creating a LegacyVersion has been deprecated and will be removed in the next major release
```

### Code of Conduct

- [X] I agree to follow the PSF Code of Conduct


Discussion:
The warning is intentionally emitted by packaging, which was updated in this version. The use of unsupported versions was previously deprecated, so I’m slightly surprised the error wasn’t triggered at the earlier warning in parse_version.

Oh. I see that parse is being called directly, not through parse_version. I did not realize that Version.parse had its own fallback.

In that case, I’m glad this caught the deprecation warning. Now we just have to figure out why a path is being passed as a version.
It's unclear to me what factor is triggering the error. Do you know what I could do to replicate the environment where this issue occurs? I do see that `_by_version_descending` is being called with a `name` that includes a path, possibly one with a dash in it.

Oh, my! I was able to replicate the issue easily just by invoking Python in Setuptools' own test environment:

```
$ ./.tox/python/bin/python -We -c "import pkg_resources"
Traceback (most recent call last):
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/_vendor/packaging/version.py", line 49, in parse
    return Version(version)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/_vendor/packaging/version.py", line 266, in __init__
    raise InvalidVersion(f"Invalid version: '{version}'")
pkg_resources.extern.packaging.version.InvalidVersion: Invalid version: '/Users/jaraco/code/public/pypa/setuptools/setuptools'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 3257, in <module>
    def _initialize_master_working_set():
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 3231, in _call_aside
    f(*args, **kwargs)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 3269, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 564, in _build_master
    ws = cls()
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 557, in __init__
    self.add_entry(entry)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 613, in add_entry
    for dist in find_distributions(entry, True):
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 2064, in find_on_path
    path_item_entries = _by_version_descending(filtered)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 2034, in _by_version_descending
    return sorted(names, key=_by_version, reverse=True)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 2032, in _by_version
    return [packaging.version.parse(part) for part in parts]
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/__init__.py", line 2032, in <listcomp>
    return [packaging.version.parse(part) for part in parts]
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/_vendor/packaging/version.py", line 51, in parse
    return LegacyVersion(version)
  File "/Users/jaraco/code/public/pypa/setuptools/pkg_resources/_vendor/packaging/version.py", line 111, in __init__
    warnings.warn(
DeprecationWarning: Creating a LegacyVersion has been deprecated and will be removed in the next major release
```

I'm going to yank this version for now to limit disruption while I work out a solution later this week.
![Screen Shot 2021-11-15 at 09 08 51](https://user-images.githubusercontent.com/308610/141795808-4a19e418-1f78-49a8-b1d6-e1cbc0958f35.png)




Patch:
diff --git a/changelog.d/2885.misc.rst b/changelog.d/2885.misc.rst
new file mode 100644
index 0000000000..1e121061f1
--- /dev/null
+++ b/changelog.d/2885.misc.rst
@@ -0,0 +1,1 @@
+Fixed errors when encountering LegacyVersions.
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 4aece3c44f..c615bc0965 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -2015,7 +2015,7 @@ def _by_version_descending(names):
 
     >>> 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', 'foo', 'bar']
+    ['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']
@@ -2023,13 +2023,22 @@ def _by_version_descending(names):
     >>> _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 [packaging.version.parse(part) for part in parts]
+        return [try_parse(part) for part in parts]
 
     return sorted(names, key=_by_version, reverse=True)
 
diff --git a/setuptools/config.py b/setuptools/config.py
index e3e44c25b8..b4e968e5ca 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -13,7 +13,7 @@
 import contextlib
 
 from distutils.errors import DistutilsOptionError, DistutilsFileError
-from setuptools.extern.packaging.version import LegacyVersion, parse
+from setuptools.extern.packaging.version import Version, InvalidVersion
 from setuptools.extern.packaging.specifiers import SpecifierSet
 
 
@@ -585,7 +585,9 @@ def _parse_version(self, value):
             version = version.strip()
             # Be strict about versions loaded from file because it's easy to
             # accidentally include newlines and other unintended content
-            if isinstance(parse(version), LegacyVersion):
+            try:
+                Version(version)
+            except InvalidVersion:
                 tmpl = (
                     'Version loaded from {value} does not '
                     'comply with PEP 440: {version}'
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index d818f44ade..270e7f3c91 100644
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -21,7 +21,7 @@
 from pkg_resources import (
     CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
     Environment, find_distributions, safe_name, safe_version,
-    to_filename, Requirement, DEVELOP_DIST, EGG_DIST,
+    to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version,
 )
 from distutils import log
 from distutils.errors import DistutilsError
@@ -294,6 +294,14 @@ def __init__(
         self.to_scan = []
         self.opener = urllib.request.urlopen
 
+    def add(self, dist):
+        # ignore invalid versions
+        try:
+            parse_version(dist.version)
+        except Exception:
+            return
+        return super().add(dist)
+
     # FIXME: 'PackageIndex.process_url' is too complex (14)
     def process_url(self, url, retrieve=False):  # noqa: C901
         """Evaluate a URL as a possible download, and maybe retrieve it"""


Test patch:
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index 965a7c0089..107dda7bab 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -703,7 +703,7 @@ def test_requirements_with_markers(self):
         )
 
     def test_local_version(self):
-        req, = parse_requirements('foo==1.0.org1')
+        req, = parse_requirements('foo==1.0+org1')
 
     def test_spaces_between_multiple_versions(self):
         req, = parse_requirements('foo>=1.0, <3')
diff --git a/pytest.ini b/pytest.ini
index 975baf68ca..8cc1052d12 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -34,7 +34,6 @@ filterwarnings=
 
 	# https://github.com/pypa/setuptools/issues/2497
 	ignore:.* is an invalid version and will not be supported::pkg_resources
-	ignore:Creating a LegacyVersion has been deprecated
 
 	# https://github.com/pypa/setuptools/pull/2865#issuecomment-965700112
 	# ideally would apply to Python 3.10+ when