Repository: sputt/req-compile

Issue:
Invalid Versions
A single invalid version listed on a candidates html page causes compilation to fail.  These candidates should be ignored or otherwise flagged and compilation should continue.For example, with setuptools 66+, joblib on pypi has a version which is no longer valid:
pkg_resources.extern.packaging.version.InvalidVersion: Invalid version: '0.3.2d.dev'

Discussion:

Patch:
diff --git a/pylintrc b/pylintrc
index d40030d..223e587 100644
--- a/pylintrc
+++ b/pylintrc
@@ -11,4 +11,4 @@ disable=
   bad-continuation,
   consider-using-f-string,
 
-ignored-modules=distutils.core
\ No newline at end of file
+ignored-modules=distutils.core
diff --git a/req_compile/filename.py b/req_compile/filename.py
index 6206248..48f77ef 100644
--- a/req_compile/filename.py
+++ b/req_compile/filename.py
@@ -59,5 +59,8 @@ def parse_source_filename(
             version_parts = version_parts[:idx]
             break
 
-    version = utils.parse_version(".".join(version_parts))
+    try:
+        version = utils.parse_version(".".join(version_parts))
+    except Exception:  # pylint: disable=broad-except
+        version = None
     return pkg_name, version
diff --git a/req_compile/repos/repository.py b/req_compile/repos/repository.py
index ddcaad8..fff8374 100644
--- a/req_compile/repos/repository.py
+++ b/req_compile/repos/repository.py
@@ -103,11 +103,7 @@ def manylinux_tag_is_compatible_with_this_system(tag: str) -> bool:
     else:
         if hasattr(_manylinux, "manylinux_compatible"):
             # pylint: disable=no-member
-            result = _manylinux.manylinux_compatible(
-                tag_major,
-                tag_minor,
-                tag_arch,
-            )
+            result = _manylinux.manylinux_compatible(tag_major, tag_minor, tag_arch,)
             if result is not None:
                 return bool(result)
         else:
@@ -444,8 +440,11 @@ def _wheel_filename_to_candidate(source: Any, filename: str) -> Optional[Candida
         build_tag = data_parts.pop(2)
     name = data_parts[0]
     abi = data_parts[3]
-    #  Convert old-style post-versions to new style so it will sort correctly
-    version = parse_version(data_parts[1].replace("_", "-"))
+    try:
+        #  Convert old-style post-versions to new style so it will sort correctly
+        version = parse_version(data_parts[1].replace("_", "-"))
+    except Exception:  # pylint: disable=broad-except
+        return None
     plats = data_parts[4].split(".")
     requires_python = WheelVersionTags(tuple(data_parts[2].split(".")))
 
@@ -743,9 +742,7 @@ class Repository(metaclass=abc.ABCMeta):
     ):  # pylint: disable=invalid-name
         # type: (pkg_resources.Requirement, Candidate, Set[NormName]) -> CantUseReason
         reason = check_usability(
-            req,
-            candidate,
-            allow_prereleases=self.allow_prerelease,
+            req, candidate, allow_prereleases=self.allow_prerelease,
         )
         if reason is None or reason == CantUseReason.U_CAN_USE:
             if (
diff --git a/requirements.in b/requirements.in
index 1dc9a4e..5106357 100644
--- a/requirements.in
+++ b/requirements.in
@@ -5,3 +5,4 @@ packaging
 wheel
 overrides
 typing_extensions
+setuptools
diff --git a/requirements.txt b/requirements.txt
index b40ce3f..1592e5c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,44 +1,47 @@
-appdirs==1.4.4              # req-compile
-astroid==2.14.1             # pylint (<=2.16.0-dev0,>=2.14.1)
+--index-url https://pypi.org/simple
+
+appdirs==1.4.4              # requirements.in
+astroid==2.15.0             # pylint (<=2.17.0-dev0,>=2.15.0)
 attrs==22.2.0               # pytest (>=19.2.0)
 black==23.1.0               # test-requirements.in
 certifi==2022.12.7          # requests (>=2017.4.17)
-charset-normalizer==3.0.1   # requests (<4,>=2)
+charset-normalizer==3.1.0   # requests (<4,>=2)
 click==8.1.3                # black (>=8.0.0)
-colorama==0.4.6             # click, pylint (>=0.4.5), pytest
 dill==0.3.6                 # pylint (>=0.2)
-exceptiongroup==1.1.0       # pytest (>=1.0.0rc8)
+exceptiongroup==1.1.1       # pytest (>=1.0.0rc8)
 idna==3.4                   # requests (<4,>=2.5)
-importlib-metadata==6.0.0   # click, pluggy (>=0.12), pytest (>=0.12)
+importlib-metadata==6.1.0   # click, pluggy (>=0.12), pytest (>=0.12)
 iniconfig==2.0.0            # pytest
 isort==5.11.5               # pylint (<6,>=4.2.5), test-requirements.in
 lazy-object-proxy==1.9.0    # astroid (>=1.4.0)
 mccabe==0.7.0               # pylint (<0.8,>=0.6)
-mypy==1.0.0                 # test-requirements.in
-mypy-extensions==1.0.0      # black (>=0.4.3), mypy (>=0.4.3)
-overrides==7.3.1            # req-compile
-packaging==23.0             # black (>=22.0), pytest, req-compile
-pathspec==0.11.0            # black (>=0.9.0)
-platformdirs==3.0.0         # black (>=2), pylint (>=2.2.0)
+mypy==1.1.1                 # test-requirements.in
+mypy-extensions==1.0.0      # black (>=0.4.3), mypy (>=1.0.0)
+overrides==7.3.1            # requirements.in
+packaging==23.0             # black (>=22.0), pytest, requirements.in
+pathspec==0.11.1            # black (>=0.9.0)
+platformdirs==3.1.1         # black (>=2), pylint (>=2.2.0)
 pluggy==1.0.0               # pytest (<2.0,>=0.12)
-pylint==2.16.1              # test-requirements.in
-pytest==7.2.1               # pytest-mock (>=5.0), test-requirements.in
+pylint==2.17.0              # test-requirements.in
+pytest==7.2.2               # pytest-mock (>=5.0), test-requirements.in
 pytest-mock==3.10.0         # test-requirements.in
-requests==2.28.2            # req-compile, responses (<3.0,>=2.22.0)
-responses==0.22.0           # test-requirements.in
+PyYAML==6.0                 # responses
+requests==2.28.2            # requirements.in, responses (<3.0,>=2.22.0)
+responses==0.23.1           # test-requirements.in
+setuptools==67.6.0          # requirements.in
 six==1.16.0                 # test-requirements.in
-toml==0.10.2                # req-compile, responses
+toml==0.10.2                # requirements.in
 tomli==2.0.1                # black (>=1.1.0), mypy (>=1.1.0), pylint (>=1.1.0), pytest (>=1.0.0)
 tomlkit==0.11.6             # pylint (>=0.10.1)
 typed-ast==1.5.4            # astroid (<2.0,>=1.4.0), black (>=1.4.2), mypy (<2,>=1.4.0)
-types-appdirs==1.4.3.1      # test-requirements.in
-types-docutils==0.19.1.3    # types-setuptools
-types-requests==2.28.11.12  # test-requirements.in
-types-setuptools==67.2.0.0  # test-requirements.in
-types-toml==0.10.8.3        # responses, test-requirements.in
-types-urllib3==1.26.25.5    # types-requests (<1.27)
-typing_extensions==4.4.0    # astroid (>=4.0.0), black (>=3.10.0.0), importlib-metadata (>=3.6.4), mypy (>=3.10), platformdirs (>=4.4), pylint (>=3.10.0), req-compile, responses
-urllib3==1.26.14            # requests (<1.27,>=1.21.1), responses (>=1.25.10)
-wheel==0.38.4               # req-compile
-wrapt==1.14.1               # astroid (<2,>=1.11)
-zipp==3.12.1                # importlib-metadata (>=0.5)
+types-appdirs==1.4.3.5      # test-requirements.in
+types-PyYAML==6.0.12.8      # responses
+types-requests==2.28.11.15  # test-requirements.in
+types-setuptools==67.6.0.5  # test-requirements.in
+types-toml==0.10.8.5        # test-requirements.in
+types-urllib3==1.26.25.8    # types-requests (<1.27)
+typing_extensions==4.5.0    # astroid (>=4.0.0), black (>=3.10.0.0), importlib-metadata (>=3.6.4), mypy (>=3.10), platformdirs (>=4.4), pylint (>=3.10.0), requirements.in, responses
+urllib3==1.26.15            # requests (<1.27,>=1.21.1), responses (>=1.25.10)
+wheel==0.40.0               # requirements.in
+wrapt==1.15.0               # astroid (<2,>=1.11)
+zipp==3.15.0                # importlib-metadata (>=0.5)
diff --git a/setup.py b/setup.py
index d4d451e..bbdb188 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 
 setup(
     name="req-compile",
-    version="1.0.0pre4",
+    version="1.0.0pre5",
     author="Spencer Putt",
     author_email="sputt@alumni.iu.edu",
     description="Python requirements compiler",


Test patch:
diff --git a/tests/test_filename.py b/tests/test_filename.py
new file mode 100644
index 0000000..6497094
--- /dev/null
+++ b/tests/test_filename.py
@@ -0,0 +1,36 @@
+import pkg_resources
+import pytest
+
+
+import req_compile.filename
+
+
+@pytest.mark.parametrize(
+    "filename,result_name,result_version",
+    [
+        ["post-2.3.1-2.tar.gz", "post", "2.3.1-2"],
+        ["pytest-ui-0.3b0.linux-x86_64.tar.gz", "pytest-ui", "0.3beta0"],
+        ["backports-thing-1.0.1.tar.gz", "backports-thing", "1.0.1"],
+        ["backports-thing-1.0.1.tar.gz", "backports-thing", "1.0.1"],
+        ["project-v1.0.tar.gz", "project", "1.0"],
+        ["pyvmomi-5.5.0.2014.1.1.tar.gz", "pyvmomi", "5.5.0.2014.1.1"],
+        ["pyvmomi-5.5.0-2014.1.1.tar.gz", "pyvmomi", None],
+        ["python-project-3-0.0.1.tar.gz", "python-project-3", "0.0.1"],
+        ["python-project-v2-0.1.1.tar.gz", "python-project-v2", "0.1.1"],
+        ["divisor-1.0.0s-1.0.0.zip", "divisor-1.0.0s", "1.0.0"],
+        [
+            "django-1.6-fine-uploader-0.2.0.3.tar.gz",
+            "django-1.6-fine-uploader",
+            "0.2.0.3",
+        ],
+        ["selenium-2.0-dev-9429.tar.gz", "selenium", "2.0-dev-9429"],
+        ["django-ajax-forms_0.3.1.tar.gz", "django-ajax-forms", "0.3.1"],
+        ["lskdjfknw.sdi.siqwenr", "lskdjfknw.sdi.siqwenr", None],
+    ],
+)
+def test_parse_source_filename(filename, result_name, result_version):
+    result = req_compile.filename.parse_source_filename(filename)
+    assert result == (
+        result_name,
+        pkg_resources.parse_version(result_version) if result_version else None,
+    )
diff --git a/tests/test_integration.py b/tests/test_integration.py
index bd3b007..841ebd7 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -39,7 +39,16 @@ def test_no_candidates(tmp_path):
 def test_compile_req_compile(tmp_path):
     """Test compiling this project from source."""
     result = subprocess.run(
-        [sys.executable, "-m", "req_compile", ".", "--wheel-dir", str(tmp_path)],
+        [
+            sys.executable,
+            "-m",
+            "req_compile",
+            "-i",
+            "https://pypi.org/simple",
+            ".",
+            "--wheel-dir",
+            str(tmp_path),
+        ],
         encoding="utf-8",
         capture_output=True,
         cwd=ROOT_DIR,
diff --git a/tests/test_metadata.py b/tests/test_metadata.py
index 5252c9d..3411b5a 100644
--- a/tests/test_metadata.py
+++ b/tests/test_metadata.py
@@ -85,33 +85,6 @@ def test_pylint_python(metadata_provider):
     assert set(info.requires()) == set(pkg_resources.parse_requirements(expected_reqs))
 
 
-@pytest.mark.parametrize(
-    "filename,result_name,result_version",
-    [
-        ["post-2.3.1-2.tar.gz", "post", "2.3.1-2"],
-        ["pytest-ui-0.3b0.linux-x86_64.tar.gz", "pytest-ui", "0.3beta0"],
-        ["backports-thing-1.0.1.tar.gz", "backports-thing", "1.0.1"],
-        ["backports-thing-1.0.1.tar.gz", "backports-thing", "1.0.1"],
-        ["project-v1.0.tar.gz", "project", "1.0"],
-        ["pyvmomi-5.5.0.2014.1.1.tar.gz", "pyvmomi", "5.5.0.2014.1.1"],
-        ["pyvmomi-5.5.0-2014.1.1.tar.gz", "pyvmomi", "5.5.0-2014.1.1"],
-        ["python-project-3-0.0.1.tar.gz", "python-project-3", "0.0.1"],
-        ["python-project-v2-0.1.1.tar.gz", "python-project-v2", "0.1.1"],
-        ["divisor-1.0.0s-1.0.0.zip", "divisor-1.0.0s", "1.0.0"],
-        [
-            "django-1.6-fine-uploader-0.2.0.3.tar.gz",
-            "django-1.6-fine-uploader",
-            "0.2.0.3",
-        ],
-        ["selenium-2.0-dev-9429.tar.gz", "selenium", "2.0-dev-9429"],
-        ["django-ajax-forms_0.3.1.tar.gz", "django-ajax-forms", "0.3.1"],
-    ],
-)
-def test_parse_source_filename(filename, result_name, result_version):
-    result = req_compile.filename.parse_source_filename(filename)
-    assert result == (result_name, pkg_resources.parse_version(result_version))
-
-
 def test_compound(mock_targz):
     """Test one tar after another directly that have failed in the passed"""
     archive = mock_targz("et_xmlfile-1.0.1")
diff --git a/tests/test_repositories.py b/tests/test_repositories.py
index e6700c5..fd5d30c 100644
--- a/tests/test_repositories.py
+++ b/tests/test_repositories.py
@@ -30,7 +30,11 @@ def test_version_compatible(mock_py_version, sys_py_version, py_requires):
 
 
 @pytest.mark.parametrize(
-    "sys_py_version, py_requires", [("3.6.3", ("py2",)), ("2.7.16", ("py3",)),],
+    "sys_py_version, py_requires",
+    [
+        ("3.6.3", ("py2",)),
+        ("2.7.16", ("py3",)),
+    ],
 )
 def test_version_incompatible(mock_py_version, sys_py_version, py_requires):
     mock_py_version(sys_py_version)
@@ -61,9 +65,6 @@ def test_sort_non_semver():
         "2013b0",
         "2012rc0",
         "2012b0",
-        "2009r",
-        "2013d",
-        "2011k",
     )
     candidates = []
     for ver in candidate_vers:
@@ -83,7 +84,8 @@ def test_sort_non_semver():
 def test_sort_specific_platforms(mock_py_version, mocker):
     mock_py_version("3.7.4")
     mocker.patch(
-        "req_compile.repos.repository.PLATFORM_TAGS", ("this_platform",),
+        "req_compile.repos.repository.PLATFORM_TAGS",
+        ("this_platform",),
     )
     candidate_wheels = (
         "sounddevice-0.4.1-cp32.cp33.cp34.cp35.cp36.cp37.cp38.cp39.pp32.pp33.pp34.pp35.pp36.pp37.py3-None-this_platform.whl",
@@ -102,7 +104,8 @@ def test_sort_specific_platforms(mock_py_version, mocker):
 def test_sort_wheels_with_any(mock_py_version, mocker):
     mock_py_version("3.7.4")
     mocker.patch(
-        "req_compile.repos.repository.PLATFORM_TAGS", ("this_platform",),
+        "req_compile.repos.repository.PLATFORM_TAGS",
+        ("this_platform",),
     )
     candidate_wheels = (
         "pyenchant-3.2.1-py3-None-this_platform.and_another.whl",