sfvparse
Zero-dependency RFC 8941 HTTP Structured Field Values parser and serializer for Python 3.11+.
Quick Start
pip install sfvparse
# OR (not yet on PyPI):
# pip install git+https://github.com/prasad-a-abhishek/sfvparse.git
from sfvparse import parse_item, parse_list, parse_dict, serialize_list
# Parse an Item
item = parse_item(b"text/plain;charset=utf-8")
# {"value": "text/plain", "params": {"charset": "utf-8"}}
# Parse a Link-header List
links = parse_list('<https://api.example.com/2>; rel="next", <https://api.example.com/1>; rel="prev"')
# [{"value": "https://api.example.com/2", "params": {"rel": "next"}}, ...]
# Parse a Dictionary
d = parse_dict(b"label=example;max-age=3600")
# {"label": {"value": "example", "params": {"max-age": 3600}}}
# Serialize back to bytes
out = serialize_list([{"value": "foo", "params": {"rel": "next"}}])
# b"foo; rel=next"
⚡ Performance & Benchmarks
sfvparse is benchmarked against the canonical reference Python implementation mnot/http_sfv across 50 iterations (10 workload profiles × 5 runs each). Reproduce locally:
python3 benchmarks/run_benchmark.py
Workload profiles (10 total — reproduce via python3 benchmarks/run_benchmark.py):
| Profile | Input size | sfvparse mean | http_sfv mean | Speedup | Notes |
|---|---|---|---|---|---|
| Single item (token) | 10 B | 6.78 µs | 6.59 µs | ~1.0× | Equal speed |
| Single item (param'd) | 32 B | 13.04 µs | 14.38 µs | 1.10× | sfvparse slightly faster |
| Single item (string+escape) | 16 B | 9.16 µs | 6.15 µs | 0.67× | http_sfv ~1.5× faster on strings |
| Short list (3 items) | 39 B | 24.77 µs | 15.22 µs | 0.61× | http_sfv ~1.6× faster on lists |
| Long list (50 items) | 1268 B | 2359.75 µs | 711.16 µs | 0.30× | http_sfv ~3.3× faster; sfvparse has O(n²) inner-list handling |
| Dict (5 keys, params) | 48 B | 42.06 µs | 48.30 µs | 1.15× | Roughly equal |
| Link header (realistic) | 80 B | 20.19 µs | ERR | — | http_sfv HttpHeader.parse throws on bare rel tokens in params |
| Accept-Language header | 44 B | 41.04 µs | 59.32 µs | 1.45× | sfvparse faster |
| Inner-list member | 12 B | 19.88 µs | 17.95 µs | 0.90× | Roughly equal |
| Deeply nested params | 70 B | 27.32 µs | 36.75 µs | 1.34× | sfvparse faster |
Honest summary: sfvparse and http_sfv are comparable on most workloads. sfvparse is ~1.1-1.5× faster on dict, accept-language, and deeply-nested params workloads. http_sfv is ~1.5-3× faster on string-heavy and list-heavy workloads; the long-list case is the most pronounced gap (sfvparse has O(n²) inner-list handling). Both use negligible memory (~1-2 KB peak). sfvparse wins on API ergonomics (single-function entry points vs class-then-parse) and on having a published PyPI package with type hints.
Why sfvparse?
Python developers building HTTP clients, REST API clients, or HTTP servers need standards-compliant RFC 8941 parsing — used in headers like Link, Sec-WebSocket-Extensions, Accept-Language, and Signature — without taking on a heavy dependency.
Competitor landscape:
| Package | PyPI | Zero-deps | RFC 8941 | Maintenance |
|---|---|---|---|---|
mnot/http_sfv (reference) |
No (GitHub only) | Yes | Yes | Reference (14★) |
http_sfv (PyPI) |
Yes | No (deps on http_sfv) | Partial | Stale |
linkheader |
Yes | Yes | RFC 5988 only | Unmaintained (Python 2 era) |
httplink |
Yes | Yes | RFC 8288 only | Niche |
sfvparse |
Yes | Yes | Full RFC 8941 | Active |
sfvparse trade-offs:
- ✅ Zero runtime dependencies —
pyproject.tomlhasdependencies = []. Only the Python standard library. - ✅ RFC 8941 compliant — full Item, List, Dictionary, and bare-item parsing (sf-token, sf-string, sf-integer, sf-decimal, sf-boolean, sf-binary) with parameter lists.
- ✅ RFC 8288 Link-header extension —
<URI>member syntax recognized automatically. - ✅ Inner-list extension —
( member member ; param )syntax. - ✅ Type-hinted TypedDict API —
Item,ListMember,DictMemberfor static type-checkers. - ✅ Works on Python 3.11+ with full PEP 604 union types and structural pattern matching.
- ⚠️ Byte-sequence asymmetry — the parser returns raw base64 (between the
::markers) per spec AC6; the serializer expects decoded bytes. Callers that want to round-trip byte sequences shouldbase64.b64decode(...)between parse and serialize. This is intentional and documented. - ⚠️ Spec deviations from strict RFC 8941 ABNF — see "Limitations / non-goals" below.
Key Features & Complete API Reference
Public functions
| Function | Returns | Description |
|---|---|---|
parse_item(data) |
Item |
Parse a single Item (bare item + parameters) |
parse_list(data) |
list[ListMember] |
Parse a List of Items / dictionary-style members |
parse_dict(data) |
dict[str, DictMember] |
Parse a Dictionary |
parse_token(data) |
str |
Parse a single sf-token |
parse_string(data) |
str |
Parse a single sf-string |
parse_integer(data) |
int |
Parse a single sf-integer |
parse_decimal(data) |
float |
Parse a single sf-decimal |
parse_boolean(data) |
bool |
Parse a single sf-boolean (?0 or ?1) |
parse_byte_sequence(data) |
bytes |
Parse a single sf-binary (returns raw base64 inside : : per spec) |
serialize_item(member) |
bytes |
Serialize one Item |
serialize_list(members) |
bytes |
Serialize a list of Items |
serialize_dict(d) |
bytes |
Serialize a dictionary |
All parse_* functions accept bytes or str input; serialize_* functions return bytes.
TypedDict types
from sfvparse import Item, ListMember, DictMember, Value
# Value = Union[str, int, float, bool, bytes, list]
item: Item = {"value": "text/plain", "params": {"charset": "utf-8"}}
member: ListMember = {"value": "https://example.com", "params": {"rel": "next"}}
dm: DictMember = {"key": "foo", "value": "bar", "params": {"max-age": 3600}}
CLI usage
sfvparse is a library-first package (no CLI of its own). Use it programmatically:
python3 -c "from sfvparse import parse_item; import sys; print(parse_item(sys.stdin.buffer.read()))"
Error handling
All parse_* functions raise ValueError on malformed input and TypeError on wrong input types. This is the only contract — no ParseError class hierarchy.
Test count
pytest collection reports 282 passing tests as of this release (covering all 25 spec acceptance criteria plus edge cases, round-trip checks, and TypeHint integration). Run python3 -m pytest tests/ --collect-only -q to see the full list.
Limitations / Non-Goals
- No RFC 9651 extensions —
sfvparseimplements the RFC 8941 subset only. Items, Lists, Dictionaries, and the six bare-item types. Nostructured-fieldsextensions from RFC 9651 are parsed. - No HTTP client / server — this is a parsing/serialization library only.
- No binary content-transfer encodings (e.g., quoted-printable, base64 transport).
- No caching-aware content negotiation (RFC 2295).
- Spec deviations from strict RFC 8941:
- Integer digit count is unbounded (Python ints are arbitrary-precision).
- Decimal fractional-digit count is unbounded (Python floats can hold the full IEEE 754 range).
- Parameter and dictionary keys are lowercased before storage (RFC 8941 says they are case-insensitive; we normalize for consistency).
parse_tokengreedily scans until end-of-input, including any internal whitespace. Strict RFC 8941 expectsparse_tokento be called on a slice bounded by other syntax.- Bare-item tokens ending in 2+ consecutive punctuation tchar (e.g.
text/plain!!!) are split — the punctuation run is treated as trailing junk thatparse_itemthen rejects.
License
MIT License — Copyright (c) 2026 sfvparse contributors. See LICENSE for full text.
Built by the Hermes repo-factory. See benchmarks/BENCHMARK.md for full benchmark methodology and raw numbers.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sfvparse-0.1.0.tar.gz.
File metadata
- Download URL: sfvparse-0.1.0.tar.gz
- Upload date:
- Size: 26.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59c0a1604be387d30136f0b98a874218c84088a85d6cc7e1a0ecadfb4cecdc98
|
|
| MD5 |
1550a9f01ba84317935d07af9e1aa721
|
|
| BLAKE2b-256 |
5a4c5cb426c102c48eeeda1413666401cf637a9dbd4c54d7094591db84a9f819
|
File details
Details for the file sfvparse-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sfvparse-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43f104bd1d9e5147191a9fcec5cc892f0585bb192e2b478f9615edc1d70a2352
|
|
| MD5 |
0b4ed1a714b0aac19f6a4837917e0003
|
|
| BLAKE2b-256 |
8ec66e71bc8d3daed965b1a2fba22d3623d54a04ef65b5b7b949ea45eea753f7
|