PyPi - Object storage upload route for package files
Proposal
Implement workhorse routes to support PyPy package upload (ref gitlab#208746 (closed)).
Details
The upload format can be found on the PyPI Legacy API.
The upload api can be used to upload artifacts by sending a multipart/form-data POST request with the following fields:
:action set to file_upload
protocol_version set to 1
content with the file to be uploaded and the proper filename (i.e. my_foo_bar-4.2-cp36-cp36m-manylinux1_x86_64.whl)
md5_digest set to the md5 hash of the uploaded file in urlsafe base64 with no padding
filetype set to the type of the artifact, i.e. bdist_wheel or sdist
When used with bdist_wheel for filetype, pyversion must be set to a specific release, i.e. cp36, when used with sdist it must be set to source
metadata_version, name and version set according to the Core metadata specifications
You can set any other field from the Core metadata specifications. All fields need to be renamed to lowercase and hyphens need to replaced by underscores. So instead of “Description-Content-Type” the field must be named “description_content_type”. Note that adding a field “Description-Content-Type” will not raise an error but will be silently ignored.
Sample HTTP Request
POST https://test.pypi.org/legacy/ HTTP/1.1
Host: test.pypi.org
User-Agent: twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/45.2.0 requests-toolbelt/0.9.1 tqdm/4.32.1 CPython/3.7.3
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Type: multipart/form-data; boundary=a6675f38de9e4de18206848e0be2b61d
Content-Length: 4453
Authorization: Basic <token>
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="name"
example-pkg-giorgenes
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="version"
0.0.2
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="filetype"
bdist_wheel
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="pyversion"
py3
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="metadata_version"
2.1
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="summary"
A small example package
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="home_page"
https://github.com/pypa/sampleproject
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="author"
Giorgenes Gelatti
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="author_email"
giorgenes@gmail.com
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="maintainer"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="maintainer_email"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="license"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="description"
# Example Package
This is a simple example package. You can use
[Github-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
to write your content.
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="keywords"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="platform"
UNKNOWN
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="classifiers"
Programming Language :: Python :: 3
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="classifiers"
License :: OSI Approved :: MIT License
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="classifiers"
Operating System :: OS Independent
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="download_url"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="comment"
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="md5_digest"
8e8c557b19b28ab3e3c0c675fa2c052e
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="sha256_digest"
90f415caac7a9fb51acb782437abe5f624c3451e4207c98ecd9bbf0e9e1ed3bb
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="blake2_256_digest"
046e866068c664b31b1df26604ebfe309998976773e689c1a75b8c7bf9d5b034
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="requires_python"
>=3.6
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="description_content_type"
text/markdown
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name=":action"
file_upload
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="protocol_version"
1
--7916ac88c5aa41e2a853c4d6abc9fb04
Content-Disposition: form-data; name="content"; filename="example_pkg_giorgenes-0.0.2-py3-none-any.whl"
Content-Type: application/octet-stream
<binary data>
--7916ac88c5aa41e2a853c4d6abc9fb04--