Skip to content

Upload

This model represents a package upload to a pypi compatible server.

Code

from typing import List, Optional
from pydantic import BaseModel, Field

class PackageUpload(BaseModel):
    metadata_version: str
    name: str
    version: str
    filename: str
    content: bytes
    pyversion: str
    filetype: str
    sha256_digest: str
    md5_digest: str
    blake2_256_digest: str
    action: str = Field(..., alias=":action")
    protocol_version: str
    requires_dist: List[str] = None
    provides_extra: List[str] = None
    description_content_type: Optional[str] = None
    requires_python: Optional[str] = None
    summary: Optional[str] = None
    maintainer_email: Optional[str] = None
    license: Optional[str] = None
    project_urls: Optional[List[str]] = None
    author_email: Optional[str] = None
    keywords: Optional[List[str]] = None
    classifiers: Optional[List[str]] = None

Example

The following Flask example will call callback(...) with any uploaded package:

from flask import Flask, request
from werkzeug.utils import secure_filename

app = Flask(__name__)

def callback(package: PackageUpload):
    print(package.name, package.version)
    return "OK", 200

@app.route("/legacy", methods=["POST"])
def upload():
    form_data = {
        "metadata_version": request.form.get("metadata_version"),
        "name": request.form.get("name"),
        "summary": request.form.get("summary"),
        "license": request.form.get("license"),
        "version": request.form.get("version"),
        "description_content_type": request.form.get("description_content_type"),
        "requires_python": request.form.get("requires_python"),
        "pyversion": request.form.get("pyversion"),
        "author_email": request.form.get("author_email"),
        "maintainer_email": request.form.get("maintainer_email"),
        "filetype": request.form.get("filetype"),
        "sha256_digest": request.form.get("sha256_digest"),
        "md5_digest": request.form.get("md5_digest"),
        "blake2_256_digest": request.form.get("blake2_256_digest"),
        ":action": request.form.get(":action"),
        "protocol_version": request.form.get("protocol_version"),
        "project_urls": request.form.getlist("project_urls"),
        "requires_dist": request.form.getlist("requires_dist"),
        "provides_extra": request.form.getlist("provides_extra"),
        "classifiers": request.form.getlist("classifiers"),
        "keywords": request.form.getlist("keywords"),
    }

    if uploaded_file := request.files.get("content"):
        form_data["filename"] = secure_filename(uploaded_file.filename)
        form_data["content"] = uploaded_file.read()
        try:
            package_upload = PackageUpload(**form_data)
        except Exception as e:
            return str(e), 400
        return callback(package_upload)
    else:
        return "No file uploaded", 400

The following twine command should work and the server will print out name and version of all the packages

twine upload --repository-url http://localhost:5000/legacy dist/*