Source code for betty.media_type
"""
Provide `media type <https://en.wikipedia.org/wiki/Media_type>`_ handling utilities.
"""
from __future__ import annotations
from email.message import EmailMessage
from typing import Any, final, TYPE_CHECKING
from typing_extensions import override
from betty.json.schema import String
if TYPE_CHECKING:
from collections.abc import Sequence, Mapping
[docs]
class InvalidMediaType(ValueError):
"""
Raised when an identifier is not a valid media type.
"""
pass # pragma: no cover
[docs]
@final
class MediaType:
"""
Define a `media type <https://en.wikipedia.org/wiki/Media_type>`_.
Media types are also commonly known as content types or MIME types.
"""
_suffix: str | None
[docs]
def __init__(self, media_type: str):
self._str = media_type
message = EmailMessage()
message["Content-Type"] = media_type
type_part = message.get_content_type()
# EmailMessage.get_content_type() always returns a type, and will fall back to alternatives if the header is
# invalid.
if not media_type.startswith(type_part):
raise InvalidMediaType(f'"{media_type}" is not a valid media type.')
self._parameters: Mapping[str, str] = dict(message["Content-Type"].params)
self._type, type_part_remainder = type_part.split("/")
if not type_part_remainder:
raise InvalidMediaType("The subtype must not be empty.")
plus_position = type_part_remainder.find("+")
if plus_position > 0:
self._subtype = type_part_remainder[0:plus_position]
self._suffix = type_part_remainder[plus_position:]
else:
self._subtype = type_part_remainder
self._suffix = None
def __hash__(self) -> int:
return hash(self._str)
@property
def type(self) -> str:
"""
The type, e.g. ``application`` for ``application/ld+json``.
"""
return self._type
@property
def subtype(self) -> str:
"""
The subtype, e.g. ``"vnd.oasis.opendocument.text"`` for ``"application/vnd.oasis.opendocument.text"``.
"""
return self._subtype
@property
def subtypes(self) -> Sequence[str]:
"""
The subtype parts, e.g. ``["vnd", "oasis", "opendocument", "text"]`` for ``"application/vnd.oasis.opendocument.text"``.
"""
return self._subtype.split("+")[0].split(".")
@property
def suffix(self) -> str | None:
"""
The suffix, e.g. ``json`` for ``application/ld+json``.
"""
return self._suffix
@property
def parameters(self) -> Mapping[str, str]:
"""
The parameters, e.g. ``{"charset": "UTF-8"}`` for ``"text/html; charset=UTF-8"``.
"""
return self._parameters
@override
def __str__(self) -> str:
return self._str
@override
def __eq__(self, other: Any) -> bool:
if not isinstance(other, MediaType):
return NotImplemented
return (self.type, self.subtype, self.suffix, self.parameters) == (
other.type,
other.subtype,
self.suffix,
other.parameters,
)