Multipart¶
Zero-dependency multipart/form-data parser and encoder -- stdlib only, Python 3.10+.
Replaces:
python-multipart
Overview¶
The multipart module parses and encodes multipart/form-data bodies per RFC 7578 / RFC 2046. Designed for HTTP file upload handling without any third-party dependencies.
| File | Description | Dependencies |
|---|---|---|
multipart/multipart.py |
Multipart parser & encoder | None (stdlib only) |
Key Features¶
- Boundary-split parsing — fast byte-level splitting with robust boundary detection
- Content-Transfer-Encoding — supports
base64andquoted-printabledecoding - RFC 5987 filename* — decodes extended filename parameters (e.g.
filename*=UTF-8''...) - Security limits — configurable
max_part_size(10 MB),max_parts(1000),max_header_size(16 KB) - Frozen dataclass output — parsed parts are immutable
Partobjects with.textand.is_filehelpers - Encode support — build multipart bodies from fields and file tuples
- ~590 lines — single file, no dependencies
How to Use in Your Project¶
Usage Examples¶
Parse a Multipart Body¶
from multipart import parse_multipart
body = b"--boundary\r\nContent-Disposition: form-data; name=\"field1\"\r\n\r\nvalue1\r\n--boundary--\r\n"
parts = parse_multipart(body, "multipart/form-data; boundary=boundary")
for part in parts:
print(part.name, part.text) # "field1" "value1"
Encode Fields and Files¶
from multipart import encode_multipart
body, content_type = encode_multipart(
fields={"username": "alice", "bio": "Hello world"},
files={"avatar": ("photo.png", b"\x89PNG...", "image/png")},
)
# content_type: "multipart/form-data; boundary=..."
# body: ready to send as HTTP request body
File Upload Fields¶
from multipart import encode_multipart
# Files can be specified in several forms:
files = {
"doc": b"raw bytes", # auto filename, octet-stream
"report": ("report.pdf", pdf_bytes), # named file
"image": ("photo.jpg", jpg_bytes, "image/jpeg"), # named file with MIME type
}
body, content_type = encode_multipart(files=files)
Round-Trip¶
from multipart import encode_multipart, parse_multipart
# Encode
body, ct = encode_multipart(
fields={"name": "Alice"},
files={"doc": ("readme.txt", b"Hello!", "text/plain")},
)
# Parse back
parts = parse_multipart(body, ct)
for part in parts:
if part.is_file:
print(f"File: {part.filename}, size: {len(part.data)}")
else:
print(f"Field: {part.name} = {part.text}")
Security Limits¶
from multipart import parse_multipart
# Restrict to 1 MB per part and 10 parts max
parts = parse_multipart(body, content_type, max_part_size=1_000_000, max_parts=10)
# Disable limits (not recommended for untrusted input)
parts = parse_multipart(body, content_type, max_part_size=0, max_parts=0)
Extract Boundary¶
from multipart import extract_boundary
boundary = extract_boundary("multipart/form-data; boundary=abc123")
print(boundary) # "abc123"
# Also handles quoted boundaries
boundary = extract_boundary('multipart/form-data; boundary="my-boundary"')
print(boundary) # "my-boundary"
API Reference¶
parse_multipart(body, content_type, *, max_part_size=10MB, max_parts=1000)¶
| Parameter | Type | Description |
|---|---|---|
body |
bytes |
Raw request body |
content_type |
str |
Full Content-Type header or bare boundary string |
max_part_size |
int |
Max bytes per part (0 to disable) |
max_parts |
int |
Max number of parts (0 to disable) |
Returns list[Part].
encode_multipart(fields=None, files=None, *, boundary=None)¶
| Parameter | Type | Description |
|---|---|---|
fields |
dict \| list[tuple] |
Text form fields |
files |
dict \| list[tuple] |
File uploads (see formats above) |
boundary |
str \| None |
Custom boundary (auto-generated if None) |
Returns (body_bytes, content_type_header).
extract_boundary(content_type)¶
Extracts the boundary string from a Content-Type header. Returns the boundary as a string.
Part (frozen dataclass)¶
| Field | Type | Description |
|---|---|---|
name |
str |
Form field name |
data |
bytes |
Raw content bytes |
filename |
str \| None |
Original filename (None for text fields) |
content_type |
str |
MIME type (default: "text/plain") |
headers |
dict[str, str] |
All MIME headers (keys lowercased) |
Properties:
.text— decode data as text using charset from content_type (or UTF-8).is_file— True if the part has a filename
Exceptions¶
| Exception | Description |
|---|---|
MultipartError |
Base exception |
MultipartParseError |
Malformed input, missing boundary, limits exceeded |
MultipartEncodeError |
Invalid encode arguments |
Notes and Caveats¶
RFC Compliance
Implements RFC 7578 (multipart/form-data) and RFC 2046 (MIME multipart). Handles edge cases like preamble/epilogue, missing final boundary, and CRLF/LF normalization.
Content-Transfer-Encoding
Parts with Content-Transfer-Encoding: base64 or quoted-printable are automatically decoded. This is rare in HTTP but common in email MIME.
Memory
The parser loads the entire body into memory. For very large file uploads, consider a streaming parser instead.
- Python version: Requires Python 3.10+.
- Performance: 1.4–4x faster than python-multipart for parsing. See benchmarks below.
Benchmark¶
See Multipart Benchmark for performance measurements.