-
Notifications
You must be signed in to change notification settings - Fork 963
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
API to get dependencies without full download #474
Comments
This is going to be possible once PEP426 is in place. |
Off topic: How does PEP426 get developed? It is soon three years old. What can I do to get it implemented? |
|
It looks like https://github.com/python/peps/blob/master/pep-0426.txt is the current PEP 426 draft, right? |
Is there anything currently available and up-to-date that's better than downloading all the metadata as json via the pypi-data app, and doing
I note some issues with pypi-data at nathforge/pypi-data#2 |
Is there any update on this? Wondering if it is now possible to get the list of a package's dependencies without a full download of the package. |
It is not in the general case, because of limitations in the packaging formats. |
From quickly analyzing the package metadata using the JSON API, it looks like out of ~120k packages in the PyPi index, only ~17k have a non null I saw that PEP 426 has a deferred status and was wondering if there were some open issues that aimed to improve somewhat the situation with the |
Thanks for bringing up and discussing this issue, and sorry for the slow response! (Context, for those who don't already know it: Warehouse needs to get to the point where we can redirect pypi.python.org to pypi.org so the site is more sustainable and reliable. Towards that end, the folks working on Warehouse have gotten limited funding to concentrate on improving and deploying it, and have kicked off work towards our development roadmap. Along the way we've been able to reply to some of the older issues in the repo as well.) Since this feature isn't something that the legacy site has, and we're prioritizing replacing the old site, I've moved it to a future milestone. @ncoghlan am I right that @rth should be looking at pypa/packaging-problems#102 and pypa/packaging-problems#54? Thanks and sorry again for the wait. |
Thanks for the detailed response @brainwane and for linking to those issues! I know that there are higher priority issues with the migration to Warehouse (and thank you for working on that!), I just commented for future reference while experimenting with the PyPi JSON API... |
Glad to help, @rth. For reference: PEP 426 has been withdrawn. As you're experimenting with the JSON API, check out the other API/feeds issues in case any of them have questions you can answer! And if you have questions, please feel free to ask them here, on |
Note that while PEP 426 (metadata 2.0) has been withdrawn, PEP 566 (metadata 2.1) has been accepted, and that includes a canonical conversion from the baseline key:value representation to a JSON compatible representation: https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata This means that at least for projects that upload |
That's really good news. Thank you for the explanations! |
What determines the value of "requires_dist" given in the json api response? When I look at one of my uploads it's there, e.g. https://pypi.org/pypi/oyaml/0.2/json which correctly says |
Metadata extraction currently only happens for the first uploaded artifact, and unlike wheel archives, sdists aren't required to contain metadata in a format that an index server knows how to read. Allowing subsequent wheel uploads to supplement the metadata extracted from an sdist would be a nice Warehouse enhancement (but is separate from this issue). |
After checking with @dstufft in relation to pypa/packaging.python.org#450, it seems recent versions of So the most likely cause of incomplete metadata now is the use of older upload clients (and older releases will be missing this data as well, since it needs to be generated client side and then delivered to PyPI as part of the release publication process). |
@ncoghlan It would be a different story if the "requires_dist" key was not returned at all, which would be PyPI saying "I don't have this information". But my issue is that it's actually returning incorrect data, i.e. the "requires_dist" key is there, and it has a value (empty array):
Users don't seem to have a way to tell the difference between a package which genuinely has no 3rd-party requirements, and one with incorrectly parsed requirements, apart from downloading the distribution. I think perhaps you should backfill these on all existing distributions, or at least all existing distributions which have a bdist present in index, so it no longer returns incorrect data. That should be easy enough to script and run as a once-off. Thoughts? |
@wimglenn I think that's 3 separate questions:
[1] For example, see https://github.com/pypa/twine/blob/master/twine/repository.py#L122 for upload, https://github.com/pypa/twine/blob/master/twine/package.py#L83 for extraction |
|
The pip maintainers would like this because it would really help with the resolver improvements and automated testing improvements they're making over the next few months. |
PyPI's JSON API does not come from a PEP, so we're either stuck trying to add this to the existing simple API, standardizing PyPI's JSON API, or defining an entirely new replacement to the simple API. Personally I'd lean towards the last option there, but if we're going to do that, then we probably want to spend some time figuring out exactly what problems with the simple API we're trying to solve are. |
@pradyunsg @uranusjr @pfmoore As we work to roll out and test the new resolver pypa/pip#6536, or think about future versions, how much would you benefit from even a prototype or minimal version of this feature? |
It would help, but it would need to be an extension to the standard for the simple API to be of significant benefit. We definitely don't want to end up special-casing PyPI/Warehouse in pip's code, and while we could add a test for whether an index supports a new API, I'd want that to be standardised (at least provisionally) or we're going to hit all sorts of backward compatibility and maintenance issues down the line. Also, this would only be of minimal benefit unless it exposed metadata for sdists, which is a hard problem. If it only handled wheels, the only benefit would be reduced download volumes for wheels that ended up not being used. And pip's caches probably make that cost relatively small. Personally, unless it was a standardised feature that provided sdist metadata1 I feel that the benefits would be marginal enough that I'd expect us to defer any work to use it until after the release of the new resolver, as "post-release performance tuning" work. 1 Or better still, metadata based on project and version alone, but that's not realistically achievable in the timescales we're looking at. |
#8254 is a proposal, that would address this as well. |
Assuming PEP 643 gets approved, we will have reliable metadata available for wheels and (increasing numbers of) sdists. Extracting that metadata and exposing it via PyPI becomes an even more attractive prospect at that point. Pip could likely work with either the JSON API or an extension to the simple API, but either one would need standardising first. |
PEP 643 got approved! What's not clear to me is how this issue interacts with #8254. |
With PEP 643 and PEP 658 (assuming the latter is accepted as-is), the procedure would be
|
Minor clarification: If it's a sdist, and either |
I'm going to close this, PEP 658 is deployed on PyPI now (though there is a bug with it, but that will be fixed soon) and that's our current path for fetching any artifact's metadata without downloading the entire artifact. |
@dstufft This is a nice enhancement for wheels uploaded, but it's falling a bit short for an API - is it really that programs/tools should parse the simple html and read those href attrs? Index still seems to show Even for simple, only the wheel got the attr - sdists don't seem to though the wording in PEP (second para of the rationale) suggests that standards-compliant sdists are still in scope. Or is it that the sdist was not standards compliant somehow? It was setuptools build, which put a Thanks |
The simple API supports JSON since PEP 691, using something like: import email.message
import requests
def parse_content_type(header: str) -> str:
m = email.message.Message()
m["content-type"] = header
return m.get_content_type()
# Construct our list of acceptable content types, we want to prefer
# that we get a v1 response serialized using JSON, however we also
# can support a v1 response serialized using HTML. For compatibility
# we also request text/html, but we prefer it least of all since we
# don't know if it's actually a Simple API response, or just some
# random HTML page that we've gotten due to a misconfiguration.
CONTENT_TYPES = [
"application/vnd.pypi.simple.v1+json",
"application/vnd.pypi.simple.v1+html;q=0.2",
"text/html;q=0.01", # For legacy compatibility
]
ACCEPT = ", ".join(CONTENT_TYPES)
# Actually make our request to the API, requesting all of the content
# types that we find acceptable, and letting the server select one of
# them out of the list.
resp = requests.get("https://pypi.org/simple/", headers={"Accept": ACCEPT})
# If the server does not support any of the content types you requested,
# AND it has chosen to return a HTTP 406 error instead of a default
# response then this will raise an exception for the 406 error.
resp.raise_for_status()
# Determine what kind of response we've gotten to ensure that it is one
# that we can support, and if it is, dispatch to a function that will
# understand how to interpret that particular version+serialization. If
# we don't understand the content type we've gotten, then we'll raise
# an exception.
content_type = parse_content_type(resp.headers.get("content-type", ""))
match content_type:
case "application/vnd.pypi.simple.v1+json":
handle_v1_json(resp)
case "application/vnd.pypi.simple.v1+html" | "text/html":
handle_v1_html(resp)
case _:
raise Exception(f"Unknown content type: {content_type}") If you don't want to support HTML repositories (if for instance, you only talk to PyPI or are OK not supporting repositories that haven't implemented PEP 691) you can simplify that down to: import email.message
import requests
def parse_content_type(header: str) -> str:
m = email.message.Message()
m["content-type"] = header
return m.get_content_type()
# Construct our list of acceptable content types, we only accept a
# a v1 response serialized using JSON.
CONTENT_TYPES = [
"application/vnd.pypi.simple.v1+json",
]
ACCEPT = ", ".join(CONTENT_TYPES)
# Actually make our request to the API, requesting all of the content
# types that we find acceptable, and letting the server select one of
# them out of the list.
resp = requests.get("https://pypi.org/simple/", headers={"Accept": ACCEPT})
# If the server does not support any of the content types you requested,
# AND it has chosen to return a HTTP 406 error instead of a default
# response then this will raise an exception for the 406 error.
resp.raise_for_status()
# Determine what kind of response we've gotten to ensure that it is one
# that we can support, and if it is, dispatch to a function that will
# understand how to interpret that particular version+serialization. If
# we don't understand the content type we've gotten, then we'll raise
# an exception.
content_type = parse_content_type(resp.headers.get("content-type", ""))
match content_type:
case "application/vnd.pypi.simple.v1+json":
handle_v1_json(resp)
case _:
raise Exception(f"Unknown content type: {content_type}") Support for sdists is blocked on PEP 643 support: #9660 |
Thanks, I've just tried accepting Ack on the PEP 643 for sdists ("dynamic" field details etc) |
Yes, that's the way that PEP 658 exposed that information. There's a trade off here between making more requests, and making the singular request larger. Putting the metadata in Of course the flip side then is that if all you want is one piece of metadata out of the entire It is a little crummy to have to parse the content as an email message rather than JSON, but that's the format that our It's possible that a future PEP will turn |
Here the issue to the discussion on python-distutils: http://code.activestate.com/lists/python-distutils-sig/25409/
To get a dependency resolver for python, there needs to be a way to get the dependencies of a package. To avoid useless network traffic the dependencies of a packages ("install_requires" in setup.py) need to be accessible via an API.
The text was updated successfully, but these errors were encountered: