Repository: sbidoul/pip-deepfreeze (language: python)

Issue:
Pin pip, setuptools, wheel and distribute when they are dependencies
When `pip`, `setuptools`, `wheel` and `distribute` , they are never present in locked requirements.
This is because pips omits them by default in `pip freeze` output.

We change to call `pip freeze --all` but never propose to uninstall these as they may be necessary to build legacy projects without build isolation.


Discussion / hints:

Fix patch:
diff --git a/news/123.features b/news/123.features
new file mode 100644
index 0000000..6647e7b
--- /dev/null
+++ b/news/123.features
@@ -0,0 +1,3 @@
+Pin pip, setuptools, wheel and distribute when they are dependencies of the project.
+Before they were not pinned in the ``requirements*.txt`` lock file because they were not
+reported by pip freeze. Now we pin them but never propose to uninstall them.
diff --git a/src/pip_deepfreeze/pip.py b/src/pip_deepfreeze/pip.py
index d6f4856..b140060 100644
--- a/src/pip_deepfreeze/pip.py
+++ b/src/pip_deepfreeze/pip.py
@@ -182,7 +182,7 @@ def pip_list(python: str) -> InstalledDistributions:
 
 def pip_freeze(python: str) -> Iterable[str]:
     """Run pip freeze."""
-    cmd = [python, "-m", "pip", "freeze"]
+    cmd = [python, "-m", "pip", "freeze", "--all"]
     return check_output(cmd).splitlines()
 
 
diff --git a/src/pip_deepfreeze/sync.py b/src/pip_deepfreeze/sync.py
index cc95fcf..11f2fb3 100644
--- a/src/pip_deepfreeze/sync.py
+++ b/src/pip_deepfreeze/sync.py
@@ -94,8 +94,11 @@ def sync(
             for req_line in frozen_reqs:
                 print(req_line, file=f)
     # uninstall unneeded dependencies, if asked to do so
-    if unneeded_reqs:
-        unneeded_req_names = get_req_names(unneeded_reqs)
+    unneeded_req_names = sorted(
+        set(str(s) for s in get_req_names(unneeded_reqs))
+        - set(["pip", "setuptools", "wheel", "distribute"])
+    )
+    if unneeded_req_names:
         unneeded_reqs_str = ",".join(unneeded_req_names)
         prompted = False
         if uninstall_unneeded is None:


Test patch:
diff --git a/tests/test_pip.py b/tests/test_pip.py
index 85496a4..ed4e95a 100644
--- a/tests/test_pip.py
+++ b/tests/test_pip.py
@@ -38,7 +38,7 @@ def test_pip_freeze(to_install, expected, virtualenv_python, testpkgs):
             *to_install,
         ]
     )
-    assert list(pip_freeze(virtualenv_python)) == expected
+    assert list(_freeze_filter(pip_freeze(virtualenv_python))) == expected
 
 
 @pytest.mark.parametrize(
@@ -81,7 +81,10 @@ def test_pip_freeze_dependencies(
             *other_installs,
         ]
     )
-    assert list(pip_freeze_dependencies(virtualenv_python, tmp_path)) == expected
+    res = pip_freeze_dependencies(virtualenv_python, tmp_path)
+    unneeded_reqs = res[-1]
+    unneeded_reqs[:] = list(_freeze_filter(unneeded_reqs))
+    assert list(res) == expected
 
 
 @pytest.mark.parametrize(
@@ -157,12 +160,10 @@ def test_pip_freeze_dependencies_by_extra(
             *other_installs,
         ]
     )
-    assert (
-        list(
-            pip_freeze_dependencies_by_extra(virtualenv_python, tmp_path, freeze_extras)
-        )
-        == expected
-    )
+    res = pip_freeze_dependencies_by_extra(virtualenv_python, tmp_path, freeze_extras)
+    unneeded_reqs = res[-1]
+    unneeded_reqs[:] = list(_freeze_filter(unneeded_reqs))
+    assert list(res) == expected
 
 
 @pytest.mark.parametrize(
@@ -188,15 +189,14 @@ def test_pip_uninstall(to_install, to_uninstall, expected, virtualenv_python, te
             ]
         )
     pip_uninstall(virtualenv_python, to_uninstall)
-    assert list(pip_freeze(virtualenv_python)) == expected
+    assert list(_freeze_filter(pip_freeze(virtualenv_python))) == expected
 
 
 def _freeze_filter(reqs: Iterable[str]) -> Iterator[str]:
     """Filter out comments and -e."""
+    filters = ("#", "-e ", "pip==", "setuptools==", "wheel==", "distribute==")
     for req in reqs:
-        if req.startswith("#"):
-            continue
-        if req.startswith("-e"):
+        if any(req.startswith(f) for f in filters):
             continue
         yield req