Skip to main content

preferhdr

Parse, validate and build the HTTP Prefer and Preference-Applied headers (RFC 7240), typed and zero-dependency.

Install

pip install preferhdr

Reading a request

parse takes the field value, or several values when the request carries several Prefer lines, and answers the questions a server asks about the registered preferences:

from preferhdr import parse

prefs = parse(["respond-async, wait=10", 'return=minimal; foo="some parameter"'])
print(prefs.respond_async, prefs.wait, prefs.return_, prefs.handling)
# True 10 minimal None
print(prefs.get("return"))
# return=minimal; foo="some parameter"
applied = prefs.get("return")
assert applied is not None  # get() gives None when the preference is absent
print("Wait" in prefs, applied.params["foo"])
# True some parameter

Names are case-insensitive, so they come back lower-cased; values are kept verbatim, and an empty value (foo="") is the same as no value. When a preference is repeated, only the first one counts, as RFC 7240 asks:

from preferhdr import parse

print(list(parse('Return=minimal, return=representation, foo=""')))
# [Preference(token='return', value='minimal', params={}), Preference(token='foo', value=None, params={})]

The default parser is tolerant, as a server must be: it accepts whitespace around = and empty list elements, reads CR, LF and NUL as spaces, and skips a malformed member (or a malformed parameter) while keeping the rest. It never raises PreferError:

from preferhdr import parse

print(list(parse("wait = 5, bad member, , handling=lenient; x=@")))
# [Preference(token='wait', value='5', params={}), Preference(token='handling', value='lenient', params={})]

Validating a value

strict=True checks the grammar a sender must follow (RFC 7240 with erratum 4439, RFC 9110 lists) and reports the first problem with its position:

from preferhdr import PreferError, parse

try:
    parse(["respond-async", "wait =10"], strict=True)
except PreferError as error:
    print(error.message, error.position, error.field)
# whitespace before '=' 4 1

Answering

Preferences.applied keeps the preferences a server honoured, without their parameters, ready for Preference-Applied. build writes any list of preferences, and vary adds Prefer to the Vary header, which RFC 7240 requires whenever a preference can change what a cache may reuse:

from preferhdr import Preference, build, parse, vary

prefs = parse("return=representation; x=1, respond-async")
print(build(prefs.applied("return"), applied=True))
# return=representation
print(build([Preference("return", "minimal"), Preference("note", "a, b")]))
# return=minimal, note="a, b"
print(vary("Accept-Encoding"))
# Accept-Encoding, Prefer

Every name and value is checked when the Preference is made, so the builder can never split a header:

from preferhdr import Preference, PreferError

try:
    Preference("return", "minimal\r\nSet-Cookie: a=1")
except PreferError as error:
    print(error)
# value of 'return' holds U+000D, which a header field cannot carry

Command line

preferhdr checks a value (or the Prefer lines of a request on stdin, as copied from a log) and prints one preference per line, exiting with 1 when the value breaks the grammar:

$ preferhdr 'Respond-Async, wait=10, return=minimal; foo="some parameter"'
respond-async
wait=10
return=minimal; foo="some parameter"
$ preferhdr 'wait = 10'
wait=10
invalid: whitespace before '=' at position 4 of field 0

--applied reads Preference-Applied instead, --json prints JSON.

API

Name What it does
parse(value, *, strict=False, applied=False) Field value or values to Preferences.
Preferences Immutable sequence: get, in, respond_async, return_, wait, handling, applied(*names).
Preference(token, value=None, params={}) One validated, hashable preference; str() writes it.
build(prefs, *, applied=False) Preferences to one field value.
vary(current=None) A Vary value that lists Prefer once.
PreferError ValueError with message, position and field.

Where the RFC leaves room

  • return=minimal, return=representation (and the same with handling): sections 4.2 and 4.4 say such a request may be treated as if neither was sent; section 2 says the first instance is the one considered. This library applies the first-wins rule, so return_ is "minimal".
  • Prefer: Lenient names a preference called lenient, not handling=lenient (erratum 4955).
  • A repeated parameter keeps its first value, like a repeated preference.
  • respond_async is True only for the bare preference; return_ and handling only for the values the RFC defines (values are case-sensitive).
  • wait must be ASCII digits; a value longer than 100 digits reads as no wait at all (it is an upper bound, and no bound means the same thing).
  • An unterminated quoted string runs to the end of its field value.
  • Values may hold obs-text as opaque data, U+00A0 to U+00FF. The C1 controls (U+0080 to U+009F) are rejected although RFC 9110 allows them: U+0085 is a line break to Python's str.splitlines, and they carry no text. Anything above U+00FF is rejected too.
  • Names are matched case-insensitively in ASCII only, as tokens are ASCII.
  • Strict mode accepts a repeated parameter (no rule forbids it) and keeps the first.
  • On stdin the command line reads the header section only (up to the first empty line) and joins folded lines with a space (RFC 9112 section 5.2).

Why

RFC 7240 is small but fiddly: optional values, quoted strings holding commas and semicolons, case-insensitive names, several header lines, first-wins duplicates, and a grammar fixed by errata. Hand-rolled splitting breaks on quoted values and lets CR LF through into responses. This library is one small, typed, zero-dependency codec for both directions, with a strict mode for tests and a tolerant one for servers.

License

MIT. See LICENSE.

Metadata

Release files for preferhdr 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for preferhdr 0.2.0
File Size Uploaded Provenance
preferhdr-0.2.0.tar.gz 27.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for preferhdr 0.2.0
File Interpreter ABI Platform
preferhdr-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 44.6 kB

Release files / preferhdr-0.2.0.tar.gz

Download URL preferhdr-0.2.0.tar.gz
Size 27.9 kB
Tags Source
SHA-256 checksum
How to use checksums
20235c094d3d958acbfc9ac753c15bf00b31b0b1f02f695375682f372d0f9810
BLAKE2b-256 checksum
How to use checksums
0bc3832befa79cee4c990fea8ecb23e36671abd996b7d073e1f3e35aff781a4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release files / preferhdr-0.2.0-py3-none-any.whl

Download URL preferhdr-0.2.0-py3-none-any.whl
Size 16.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
51b2f72d6c33a53149cd9d39f41e6b6e82ae410e74414a78a4747cab49519c29
BLAKE2b-256 checksum
How to use checksums
bef2e073eec47e92b7b699a6d1f39e05fd897d9f0b42ad4104b12fe84fa05136
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Security for preferhdr 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page