When running pip install foo==0.1.0
, and pip finds /local/path/to/foo-0.1.0-py3-none-any.whl
, why doesn’t pip by default go ahead and install it? At least in my case, it still seems to be using network traffic and time by going out and querying indices when it would seem like the default behavior might identify this as the BestCandidateResult
and short circuit checking elsewhere.
Some context: r/learnpython/comments/nkv159/why_does_pip_search_indexes_when_a_local_wheel_is/ (sorry, new user 2 link limit)
Tox was taking literally hours to run my testing suite on my speedy new M1 Mac, even though the project is laughably small and has minimal dependencies; in debugging, it looks like my local devpi
server is running extremely slowly for some reason, roughly 20 seconds per package.
Regardless, I was surprised that devpi mattered at all; I had wheel
ed my dependencies prior to running my test suite, and I could see with pip install -vvv
that it was finding these wheels. However, it was still querying the indices I’ve set up in my pip.conf
, and for some reason it was still downloading the wheels from my devpi server even though a local wheel was available (still trying to figure this out).
When a specific version is requested, isn’t finding a compatible wheel on the local filesystem about as good as it gets? I know I can set --no-index
and get this behavior, but it seems like having the default be that pip searches indices for a file that it has already found locally seems odd.
Would it be reasonable to refactor get_applicable_candidates
+ relevant logic into a generator (instead of the sorted
greediness) that looked through local filesystems first, and if a specific version is requested and a matching candidate is found, short-circuits the subsequent network requests?
Just imagining something like:
def get_candidates() -> t.Generator[candidate]:
...
for candidate in local_candidates:
yield candidate
for candidate in remote_candidates:
yield candidate
def find_best_candidate() -> candidate:
candidates = []
for candidate in get_candidates():
if specific_version_requested() and matches_requested_version(candidate) and is_local(candidate):
return candidate
candidates.append(candidate)
return max(candidates, key=self._sort_key)