Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add allow_all_prereleases to CandidateEvaluator's constructor #6514

Merged
merged 3 commits into from
May 22, 2019

Conversation

cjerdonek
Copy link
Member

@cjerdonek cjerdonek commented May 20, 2019

This PR is another follow-up to PR #6424 (the previous follow-up was PR #6511). It adds allow_all_prereleases to the list of arguments accepted by CandidateEvaluator's constructor, as suggested first in this comment to PR #6424: #6424 (comment) Doing this lets us eliminate another argument to PackageFinder's contructor, since it's now taken care of by the candidate_evaluator argument.

This PR also simplifies CandidateEvaluator by moving the creation of InstallationCandidate objects back to PackageFinder, which was made possible by the refactor in PR #6484.

In this way, CandidateEvaluator takes on more of the evaluating / filtering responsibility, while the state stays with PackageFinder.

@cjerdonek cjerdonek added C: finder PackageFinder and index related code skip news Does not need a NEWS file entry (eg: trivial changes) type: refactor Refactoring code labels May 20, 2019
@uranusjr
Copy link
Member

Is there a reason to introduce make_found_candidates()? It’s used only once and has minimal content; I wonder if it is more readable to inline it in PackageFinder.find_candidates().

@cjerdonek
Copy link
Member Author

I don't think "introduced" is the right word because the method already existed before as FoundCandidates.from_specifier(). Rather, the method is being moved / renamed to the CandidateEvaluator class, where the allow_prereleases attribute is located (needed for the method).

But yes, I think there are good reasons to put it there. For one, it hides complexity away from PackageFinder, which we are trying to simplify. For example, with this form, PackageFinder doesn't have to know about allow_prereleases (which is the responsibility of CandidateEvaluator). It also puts the responsibility of filtering with CandidateEvaluator, which is taking on the responsibility of evaluating and sorting candidates. Also, to me I think the lines in the method are actually complicated and hard to understand :) , so it's worth keeping that separated out to help my brain (and maybe others'). (E.g. it contains a nested set comprehension with a 7-line comment in the middle!)

Finally, it makes things nice and clean on the PackageFinder side, which is what we should be optimizing for, IMO:

def find_candidates(self, project_name, specifier=None):
    candidates = self.find_all_candidates(project_name)
    return self.candidate_evaluator.make_found_candidates(
        candidates, specifier=specifier,
    )

Would it help to give it a better, more descriptive name (e.g. filter_found_candidates())? That way it would be clearer why CandidateEvaluator has the responsibility.

@uranusjr
Copy link
Member

That makes a lot of sense, thanks for the explanation!

Reading the implementation again, I think what bothers me is not moving constructing function, but that FoundCandidates becomes tightly coupled with CandidateEvaluator. FoundCandidates is only supposed to be created by CandidateEvaluator, but the instance needs the creating evaluator to work. My gut feeling is the border of responsibilities between these two classes might be wrong, but I am not sure what the better approach is here.

Maybe it’d be possible to remove FoundCandidates. get_best(), so that FoundCandidates is only responsible for holding a collection of candidates, and CandidateEvaluator is responsible for determining whether each candidate is suitable for install?

@cjerdonek
Copy link
Member Author

Yes, I agree with you re: the relationship between those two classes. There is some circularity which isn't ideal. At the same time, FoundCandidates is such a tiny class (just a convenience "glue" class, really, encapsulating a result), and easy to change later, that it doesn't bother me too much.

Another possibility to break the circularity would be for CandidateEvaluator to inject not its whole self but just its get_best_candidate method object. Then FoundCandidates just sees an opaque function rather than the whole CandidateEvaluator object. But by doing that it just seems we'd be striving for something for the sake of purity.

Re: removing FoundCandidates.get_best(), one problem there is that it would then become awkward in pip_version_check() (the caller would have to know about the evaluator attribute), and I think we want to measure things by how simple they are for the caller (at the higher levels).

@uranusjr
Copy link
Member

I agree that passing an opaque callable is not very worthwhile. I think the interfacing problem of FoundCandidates.get_best() can be solved rather inconsequentially, since pip_version_check() is the only outside user of it right now (besides PackageFinder). But that does not really affect the main purpose of this PR (moving prerelease logic into the evaluator), and can wait after this is merged.

Copy link
Member

@uranusjr uranusjr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pondered a bit and now feel this is a good design. 💡

@cjerdonek
Copy link
Member Author

Okay, thanks much for taking the time to review and discuss.

@cjerdonek cjerdonek merged commit b554c15 into pypa:master May 22, 2019
@cjerdonek cjerdonek deleted the candidate-evaluator-pre-releases branch May 22, 2019 15:55
@lock lock bot added the auto-locked Outdated issues that have been locked by automation label Jun 21, 2019
@lock lock bot locked as resolved and limited conversation to collaborators Jun 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
auto-locked Outdated issues that have been locked by automation C: finder PackageFinder and index related code skip news Does not need a NEWS file entry (eg: trivial changes) type: refactor Refactoring code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants