Pip 2021-01-13

By Max Woerner Chase

Woohoo, I got a shoutout from Cracking the Cryptic.

Anyway, I'm looking over pip's internals again, and I'm going to need to take notes. This is kind of starting in the middle, but collector:LinkCollector.collect_links seems promising. I'll put a pin in that, and see if I can hit it the other way by passing a URL requirement to the new resolver. Or maybe I should be looking at package_finder:PackageFinder.find_all_candidates.

Okay, looking from the middle isn't working for me. Let's trace this from the start. First, let's see what kind of errors I can get by forcing incompatible requirements.

Okay, a requirement leads to an error from factory:Factory.make_requirement_from_install_req, which was called from resolver:Resolver.resolve. Now, a valid URL requirement is propagated by adding it to the requirements list. Constraints are added to the constraints dict, which is fed into the PipProvider. The lower-effort option at this level would be to somehow change the PipProvider behavior, but first I should look at the resolve method being used here... Okay, I've gotten to, provider:PipProvider.find_matches looks like it should be relevant, and its behavior depends on factory:Factory.find_candidates, which takes a constraint! We know that the constraint is activated if it gets this far, so what it needs to do is convert the link/url/whatever information from the constraint, into a Candidate.

Now, that part is just the implementation of the feature. The requirement that I need to satisfy, is that installation should fail if the candidate generated points to an incompatible wheel. So far, I see two basic ways to accomplish that. One is to avoid generating the candidate, and waiting for resolution to fail. The other is to error out immediately. I was going to say that erroring out made more sense because there's no way to recover, but then I realized that there is: look for a version that doesn't rely on the dependency! So, the correct answer has to be to avoid generating the candidate, and that gives us some test cases.

We want to make sure that wheel tags are properly matched, that an install attempt that requires an incompatible wheel will fail (as opposed to, say, nonterminating), and that, if a constraint points to an incompatible wheel, the selected versions do not depend on that package.

To implement this, I need a helper that converts the data that will be stored on constraints into a Candidate. It should check what the URL points to, and, if it's a wheel, whether it's compatible. My initial inclination is to implement this as a generator that takes the constraint argument and iterates over the field, producing valid candidates. (This would end up deferring obviously broken situations, but we're delegating to the resolver, because it can backtrack.)

I'm still not sure how to test this, but at least I have test cases. I also might need to adjust some of my ideas about what gets stored on the constraint, if it's getting converted into a Candidate, but we'll see.

Okay, that was a productive hour or two, time to wrap up.

Good night.