-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Better freeze of distributions installed from direct URL references #7612
Conversation
7a22e23
to
b8afe21
Compare
6480ecf
to
1be708f
Compare
ea038be
to
5f60f6f
Compare
@pypa/pip-committers I have a question following #7650 (comment). Should This PR currently emits PEP 440 style requirements. |
IMO it should use PEP 440 (so that requirements files are less tied to pip-specific details, and are more standards based). But that does mean that we need to make sure we support PEP 440 for all the URL styles we need - and #7650 suggests we have some tidying up to do there. |
Good. Converting pip URLs to PEP 440 works just fine, because URL parsing is done by the |
5c0402c
to
bdd6e58
Compare
def _get(d, expected_type, key, default=None): | ||
# type: (Dict[str, Any], Type[T], str, Optional[T]) -> Optional[T] | ||
"""Get value from dictionary and verify expected type.""" | ||
if key not in d: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try:
value = d[key]
except KeyError:
return default
is more Pythonic IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh. Is it? I've used both over time and I now try to avoid exceptions for flow control.
|
||
def _exactly_one_of(infos): | ||
# type: (Iterable[Optional[InfoType]]) -> InfoType | ||
infos = [info for info in infos if info is not None] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
infos = [info for info in infos if info is not None] | |
infos = list(filter(None, infos)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filter(None, iterable)
removes falsy entries, not just None. It’s probably not a problem here, but I prefer the current comprehension implementation for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also find list comprehension more readable, as its more explicit. With filter I have to think for a fraction of a second what None
means as a filter function.
def _exactly_one_of(infos): | ||
# type: (Iterable[Optional[InfoType]]) -> InfoType | ||
infos = [info for info in infos if info is not None] | ||
if len(infos) == 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if len(infos) == 0: | |
if not infos: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be too magical?
iterator = (info for info in infos if info is not None)
try:
info = next(iterator)
except StopIteration:
# missing one
if next(iterator, None):
# more than one
return info
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be too magical?
I like it, although I would use iterator = filter(None, infos)
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be too magical?
Yes it would be, IMO. That requires me quite more than a fraction of a second to understand what this does compared to the couple of if
s. Also I tend to avoid using exceptions for flow control purposes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid exceptions here you can always do next(iterator, None)
. But that doesn’t help with the magical aspect.
else None, | ||
vcs_info=self.info.to_dict() | ||
if isinstance(self.info, VcsInfo) | ||
else None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would we keep a name
class member on the individual types which holds the key name and just use that instead of these individual checks
requested_revision = None # type: Optional[str] | ||
commit_id = None # type: str | ||
resolved_revision = None # type: Optional[str] | ||
resolved_revision_type = None # type: Optional[str] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this and the other classes can we specify these in an __init__
so it's clearer that these are to be used as object members and not class members?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. TIL that mypy understands that.
@@ -598,6 +611,7 @@ def install_wheel( | |||
pycompile=True, # type: bool | |||
warn_script_location=True, # type: bool | |||
_temp_dir_for_testing=None, # type: Optional[str] | |||
direct_url=None, # type: DirectUrl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type should be Optional[DirectUrl]
- strict-optional is disabled in this file otherwise it would've been caught.
) | ||
with open(temp_direct_url_path, 'wb') as temp_direct_url_file: | ||
temp_direct_url_file.write(direct_url.to_json().encode("utf-8")) | ||
shutil.move(temp_direct_url_path, direct_url_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make an issue to use a safer write-then-move approach like we do here? If we don't fix it everywhere in this module before this PR gets merged then I'd prefer to keep this as-is so that they can all be fixed at once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Issue created: #7699
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The corresponding issue now has an approved PR (#7929); so I think it'd be nice if we could adopt that approach -- it'd probably need that PR to merge but I don't think there's any reason it can't be merged within this week. :)
tests/unit/test_wheel.py
Outdated
def test_std_install_with_direct_url(self, data, tmpdir): | ||
self.prep(data, tmpdir) | ||
direct_url = DirectUrl() | ||
direct_url.url = "file:///home/user/archive.tgz" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At first glance it seems kind of random that we're setting a different url than we would actually expect. I assume this is to ensure that install_wheel
isn't incorrectly using the wheel path argument? Can we put a comment to that effect?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment. direct_url.url
is typically the original requested link, while wheelpath
is the result of a local build.
src/pip/_internal/cache.py
Outdated
"""Returns a link to a cached item if it exists, otherwise returns the | ||
passed link. Also returns a flag indicating if the cached link is | ||
in the persistent cache (True), in the ephemeral cache (False), | ||
or not in cache (None). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would it look to return something enum-like instead?
bdd6e58
to
24d20cd
Compare
24d20cd
to
e9dfe30
Compare
Hello! I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the |
Rebased to resolve conflict following de populate_link move in #7801 Still ready for another review round. @xavfernandez maybe? @chrahunt it would be great if you could update your review, I think the changes your requested are done. |
Other than a minor comment (#7612 (comment)), I think this PR looks pretty good. I do think it'd be a good idea to merge this in ASAP, and iterate on this as we go; instead of trying to implement rest of the optional / nuanced functionality as part of this PR. |
Wait, no. Uhhhh. I didn't intend to actually do the merge. Hopefully "use both changes" was the correct thing here. Otherwise, apologies for the extra work @sbidoul. 🙈 |
Return whether the link was found in the persistent or ephemeral cache.
pass it to install_wheel via install
Thanks @xavfernandez for the review. |
Wheeee! I got to do the green button clicky stuff! ^.^ |
Many thanks and congratulations to @sbidoul! Thanks everyone who contributed to or reviewed this PR! ^>^ |
@sbidoul well done !!! |
Thanks for merging @pradyunsg. Big thank you to everyone who helped with this in one way or another! @bmartinn Thank you for the kind words. Note pip does not do RC releases, but I encourage you to test now by installing the master branch with |
Closes #609 via PEP 610.
In a nutshell, before it did this:
Now it does this:
DirectUrl
, a model for PEP 610DirectUrl
to PEP 440 requirement stringDirectUrl
from aDistribution
ifdirect_url.json
metadata is presentfreeze
test)DirectUrl
from aLink
direct_url.json
at installation of non-editable direct URL requirementsinstall_wheel
viainstall
direct_url.json
metadata