yggdrasil.enums.media_type¶
media_type ¶
Format + codec wrapper resolution.
Where :class:MimeType resolves a single MIME identifier (string,
extension, magic bytes), :class:MediaType resolves the combination
of an inner format and an outer wrapper codec — what you actually need
to dispatch reads/writes.
Two-stage sniffing¶
For bytes-shaped and file-like inputs the resolution is two-stage.
The outer magic identifies the wrapper (b"\x1f\x8b..." → GZIP).
When the wrapper is a codec we then peek a head-window of the
decompressed payload and sniff that — so a gzip-wrapped parquet
returns MediaType(PARQUET, codec=GZIP) rather than the lossy
MediaType(OCTET_STREAM, codec=GZIP) that comes from sniffing the
outer mime alone.
Two entry points:
- :meth:
MediaType.from_magic— bytes / bytearray / memoryview. No cursor concerns; sniffs in-memory. - :meth:
MediaType.from_io— file-like withread+seek. Seeks the source freely during sniffing (one read at position 0 for the outer head; if a codec is identified, another seek to 0 to drive a streaming decompressor for the inner head). The caller's cursor is captured on entry and restored on exit, so the IO is left exactly where it came in regardless of how much internal seeking happened.
Both entry points fall back to MediaType(OCTET_STREAM, codec=<wrapper>)
when the wrapper is identified but the inner sniff fails — losing
nothing the caller already had.
MediaType
dataclass
¶
from_many
classmethod
¶
Compose a MediaType from an ordered mime list (e.g. URL extensions).
Two conventions land here:
trades.csv.zst→["csv", "zst"]: the codec is the outer wrapper (you have to unzst before you can parse csv) →MediaType(CSV, codec=ZSTD).part-xxx.zstd.parquet→["zstd", "parquet"]: the format is the outer wrapper and the codec is the parquet page-codec hint baked into the file. Parquet handles the decompression internally →MediaType(PARQUET)with no outer codec; setting one would route the read through a decompressor that doesn't belong on this byte stream.
Order matters: the last mime decides. Last-is-codec promotes the codec to the wrapper slot; last-is-format keeps the format alone and drops earlier codec hints.
from_magic
classmethod
¶
from_magic(
buf: Union[bytes, bytearray, memoryview, IO[bytes]],
*,
default: "MediaType" = ...
) -> "MediaType"
Sniff a :class:MediaType from raw bytes.
Two-stage: outer magic identifies the wrapper; if the wrapper is a codec, decompress a head-window and sniff the inner format. See module docstring for the full story.
Accepts bytes / bytearray / memoryview directly. For
convenience, also accepts an IO[bytes] — in that case it
delegates to :meth:from_io, which manages cursor save/restore.
:param default: Fallback when the outer sniff finds nothing.
... (default) raises :class:ValueError; any other
value (including None) is returned as-is.
from_io
classmethod
¶
Sniff a :class:MediaType from a file-like, seeking freely.
Captures the cursor on entry and restores it on exit, so the caller's stream position is unaffected. Inside the call we seek freely:
seek(0)and read the first :data:_OUTER_PEEKbytes for outer-magic resolution.- If the outer mime is a codec,
seek(0)again and drive the codec's streaming decompressor directly to read the first :data:_INNER_PEEKbytes of decoded payload. - Sniff the decoded head for the inner mime.
The codec resolution is one dict lookup
(:meth:Codec.from_mime) — the outer magic loop already
landed on the registered :class:MimeType singleton, so we
reuse it as the key without re-resolving. For streaming
codecs (gzip / zstd / lz4 / bz2 / xz / zlib / lzma) the
decompressor is opened directly on the IO via
:meth:Codec._open_decompress_reader — no IO wrap, no
full-buffer materialization. Non-streaming codecs (snappy,
brotli) fall back to reading the compressed body and calling
:meth:Codec.decompress_bytes.
Decompression errors during the inner sniff are swallowed —
the worst case is returning a less-specific MediaType (the
outer codec is preserved). A separate caller that wants to
validate decompressibility should call :meth:Codec.decompress
directly.
:param io_obj: Any file-like with read + seek (stdlib
io.BytesIO, an open file, our own IO, etc.).
:param default: Fallback when the outer sniff finds nothing.
... (default) raises :class:ValueError; any other
value (including None) is returned as-is.