Python Multipart

raw JSON →
0.0.22 verified Tue May 12 auth: no python install: verified quickstart: stale

python-multipart is an Apache2-licensed streaming multipart parser for Python. It is designed for handling `multipart/form-data` POST requests, typically used in web servers for file uploads and complex form data. The library is actively maintained with frequent releases, currently at version 0.0.22, and supports modern Python versions (>=3.10).

pip install python-multipart
error ImportError: 'python-multipart' must be installed to handle file uploads
cause This error occurs when a web framework (like FastAPI or Starlette) attempts to process `multipart/form-data` requests but the `python-multipart` library, which it depends on for this functionality, is not installed.
fix
pip install python-multipart
error ModuleNotFoundError: No module named 'python_multipart'
cause The user is attempting to import the library using its PyPI distribution name (`python-multipart` converted to `python_multipart`), but the actual Python package name for import is `multipart`.
fix
import multipart
error ValueError: No boundary found
cause This error indicates that the `multipart/form-data` parser could not find the expected boundary string in the request body, often due to a missing or incorrect `Content-Type` header or malformed multipart data.
fix
Ensure the Content-Type header is set correctly (e.g., multipart/form-data; boundary=your_boundary_string) and matches the boundary string present in the request body.
error RuntimeWarning: coroutine 'MultipartReader.read_part' was never awaited
cause This warning appears when an asynchronous method, such as `MultipartReader.read_part()`, is called but its returned coroutine object is not awaited, meaning the asynchronous operation is never actually executed.
fix
When working with the asynchronous multipart.reader.MultipartReader, ensure that all calls to its async methods (e.g., read_part, next_part) are prefixed with await within an async function.
breaking The import name was changed from `multipart` to `python_multipart` in version 0.0.13. Direct imports using `import multipart` will no longer work and may lead to `ModuleNotFoundError` or unexpected behavior if another `multipart` package is installed. Versions 0.0.13 and 0.0.14 were temporarily yanked due to breakage related to this change.
fix Update all import statements from `import multipart` or `from multipart import ...` to `from python_multipart import ...`.
breaking Support for Python 3.8 and 3.9 was dropped in version 0.0.21. The library now requires Python 3.10 or newer.
fix Upgrade your Python environment to 3.10 or a newer supported version (e.g., 3.11, 3.12, 3.13, 3.14).
gotcha A separate, unrelated package on PyPI is also named `multipart`. Installing `pip install multipart` instead of `pip install python-multipart` will install the wrong library, leading to `ModuleNotFoundError` if `from python_multipart import ...` is used, or unexpected behavior if `import multipart` is still in your code.
fix Always use `pip install python-multipart` to ensure the correct library is installed.
gotcha In version 0.0.22, the `File` object produced by the parser will no longer include directory paths in its `filename` attribute. It will only contain the base filename.
fix If your application relied on `File.filename` containing a path, adjust your logic to expect only the base name. If paths are critical, you may need to implement custom logic to extract them from the `Content-Disposition` header directly (if present and needed).
gotcha Version 0.0.18 introduced a 'hard break' if data is found after the last boundary in `MultipartParser`. This means the parser will now strictly enforce the end of the multipart body, potentially raising errors for malformed requests that previously might have been partially processed.
fix Ensure that clients sending multipart data adhere strictly to the `multipart/form-data` specification, particularly regarding the final boundary and any subsequent data.
deprecated In version 0.0.15, `FutureWarning` messages were replaced with `PendingDeprecationWarning`. While not a breaking change, it indicates that certain behaviors or features are slated for deprecation or removal in future major releases.
fix Monitor the project's changelog and documentation for details on upcoming deprecations. Address any `PendingDeprecationWarning` messages in your code to prepare for future breaking changes.
gotcha The `TypeError: can't concat dict to bytes` when initializing `MultipartParser` indicates that a dictionary of HTTP headers (or any dict) was passed as the `content_type` argument. The `MultipartParser` constructor expects the raw value of the `Content-Type` header (a string or bytes) from which it can extract the boundary, or directly the boundary string/bytes, not the entire headers dictionary.
fix Instead of passing a dictionary of headers, extract the `Content-Type` header value (e.g., `headers.get('Content-Type')`) and pass that string or bytes object to `MultipartParser`. For example, `parser = MultipartParser(headers.get('Content-Type').encode('latin-1'))` if your headers are strings, or `parser = MultipartParser(headers.get(b'Content-Type'))` if your headers are bytes (assuming keys are bytes).
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.06s 18.0M
3.10 alpine (musl) - - 0.07s 18.0M
3.10 slim (glibc) wheel 1.5s 0.04s 18M
3.10 slim (glibc) - - 0.04s 18M
3.11 alpine (musl) wheel - 0.11s 19.8M
3.11 alpine (musl) - - 0.12s 19.8M
3.11 slim (glibc) wheel 1.6s 0.10s 20M
3.11 slim (glibc) - - 0.09s 20M
3.12 alpine (musl) wheel - 0.08s 11.7M
3.12 alpine (musl) - - 0.09s 11.7M
3.12 slim (glibc) wheel 1.4s 0.09s 12M
3.12 slim (glibc) - - 0.14s 12M
3.13 alpine (musl) wheel - 0.07s 11.5M
3.13 alpine (musl) - - 0.07s 11.3M
3.13 slim (glibc) wheel 1.4s 0.07s 12M
3.13 slim (glibc) - - 0.07s 12M
3.9 alpine (musl) wheel - 0.06s 17.5M
3.9 alpine (musl) - - 0.06s 17.5M
3.9 slim (glibc) wheel 1.7s 0.05s 18M
3.9 slim (glibc) - - 0.06s 18M

This quickstart demonstrates how to parse a `multipart/form-data` request body using `MultipartParser`. It simulates an incoming HTTP request with form fields and a file, processing them using event-like iteration.

import io
from python_multipart import MultipartParser

# Simulate an HTTP request body with multipart/form-data
boundary = b"----WebKitFormBoundary7MA4YWxkTrZu0gW"
body_data = (
    b"--" + boundary + b"\r\n"
    b'Content-Disposition: form-data; name="username"\r\n'
    b'\r\n'
    b'testuser\r\n'
    b"--" + boundary + b"\r\n"
    b'Content-Disposition: form-data; name="upload_file"; filename="hello.txt"\r\n'
    b'Content-Type: text/plain\r\n'
    b'\r\n'
    b'Hello, World!\nThis is a test file.\r\n'
    b"--" + boundary + b"--\r\n"
)

# Simulate HTTP headers
headers = {
    "Content-Type": f"multipart/form-data; boundary={boundary.decode()}",
    "Content-Length": str(len(body_data)),
}

# Use BytesIO to simulate a file-like object for the body stream
body_stream = io.BytesIO(body_data)

# Create a parser instance
parser = MultipartParser(headers)

# Iterate through parts and process them
print("Parsing multipart data:")
for part in parser.parse(body_stream):
    if hasattr(part, 'field_name'): # It's a form field
        print(f"  Field: name={part.field_name.decode()}, value={part.value.decode()}")
    elif hasattr(part, 'file_name'): # It's an uploaded file
        print(f"  File: name={part.field_name.decode()}, filename={part.file_name.decode()}, content_type={part.content_type.decode()}")
        file_content = part.value # The file content is available as bytes
        print(f"  File content length: {len(file_content)} bytes")
        # In a real application, you would typically save or process file_content

print("\nParsing complete.")