Source code for ietfparse.datastructures
"""
Important data structures.
- :class:`.ContentType`: MIME ``Content-Type`` header.
- :class:`.LinkHeader`: parsed ``Link`` header.
This module contains data structures that were useful in
implementing this library. If a data structure might be
useful outside of a particular piece of functionality, it
is fully fleshed out and ends up here.
"""
from __future__ import annotations
import functools
from collections import abc
[docs]@functools.total_ordering
class ContentType(object):
"""A MIME ``Content-Type`` header.
:param content_type: the primary content type
:param content_subtype: the content sub-type
:param content_suffix: optional content suffix
:param parameters: optional dictionary of content type
parameters
Internet content types are described by the :mailheader:`Content-Type`
header from :rfc:`2045`. It was reused across many other protocol
specifications, most notably HTTP (:rfc:`7231`). This header's
syntax is described in :rfc:`2045#section-5.1`. In its most basic
form, a content type header looks like ``text/html``. The primary
content type is ``text`` with a *subtype* of ``html``. Content type
headers can include *parameters* as ``name=value`` pairs separated
by colons.
:rfc:`6839` added the ability to use a content type to identify the
semantic value of a representation with a content type and also identify
the document format as a content type suffix. For example,
``application/vnd.github.v3+json`` is used to identify documents that
match version 3 of the GitHub API that are represented as JSON documents.
The same entity encoded as msgpack would have the content type
``application/vnd.github.v3+msgpack``. In this case, the content type
identifies the information that is in the document and the suffix is used
to identify the content format.
"""
content_type: str
content_subtype: str
parameters: abc.MutableMapping[str, str]
content_suffix: str | None
quality: float | None
def __init__(self,
content_type: str,
content_subtype: str,
parameters: abc.Mapping[str, str | int] | None = None,
content_suffix: str | None = None) -> None:
self.content_type = content_type.strip().lower()
self.content_subtype = content_subtype.strip().lower()
self.quality = None
if content_suffix is not None:
self.content_suffix = content_suffix.strip().lower()
else:
self.content_suffix = None
self.parameters = {}
if parameters is not None:
for name in parameters:
self.parameters[name.lower()] = str(parameters[name])
def __str__(self) -> str:
suffix, params = '', ''
if self.content_suffix:
suffix = f'+{self.content_suffix}'
if self.parameters:
params = '; '.join(f'{name}={self.parameters[name]}'
for name in sorted(self.parameters))
params = f'; {params}'
return f'{self.content_type}/{self.content_subtype}{suffix}{params}'
def __repr__(self) -> str: # pragma: no cover
if self.content_suffix:
content_suffix = f'+{self.content_suffix}'
else:
content_suffix = ''
return '<{0}.{1} {2}/{3}{4}, {5} parameters>'.format(
self.__class__.__module__, self.__class__.__name__,
self.content_type, self.content_subtype, content_suffix,
len(self.parameters))
def __eq__(self, other: object) -> bool:
if not isinstance(other, ContentType):
return NotImplemented
return (self.content_type == other.content_type
and self.content_subtype == other.content_subtype
and self.content_suffix == other.content_suffix
and self.parameters == other.parameters)
def __lt__(self, other: object) -> bool:
if not isinstance(other, ContentType):
return NotImplemented
if self.content_type == '*' and other.content_type != '*':
return True
if self.content_subtype == '*' and other.content_subtype != '*':
return True
if len(self.parameters) < len(other.parameters):
return True
if self.content_type == other.content_type:
return self.content_subtype < other.content_subtype
return self.content_type < other.content_type