Repository: jazzband/pip-tools
Pull request #968 (2019-10-25 07:54:33)

Cleanup ephemeral wheel cache properly
<!--- Describe the changes here. --->

Attribute `self.wheel_cache` has been removed in f6de247b3f7a9e776325057a1e6cf6e48d34e7e7, thus `self.wheel_cache.cleanup()` will allways throw:

```pythin
AttributeError("'PyPIRepository' object has no attribute 'wheel_cache'")
```

and `wheel_cache.cleanup()` will never be called as supposed to be.

WheelCache.cleanup() has been introduced in `pip==10.0.0`. Try-except block has been refactored, for it's better to check the pip version explicitly (LBYL) rather than ask for forgiveness (EAFP).


**Changelog-friendly one-liner**: Fix dependency resolver to clean up the ephemeral wheel cache.

##### Contributor checklist

- [x] Provided the tests for the changes.
- [X] Gave a clear one-line description in the PR (that the maintainers can add to CHANGELOG.md on release).
- [x] Assign the PR to an existing or new milestone for the target version (following [Semantic Versioning](https://blog.versioneye.com/2014/01/16/semantic-versioning/)).

codecov[bot]: # [Codecov](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=h1) Report
> Merging [#968](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=desc) into [master](https://codecov.io/gh/jazzband/pip-tools/commit/2996077cb33710045bc84e62058eae8e42e2744c?src=pr&el=desc) will **decrease** coverage by `1.09%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/jazzband/pip-tools/pull/968/graphs/tree.svg?width=650&token=CELIrP9w8G&height=150&src=pr)](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##           master     #968     +/-   ##
=========================================
- Coverage   99.18%   98.08%   -1.1%     
=========================================
  Files          35       35             
  Lines        2340     2250     -90     
  Branches      301      301             
=========================================
- Hits         2321     2207    -114     
- Misses         11       33     +22     
- Partials        8       10      +2
```


| [Impacted Files](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [piptools/repositories/pypi.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-cGlwdG9vbHMvcmVwb3NpdG9yaWVzL3B5cGkucHk=) | `91.45% <100%> (-3.72%)` | :arrow_down: |
| [tests/test\_repositories.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9yZXBvc2l0b3JpZXMucHk=) | `34.61% <0%> (-65.39%)` | :arrow_down: |
| [piptools/repositories/local.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-cGlwdG9vbHMvcmVwb3NpdG9yaWVzL2xvY2FsLnB5) | `91.11% <0%> (-0.89%)` | :arrow_down: |
| [piptools/scripts/sync.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-cGlwdG9vbHMvc2NyaXB0cy9zeW5jLnB5) | `100% <0%> (ø)` | :arrow_up: |
| [piptools/resolver.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-cGlwdG9vbHMvcmVzb2x2ZXIucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [tests/test\_writer.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF93cml0ZXIucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [piptools/cache.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-cGlwdG9vbHMvY2FjaGUucHk=) | `100% <0%> (ø)` | :arrow_up: |
| [tests/test\_cli\_compile.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-dGVzdHMvdGVzdF9jbGlfY29tcGlsZS5weQ==) | `100% <0%> (ø)` | :arrow_up: |
| [tests/conftest.py](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree#diff-dGVzdHMvY29uZnRlc3QucHk=) | `100% <0%> (ø)` | :arrow_up: |
| ... and [12 more](https://codecov.io/gh/jazzband/pip-tools/pull/968/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=footer). Last update [2996077...fd8b573](https://codecov.io/gh/jazzband/pip-tools/pull/968?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/piptools/repositories/pypi.py b/piptools/repositories/pypi.py
index 50bcc87..b6b7b8a 100644
--- a/piptools/repositories/pypi.py
+++ b/piptools/repositories/pypi.py
@@ -274,10 +274,10 @@ class PyPIRepository(BaseRepository):
                         os.environ["PIP_REQ_TRACKER"] = prev_tracker
                     else:
                         del os.environ["PIP_REQ_TRACKER"]
-                try:
-                    self.wheel_cache.cleanup()
-                except AttributeError:
-                    pass
+
+                # WheelCache.cleanup() introduced in pip==10.0.0
+                if PIP_VERSION >= (10,):
+                    wheel_cache.cleanup()
         return self._dependencies_cache[ireq]
 
     def get_hashes(self, ireq):


Test patch:
diff --git a/tests/test_repository_pypi.py b/tests/test_repository_pypi.py
index 8eca3ec..c39b9a5 100644
--- a/tests/test_repository_pypi.py
+++ b/tests/test_repository_pypi.py
@@ -174,3 +174,19 @@ def test_pypirepo_calls_reqset_with_str_paths(pypi_repository, from_line):
         assert isinstance(called_with_finder, PackageFinder)
         assert called_with_ireq == ireq
         assert not pf_call_kwargs
+
+
+@pytest.mark.skipif(
+    PIP_VERSION < (10,), reason="WheelCache.cleanup() introduced in pip==10.0.0"
+)
+@mock.patch("piptools.repositories.pypi.PyPIRepository.resolve_reqs")  # to run offline
+@mock.patch("piptools.repositories.pypi.WheelCache")
+def test_wheel_cache_cleanup_called(
+    WheelCache, resolve_reqs, pypi_repository, from_line
+):
+    """
+    Test WheelCache.cleanup() called once after dependency resolution.
+    """
+    ireq = from_line("six==1.10.0")
+    pypi_repository.get_dependencies(ireq)
+    WheelCache.return_value.cleanup.assert_called_once_with()