mirror of
https://github.com/sprockets/sprockets.mixins.mediatype.git
synced 2024-12-27 20:24:39 +00:00
Documentation updates for typing.
The changes to `api.rst` are particularly important since that is where I describe the externally available type annotations.
This commit is contained in:
parent
acc0a1db14
commit
4a0ca5e390
6 changed files with 157 additions and 79 deletions
64
docs/api.rst
64
docs/api.rst
|
@ -1,7 +1,27 @@
|
|||
API Documentation
|
||||
=================
|
||||
|
||||
.. currentmodule:: sprockets.mixins.mediatype.content
|
||||
|
||||
The easiest way to use this library is to:
|
||||
|
||||
#. call :func:`.install` when you create your application instance and specify a
|
||||
default content type
|
||||
#. call :func:`.add_transcoder` to install transcoders for the content types that
|
||||
you support -- use :func:`.add_binary_content_type` and/or
|
||||
:func:`.add_text_content_type` if you don't want to define a
|
||||
:class:`~sprockets.mixins.mediatype.type_info.Transcoder` class.
|
||||
#. include :class:`.ContentMixin` in your handler's inheritance chain
|
||||
#. call :meth:`~.ContentMixin.get_request_body` to retrieve a request body
|
||||
sent in any of the supported content types
|
||||
#. call :meth:`~.ContentMixin.send_response` to send a response in any of the
|
||||
supported content types
|
||||
|
||||
The :class:`.ContentMixin` will take care of inspecting the :http:header:`Content-Type`
|
||||
header and deserialize the request as well as implementing the
|
||||
:rfc:`proactive content negotiation algorithm <7231#section-3.4.1>` described in
|
||||
:rfc:`7231` to serialize a response object appropriately.
|
||||
|
||||
Content Type Handling
|
||||
---------------------
|
||||
.. autoclass:: ContentMixin
|
||||
|
@ -11,7 +31,7 @@ Content Type Registration
|
|||
-------------------------
|
||||
.. autofunction:: install
|
||||
|
||||
.. autofunction:: get_settings
|
||||
.. autofunction:: add_transcoder
|
||||
|
||||
.. autofunction:: set_default_content_type
|
||||
|
||||
|
@ -19,7 +39,7 @@ Content Type Registration
|
|||
|
||||
.. autofunction:: add_text_content_type
|
||||
|
||||
.. autofunction:: add_transcoder
|
||||
.. autofunction:: get_settings
|
||||
|
||||
.. autoclass:: ContentSettings
|
||||
:members:
|
||||
|
@ -33,3 +53,43 @@ Bundled Transcoders
|
|||
|
||||
.. autoclass:: MsgPackTranscoder
|
||||
:members:
|
||||
|
||||
.. _type-info:
|
||||
|
||||
Python Type Information
|
||||
-----------------------
|
||||
The ``sprockets.mixins.mediatype.type_info`` module contains a number of
|
||||
convenience type definitions for those you you who take advantage of type
|
||||
annotations.
|
||||
|
||||
.. currentmodule:: sprockets.mixins.mediatype.type_info
|
||||
|
||||
Interface Types
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: Transcoder
|
||||
:members:
|
||||
|
||||
.. autodata:: Serializable
|
||||
|
||||
.. autodata:: Deserialized
|
||||
|
||||
Convenience Types
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autodata:: PackBFunction
|
||||
|
||||
.. autodata:: UnpackBFunction
|
||||
|
||||
.. autodata:: DumpSFunction
|
||||
|
||||
.. autodata:: LoadSFunction
|
||||
|
||||
Contract Types
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. autoclass:: HasSettings
|
||||
:members:
|
||||
|
||||
.. autoclass:: DefinesIsoFormat
|
||||
:members:
|
||||
|
|
31
docs/conf.py
31
docs/conf.py
|
@ -3,10 +3,7 @@ import os
|
|||
import pkg_resources
|
||||
|
||||
needs_sphinx = '4.0'
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.extlinks', 'sphinxcontrib.httpdomain'
|
||||
]
|
||||
extensions = ['sphinx.ext.viewcode', 'sphinxcontrib.httpdomain']
|
||||
master_doc = 'index'
|
||||
project = 'sprockets.mixins.mediatype'
|
||||
copyright = '2015-2021, AWeber Communications'
|
||||
|
@ -24,14 +21,40 @@ html_sidebars = {
|
|||
'**': ['about.html', 'navigation.html'],
|
||||
}
|
||||
|
||||
# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html
|
||||
extensions.append('sphinx.ext.intersphinx')
|
||||
intersphinx_mapping = {
|
||||
'ietfparse': ('https://ietfparse.readthedocs.io/en/latest', None),
|
||||
'python': ('https://docs.python.org/3', None),
|
||||
'requests': ('https://requests.readthedocs.org/en/latest/', None),
|
||||
'sprockets': ('https://sprockets.readthedocs.org/en/latest/', None),
|
||||
'tornado': ('https://www.tornadoweb.org/en/stable/', None),
|
||||
}
|
||||
|
||||
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
|
||||
# We need to define type aliases for both the simple name (e.g., Deserialized)
|
||||
# and the prefixed name (e.g., type_info.Deserialized) since both forms
|
||||
# appear in the typing annotations.
|
||||
extensions.append('sphinx.ext.autodoc')
|
||||
autodoc_type_aliases = {
|
||||
alias: f'sprockets.mixins.mediatype.type_info.{alias}'
|
||||
for alias in {
|
||||
'DefinesIsoFormat', 'Deserialized', 'DumpSFunction', 'HasSettings',
|
||||
'LoadSFunction', 'MsgPackable', 'PackBFunction', 'Serializable',
|
||||
'Transcoder', 'UnpackBFunction'
|
||||
}
|
||||
}
|
||||
autodoc_type_aliases.update({
|
||||
f'type_info.{alias}': f'sprockets.mixins.mediatype.type_info.{alias}'
|
||||
for alias in {
|
||||
'DefinesIsoFormat', 'Deserialized', 'DumpSFunction', 'HasSettings',
|
||||
'LoadSFunction', 'MsgPackable', 'PackBFunction', 'Serializable',
|
||||
'Transcoder', 'UnpackBFunction'
|
||||
}
|
||||
})
|
||||
|
||||
# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
|
||||
extensions.append('sphinx.ext.extlinks')
|
||||
extlinks = {
|
||||
'compare': ('https://github.com/sprockets/sprockets.mixins.mediatype'
|
||||
'/compare/%s', '%s')
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
Version History
|
||||
===============
|
||||
|
||||
:compare:`Next <3.0.4...master>`
|
||||
--------------------------------
|
||||
- Add type annotations (see :ref:`type-info`)
|
||||
|
||||
:compare:`3.0.4 <3.0.3...3.0.4>` (2 Nov 2020)
|
||||
---------------------------------------------
|
||||
- Return a "400 Bad Request" when an invalid Content-Type header is received
|
||||
|
|
|
@ -61,13 +61,9 @@ class ContentSettings:
|
|||
|
||||
The settings instance contains the list of available content
|
||||
types and handlers associated with them. Each handler implements
|
||||
a simple interface:
|
||||
|
||||
- ``to_bytes(dict, encoding:str) -> bytes``
|
||||
- ``from_bytes(bytes, encoding:str) -> dict``
|
||||
|
||||
Use the :func:`add_binary_content_type` and :func:`add_text_content_type`
|
||||
helper functions to modify the settings for the application.
|
||||
the :class:`~sprockets.mixins.mediatype.type_info.Transcoder`
|
||||
interface. Use :func:`add_transcoder` to add support for a
|
||||
specific content type to the application.
|
||||
|
||||
This class acts as a mapping from content-type string to the
|
||||
appropriate handler instance. Add new content types and find
|
||||
|
@ -92,7 +88,7 @@ class ContentSettings:
|
|||
return app
|
||||
|
||||
Of course, that is quite tedious, so use the :class:`.ContentMixin`
|
||||
instead.
|
||||
instead of using the settings directly.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -178,12 +174,13 @@ def get_settings(
|
|||
"""
|
||||
Retrieve the media type settings for a application.
|
||||
|
||||
:param tornado.web.Application application:
|
||||
:keyword bool force_instance: if :data:`True` then create the
|
||||
:param application:
|
||||
:param force_instance: if :data:`True` then create the
|
||||
instance if it does not exist
|
||||
|
||||
:return: the content settings instance
|
||||
:rtype: sprockets.mixins.mediatype.content.ContentSettings
|
||||
:return: the content settings instance or :data:`None` if
|
||||
`force_instance` is not :data:`True` and :func:`.install`
|
||||
has not been called
|
||||
|
||||
"""
|
||||
try:
|
||||
|
@ -200,12 +197,12 @@ def add_binary_content_type(application: type_info.HasSettings,
|
|||
"""
|
||||
Add handler for a binary content type.
|
||||
|
||||
:param tornado.web.Application application: the application to modify
|
||||
:param str content_type: the content type to add
|
||||
:param application: the application to modify
|
||||
:param content_type: the content type to add
|
||||
:param pack: function that packs a dictionary to a byte string.
|
||||
``pack(dict) -> bytes``
|
||||
See :any:`type_info.PackBFunction`
|
||||
:param unpack: function that takes a byte string and returns a
|
||||
dictionary. ``unpack(bytes) -> dict``
|
||||
dictionary. See :any:`type_info.UnpackBFunction`
|
||||
|
||||
"""
|
||||
add_transcoder(application,
|
||||
|
@ -219,13 +216,13 @@ def add_text_content_type(application: type_info.HasSettings,
|
|||
"""
|
||||
Add handler for a text content type.
|
||||
|
||||
:param tornado.web.Application application: the application to modify
|
||||
:param str content_type: the content type to add
|
||||
:param str default_encoding: encoding to use when one is unspecified
|
||||
:param application: the application to modify
|
||||
:param content_type: the content type to add
|
||||
:param default_encoding: encoding to use when one is unspecified
|
||||
:param dumps: function that dumps a dictionary to a string.
|
||||
``dumps(dict, encoding:str) -> str``
|
||||
See :any:`type_info.DumpSFunction`
|
||||
:param loads: function that loads a dictionary from a string.
|
||||
``loads(str, encoding:str) -> dict``
|
||||
See :any:`type_info.LoadSFunction`
|
||||
|
||||
Note that the ``charset`` parameter is stripped from `content_type`
|
||||
if it is present.
|
||||
|
@ -246,32 +243,16 @@ def add_transcoder(application: type_info.HasSettings,
|
|||
"""
|
||||
Register a transcoder for a specific content type.
|
||||
|
||||
:param tornado.web.Application application: the application to modify
|
||||
:param transcoder: object that translates between :class:`bytes` and
|
||||
:class:`object` instances
|
||||
:param str content_type: the content type to add. If this is
|
||||
unspecified or :data:`None`, then the transcoder's ``content_type``
|
||||
attribute is used.
|
||||
:param application: the application to modify
|
||||
:param transcoder: object that translates between :class:`bytes`
|
||||
and object instances
|
||||
:param content_type: the content type to add. If this is
|
||||
unspecified or :data:`None`, then the transcoder's
|
||||
``content_type`` attribute is used.
|
||||
|
||||
The `transcoder` instance is required to implement the following
|
||||
simple protocol:
|
||||
|
||||
.. attribute:: transcoder.content_type
|
||||
|
||||
:class:`str` that identifies the MIME type that the transcoder
|
||||
implements.
|
||||
|
||||
.. method:: transcoder.to_bytes(inst_data, encoding=None) -> bytes
|
||||
|
||||
:param object inst_data: the object to encode
|
||||
:param str encoding: character encoding to apply or :data:`None`
|
||||
:returns: the encoded :class:`bytes` instance
|
||||
|
||||
.. method:: transcoder.from_bytes(data_bytes, encoding=None) -> object
|
||||
|
||||
:param bytes data_bytes: the :class:`bytes` instance to decode
|
||||
:param str encoding: character encoding to use or :data:`None`
|
||||
:returns: the decoded :class:`object` instance
|
||||
The `transcoder` instance is required to implement the
|
||||
:class:`~sprockets.mixins.mediatype.type_info.Transcoder`
|
||||
protocol.
|
||||
|
||||
"""
|
||||
settings = get_settings(application, force_instance=True)
|
||||
|
@ -284,9 +265,9 @@ def set_default_content_type(application: type_info.HasSettings,
|
|||
"""
|
||||
Store the default content type for an application.
|
||||
|
||||
:param tornado.web.Application application: the application to modify
|
||||
:param str content_type: the content type to default to
|
||||
:param str|None encoding: encoding to use when one is unspecified
|
||||
:param application: the application to modify
|
||||
:param content_type: the content type to default to
|
||||
:param encoding: encoding to use when one is unspecified
|
||||
|
||||
"""
|
||||
settings = get_settings(application, force_instance=True)
|
||||
|
@ -322,6 +303,17 @@ class ContentMixin(web.RequestHandler):
|
|||
|
||||
def get_response_content_type(self) -> typing.Union[str, None]:
|
||||
"""Select the content type will be used in the response.
|
||||
|
||||
This method implements proactive content negotiation as
|
||||
described in :rfc:`7231#section-3.4.1` using the
|
||||
:http:header:`Accept` request header or the configured
|
||||
default content type if the header is not present. The
|
||||
selected response type is cached and returned. It will
|
||||
be used when :meth:`.send_response` is called.
|
||||
|
||||
Note that this method is called by :meth:`.send_response`
|
||||
so you will seldom need to call it directly.
|
||||
|
||||
"""
|
||||
if self._best_response_match is None:
|
||||
settings = get_settings(self.application, force_instance=True)
|
||||
|
@ -346,7 +338,7 @@ class ContentMixin(web.RequestHandler):
|
|||
"""
|
||||
Fetch (and cache) the request body as a dictionary.
|
||||
|
||||
:raise web.HTTPError:
|
||||
:raise tornado.web.HTTPError:
|
||||
- if the content type cannot be matched, then the status code
|
||||
is set to 415 Unsupported Media Type.
|
||||
- if decoding the content body fails, then the status code is
|
||||
|
@ -390,10 +382,15 @@ class ContentMixin(web.RequestHandler):
|
|||
"""
|
||||
Serialize and send ``body`` in the response.
|
||||
|
||||
:param dict body: the body to serialize
|
||||
:param bool set_content_type: should the :http:header:`Content-Type`
|
||||
:param body: the body to serialize
|
||||
:param set_content_type: should the :http:header:`Content-Type`
|
||||
header be set? Defaults to :data:`True`
|
||||
|
||||
The transcoder for the response is selected by calling
|
||||
:meth:`.get_response_content_type` which chooses an
|
||||
appropriate transcoder based on the :http:header:`Accept`
|
||||
header from the request.
|
||||
|
||||
"""
|
||||
settings = get_settings(self.application, force_instance=True)
|
||||
# TODO -- account for get_response_type returning None
|
||||
|
|
|
@ -20,7 +20,7 @@ class BinaryContentHandler:
|
|||
"""
|
||||
Pack and unpack binary types.
|
||||
|
||||
:param str content_type: registered content type
|
||||
:param content_type: registered content type
|
||||
:param pack: function that transforms an object instance
|
||||
into :class:`bytes`
|
||||
:param unpack: function that transforms :class:`bytes`
|
||||
|
@ -43,8 +43,8 @@ class BinaryContentHandler:
|
|||
"""
|
||||
Transform an object into :class:`bytes`.
|
||||
|
||||
:param object inst_data: object to encode
|
||||
:param str encoding: ignored
|
||||
:param inst_data: object to encode
|
||||
:param encoding: ignored
|
||||
:returns: :class:`tuple` of the selected content
|
||||
type and the :class:`bytes` representation of
|
||||
`inst_data`
|
||||
|
@ -59,11 +59,8 @@ class BinaryContentHandler:
|
|||
"""
|
||||
Get an object from :class:`bytes`
|
||||
|
||||
:param bytes data_bytes: stream of bytes to decode
|
||||
:param str encoding: ignored
|
||||
:param dict content_parameters: optional :class:`dict` of
|
||||
content type parameters from the :mailheader:`Content-Type`
|
||||
header
|
||||
:param data_bytes: stream of bytes to decode
|
||||
:param encoding: ignored
|
||||
:returns: decoded :class:`object` instance
|
||||
|
||||
"""
|
||||
|
@ -74,12 +71,12 @@ class TextContentHandler:
|
|||
"""
|
||||
Transcodes between textual and object representations.
|
||||
|
||||
:param str content_type: registered content type
|
||||
:param content_type: registered content type
|
||||
:param dumps: function that transforms an object instance
|
||||
into a :class:`str`
|
||||
:param loads: function that transforms a :class:`str`
|
||||
into an object instance
|
||||
:param str default_encoding: encoding to apply when
|
||||
:param default_encoding: encoding to apply when
|
||||
transcoding from the underlying body :class:`byte`
|
||||
instance
|
||||
|
||||
|
@ -104,8 +101,8 @@ class TextContentHandler:
|
|||
"""
|
||||
Transform an object into :class:`bytes`.
|
||||
|
||||
:param object inst_data: object to encode
|
||||
:param str encoding: character set used to encode the bytes
|
||||
:param inst_data: object to encode
|
||||
:param encoding: character set used to encode the bytes
|
||||
returned from the ``dumps`` function. This defaults to
|
||||
:attr:`default_encoding`
|
||||
:returns: :class:`tuple` of the selected content
|
||||
|
@ -125,13 +122,10 @@ class TextContentHandler:
|
|||
"""
|
||||
Get an object from :class:`bytes`
|
||||
|
||||
:param bytes data: stream of bytes to decode
|
||||
:param str encoding: character set used to decode the incoming
|
||||
:param data: stream of bytes to decode
|
||||
:param encoding: character set used to decode the incoming
|
||||
bytes before calling the ``loads`` function. This defaults
|
||||
to :attr:`default_encoding`
|
||||
:param dict content_parameters: optional :class:`dict` of
|
||||
content type parameters from the :mailheader:`Content-Type`
|
||||
header
|
||||
:returns: decoded :class:`object` instance
|
||||
|
||||
"""
|
||||
|
|
|
@ -26,10 +26,10 @@ class JSONTranscoder(handlers.TextContentHandler):
|
|||
"""
|
||||
JSON transcoder instance.
|
||||
|
||||
:param str content_type: the content type that this encoder instance
|
||||
:param content_type: the content type that this encoder instance
|
||||
implements. If omitted, ``application/json`` is used. This is
|
||||
passed directly to the ``TextContentHandler`` initializer.
|
||||
:param str default_encoding: the encoding to use if none is specified.
|
||||
:param default_encoding: the encoding to use if none is specified.
|
||||
If omitted, this defaults to ``utf-8``. This is passed directly to
|
||||
the ``TextContentHandler`` initializer.
|
||||
|
||||
|
@ -77,7 +77,7 @@ class JSONTranscoder(handlers.TextContentHandler):
|
|||
"""
|
||||
Called to encode unrecognized object.
|
||||
|
||||
:param object obj: the object to encode
|
||||
:param obj: the object to encode
|
||||
:return: the encoded object
|
||||
:raises TypeError: when `obj` cannot be encoded
|
||||
|
||||
|
@ -114,7 +114,7 @@ class MsgPackTranscoder(handlers.BinaryContentHandler):
|
|||
"""
|
||||
Msgpack Transcoder instance.
|
||||
|
||||
:param str content_type: the content type that this encoder instance
|
||||
:param content_type: the content type that this encoder instance
|
||||
implements. If omitted, ``application/msgpack`` is used. This
|
||||
is passed directly to the ``BinaryContentHandler`` initializer.
|
||||
|
||||
|
|
Loading…
Reference in a new issue