mirror of
https://github.com/sprockets/sprockets.mixins.mediatype.git
synced 2024-12-29 11:17:10 +00:00
Repackage sprockets.mixins.mediatype into a package.
Distributing a raw python module into a namespace package seems to be somewhat unreliable though I haven't proven it yet... In any case, installing a package inside of a namespace package behaves itself.
This commit is contained in:
parent
ee26168e25
commit
ce54332cab
5 changed files with 179 additions and 42 deletions
|
@ -1,6 +1,11 @@
|
|||
Version History
|
||||
===============
|
||||
|
||||
`Next Release`_
|
||||
---------------
|
||||
- Repackage from a module into a package. Distributing raw modules inside
|
||||
of a namespace package is unreliable and questionably correct.
|
||||
|
||||
`1.0.4`_ (14 Sep 2015)
|
||||
----------------------
|
||||
- Support using the default_content_type in the settings if request does not
|
||||
|
@ -22,6 +27,7 @@ Version History
|
|||
---------------------
|
||||
- Initial Release
|
||||
|
||||
.. _Next Release: https://github.com/sprockets/sprockets.http/compare/1.0.4...HEAD
|
||||
.. _1.0.4: https://github.com/sprockets/sprockets.http/compare/1.0.3...1.0.4
|
||||
.. _1.0.3: https://github.com/sprockets/sprockets.http/compare/1.0.2...1.0.3
|
||||
.. _1.0.2: https://github.com/sprockets/sprockets.http/compare/1.0.1...1.0.2
|
||||
|
|
6
setup.py
6
setup.py
|
@ -4,6 +4,8 @@
|
|||
import os
|
||||
import setuptools
|
||||
|
||||
from sprockets.mixins import mediatype
|
||||
|
||||
|
||||
def read_requirements(file_name):
|
||||
requirements = []
|
||||
|
@ -28,7 +30,7 @@ tests_require = read_requirements('testing.txt')
|
|||
|
||||
setuptools.setup(
|
||||
name='sprockets.mixins.mediatype',
|
||||
version='1.0.4',
|
||||
version=mediatype.__version__,
|
||||
description='A mixin for reporting handling content-type/accept headers',
|
||||
long_description='\n' + open('README.rst').read(),
|
||||
url='https://github.com/sprockets/sprockets.mixins.media_type',
|
||||
|
@ -36,7 +38,7 @@ setuptools.setup(
|
|||
author_email='api@aweber.com',
|
||||
license='BSD',
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Natural Language :: English',
|
||||
|
|
24
sprockets/mixins/mediatype/__init__.py
Normal file
24
sprockets/mixins/mediatype/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
"""
|
||||
sprockets.mixins.media_type
|
||||
|
||||
"""
|
||||
try:
|
||||
from .content import (ContentMixin, ContentSettings,
|
||||
add_binary_content_type, add_text_content_type,
|
||||
set_default_content_type)
|
||||
|
||||
except ImportError as error: # pragma no cover
|
||||
def _error_closure(*args, **kwargs):
|
||||
raise error
|
||||
|
||||
ContentMixin = _error_closure
|
||||
ContentSettings = _error_closure
|
||||
add_binary_content_type = _error_closure
|
||||
add_text_content_type = _error_closure
|
||||
set_default_content_type = _error_closure
|
||||
|
||||
version_info = (1, 0, 4)
|
||||
__version__ = '.'.join(str(v) for v in version_info)
|
||||
__all__ = ('ContentMixin', 'ContentSettings', 'add_binary_content_type',
|
||||
'add_text_content_type', 'set_default_content_type',
|
||||
'version_info', '__version__')
|
|
@ -1,16 +1,33 @@
|
|||
"""
|
||||
sprockets.mixins.media_type
|
||||
===========================
|
||||
Content handling for Tornado.
|
||||
|
||||
- :func:`.set_default_content_type` sets the content type that is
|
||||
used when an ``Accept`` or ``Content-Type`` header is omitted.
|
||||
- :func:`.add_binary_content_type` register transcoders for a binary
|
||||
content type
|
||||
- :func:`.add_text_content_type` register transcoders for a textual
|
||||
content type
|
||||
- :class:`.ContentSettings` an instance of this is attached to
|
||||
:class:`tornado.web.Application` to hold the content mapping
|
||||
information for the application
|
||||
- :class:`.ContentMixin` attaches a :class:`.ContentSettings`
|
||||
instance to the application and implements request decoding &
|
||||
response encoding methods
|
||||
|
||||
This module is the primary interface for this library. It exposes
|
||||
functions for registering new content handlers and a mix-in that
|
||||
adds content handling methods to :class:`~tornado.web.RequestHandler`
|
||||
instances.
|
||||
|
||||
"""
|
||||
import logging
|
||||
|
||||
from ietfparse import algorithms, errors, headers
|
||||
from tornado import escape, web
|
||||
from tornado import web
|
||||
|
||||
from . import handlers
|
||||
|
||||
|
||||
version_info = (1, 0, 4)
|
||||
__version__ = '.'.join(str(v) for v in version_info)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -110,7 +127,8 @@ def add_binary_content_type(application, content_type, pack, unpack):
|
|||
|
||||
"""
|
||||
settings = ContentSettings.from_application(application)
|
||||
settings[content_type] = _BinaryContentHandler(content_type, pack, unpack)
|
||||
settings[content_type] = handlers.BinaryContentHandler(
|
||||
content_type, pack, unpack)
|
||||
|
||||
|
||||
def add_text_content_type(application, content_type, default_encoding,
|
||||
|
@ -128,8 +146,8 @@ def add_text_content_type(application, content_type, default_encoding,
|
|||
|
||||
"""
|
||||
settings = ContentSettings.from_application(application)
|
||||
settings[content_type] = _TextContentHandler(content_type, dumps, loads,
|
||||
default_encoding)
|
||||
settings[content_type] = handlers.TextContentHandler(
|
||||
content_type, dumps, loads, default_encoding)
|
||||
|
||||
|
||||
def set_default_content_type(application, content_type, encoding=None):
|
||||
|
@ -232,35 +250,3 @@ class ContentMixin(object):
|
|||
if set_content_type:
|
||||
self.set_header('Content-Type', content_type)
|
||||
self.write(data_bytes)
|
||||
|
||||
|
||||
class _BinaryContentHandler(object):
|
||||
|
||||
def __init__(self, content_type, pack, unpack):
|
||||
self._pack = pack
|
||||
self._unpack = unpack
|
||||
self.content_type = content_type
|
||||
|
||||
def to_bytes(self, data_dict, encoding=None):
|
||||
return self.content_type, self._pack(data_dict)
|
||||
|
||||
def from_bytes(self, data, encoding=None):
|
||||
return self._unpack(data)
|
||||
|
||||
|
||||
class _TextContentHandler(object):
|
||||
|
||||
def __init__(self, content_type, dumps, loads, default_encoding):
|
||||
self._dumps = dumps
|
||||
self._loads = loads
|
||||
self.content_type = content_type
|
||||
self.default_encoding = default_encoding
|
||||
|
||||
def to_bytes(self, data_dict, encoding=None):
|
||||
selected = encoding or self.default_encoding
|
||||
content_type = '{0}; charset="{1}"'.format(self.content_type, selected)
|
||||
dumped = self._dumps(escape.recursive_unicode(data_dict))
|
||||
return content_type, dumped.encode(selected)
|
||||
|
||||
def from_bytes(self, data, encoding=None):
|
||||
return self._loads(data.decode(encoding or self.default_encoding))
|
119
sprockets/mixins/mediatype/handlers.py
Normal file
119
sprockets/mixins/mediatype/handlers.py
Normal file
|
@ -0,0 +1,119 @@
|
|||
"""
|
||||
Basic content handlers.
|
||||
|
||||
- :class:`BinaryContentHandler` basic transcoder for binary types that
|
||||
simply calls functions for encoding and decoding
|
||||
- :class:`TextContentHandler` transcoder that translates binary bodies
|
||||
to text before calling functions that encode & decode text
|
||||
|
||||
"""
|
||||
from tornado import escape
|
||||
|
||||
|
||||
class BinaryContentHandler(object):
|
||||
"""
|
||||
Pack and unpack binary types.
|
||||
|
||||
:param str content_type: registered content type
|
||||
:param pack: function that transforms an object instance
|
||||
into :class:`bytes`
|
||||
:param unpack: function that transforms :class:`bytes`
|
||||
into an object instance
|
||||
|
||||
This transcoder is a thin veneer around a pair of packing
|
||||
and unpacking functions.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, content_type, pack, unpack):
|
||||
self._pack = pack
|
||||
self._unpack = unpack
|
||||
self.content_type = content_type
|
||||
|
||||
def to_bytes(self, inst_data, encoding=None):
|
||||
"""
|
||||
Transform an object into :class:`bytes`.
|
||||
|
||||
:param object inst_data: object to encode
|
||||
:param str encoding: ignored
|
||||
:returns: :class:`tuple` of the selected content
|
||||
type and the :class:`bytes` representation of
|
||||
`inst_data`
|
||||
|
||||
"""
|
||||
return self.content_type, self._pack(inst_data)
|
||||
|
||||
def from_bytes(self, data_bytes, encoding=None):
|
||||
"""
|
||||
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
|
||||
:returns: decoded :class:`object` instance
|
||||
|
||||
"""
|
||||
return self._unpack(data_bytes)
|
||||
|
||||
|
||||
class TextContentHandler(object):
|
||||
"""
|
||||
Transcodes between textual and object representations.
|
||||
|
||||
:param str 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
|
||||
transcoding from the underlying body :class:`byte`
|
||||
instance
|
||||
|
||||
This transcoder wraps functions that transcode between :class:`str`
|
||||
and :class:`object` instances. In particular, it handles the
|
||||
additional step of transcoding into the :class:`byte` instances
|
||||
that tornado expects.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, content_type, dumps, loads, default_encoding):
|
||||
self._dumps = dumps
|
||||
self._loads = loads
|
||||
self.content_type = content_type
|
||||
self.default_encoding = default_encoding
|
||||
|
||||
def to_bytes(self, inst_data, encoding=None):
|
||||
"""
|
||||
Transform an object into :class:`bytes`.
|
||||
|
||||
:param object inst_data: object to encode
|
||||
:param str 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
|
||||
type and the :class:`bytes` representation of
|
||||
`inst_data`
|
||||
|
||||
"""
|
||||
selected = encoding or self.default_encoding
|
||||
content_type = '{0}; charset="{1}"'.format(self.content_type, selected)
|
||||
dumped = self._dumps(escape.recursive_unicode(inst_data))
|
||||
return content_type, dumped.encode(selected)
|
||||
|
||||
def from_bytes(self, data, encoding=None):
|
||||
"""
|
||||
Get an object from :class:`bytes`
|
||||
|
||||
:param bytes data: stream of bytes to decode
|
||||
:param str 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
|
||||
|
||||
"""
|
||||
return self._loads(data.decode(encoding or self.default_encoding))
|
Loading…
Reference in a new issue