Repository: adamchainz/pip-lock

Issue:
pip-lock does detect dependencies with setup extras
There are requirement mismatches with requirements.txt
    * Package django-storages[boto3] is in requirements.txt but not in virtualenv

Discussion:

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 9f9cc5b..568f9d1 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -12,6 +12,7 @@ History
   ``importlib.metadata.version("pip-lock")``
   (`docs <https://docs.python.org/3.8/library/importlib.metadata.html#distribution-versions>`__ /
   `backport <https://pypi.org/project/importlib-metadata/>`__).
+* Fix parsing of package names featuring extras e.g. ``package[extra1,extra2]``.
 
 2.0.0 (2019-02-28)
 ------------------
diff --git a/src/pip_lock.py b/src/pip_lock.py
index 9d93bfa..3c1e7a0 100644
--- a/src/pip_lock.py
+++ b/src/pip_lock.py
@@ -40,8 +40,11 @@ def get_package_versions(lines, ignore_external=False):
         if ignore_external and line.startswith("-e"):
             continue
 
-        name, version_plus = line.split("==", 1)
-        versions[name.lower()] = version_plus.split(" ", 1)[0]
+        full_name, version_and_extras = line.split("==", 1)
+        # Strip extras
+        name = full_name.split("[", 1)[0].lower()
+        version = version_and_extras.split(" ", 1)[0]
+        versions[name] = version
 
     return versions
 


Test patch:
diff --git a/tests/test_pip_lock.py b/tests/test_pip_lock.py
index da3f02a..c4a3881 100644
--- a/tests/test_pip_lock.py
+++ b/tests/test_pip_lock.py
@@ -17,7 +17,7 @@ def create_file(tmpdir, name, text):
     return str(t)
 
 
-class TestReadPip(object):
+class TestReadPip:
     def test_read(self, tmpdir):
         path = create_file(tmpdir, "requirements.txt", "package1==1.0\npackage2==1.1")
         assert read_pip(path) == ["package1==1.0", "package2==1.1"]
@@ -33,7 +33,7 @@ class TestReadPip(object):
         assert read_pip(path) == [""]
 
 
-class TestGetPackageVersion(object):
+class TestGetPackageVersion:
     def test_version(self):
         assert get_package_versions(["package1==1.0", "package2==1.1"]) == {
             "package1": "1.0",
@@ -53,7 +53,7 @@ class TestGetPackageVersion(object):
         assert get_package_versions(["https://www.google.com"]) == {}
 
 
-class TestGetMismatches(object):
+class TestGetMismatches:
     def setUp(self, tmpdir):
         super(TestGetMismatches, self).setUp()
         self.requirements_path = create_file(
@@ -115,8 +115,17 @@ class TestGetMismatches(object):
 
         assert get_mismatches(requirements_path) == {}
 
+    @mock.patch("pip_lock.pip_freeze")
+    def test_package_with_extra(self, pip_freeze, tmpdir):
+        pip_freeze.return_value = ["package==1.1"]
+        requirements_path = create_file(
+            tmpdir, "requirements.txt", "package[anextra]==1.1"
+        )
+
+        assert get_mismatches(requirements_path) == {}
+
 
-class TestPrintErrors(object):
+class TestPrintErrors:
     def test_errors(self, capsys):
         print_errors(["error message 1", "error message 2"])
         _, err = capsys.readouterr()
@@ -131,7 +140,7 @@ class TestPrintErrors(object):
         assert "post text" in err
 
 
-class TestCheckRequirements(object):
+class TestCheckRequirements:
     @mock.patch("pip_lock.get_mismatches")
     def test_no_mismatches(self, get_mismatches):
         get_mismatches.return_value = {}