Back to index

moin  1.9.0~rc2
wrappers.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     werkzeug.contrib.wrappers
00004     ~~~~~~~~~~~~~~~~~~~~~~~~~
00005 
00006     Extra wrappers or mixins contributed by the community.  These wrappers can
00007     be mixed in into request objects to add extra functionality.
00008 
00009     Example::
00010 
00011         from werkzeug import Request as RequestBase
00012         from werkzeug.contrib.wrappers import JSONRequestMixin
00013 
00014         class Request(RequestBase, JSONRequestMixin):
00015             pass
00016 
00017     Afterwards this request object provides the extra functionality of the
00018     :class:`JSONRequestMixin`.
00019 
00020     :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
00021     :license: BSD, see LICENSE for more details.
00022 """
00023 from werkzeug.exceptions import BadRequest
00024 from werkzeug.utils import cached_property
00025 from werkzeug._internal import _decode_unicode
00026 try:
00027     from simplejson import loads
00028 except ImportError:
00029     from json import loads
00030 
00031 
00032 class JSONRequestMixin(object):
00033     """Add json method to a request object.  This will parse the input data
00034     through simplejson if possible.
00035 
00036     :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
00037     is not json or if the data itself cannot be parsed as json.
00038     """
00039 
00040     @cached_property
00041     def json(self):
00042         """Get the result of simplejson.loads if possible."""
00043         if 'json' not in self.environ.get('CONTENT_TYPE', ''):
00044             raise BadRequest('Not a JSON request')
00045         try:
00046             return loads(self.data)
00047         except Exception:
00048             raise BadRequest('Unable to read JSON request')
00049 
00050 
00051 class ProtobufRequestMixin(object):
00052     """Add protobuf parsing method to a request object.  This will parse the
00053     input data through `protobuf`_ if possible.
00054 
00055     :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
00056     is not protobuf or if the data itself cannot be parsed property.
00057 
00058     .. _protobuf: http://code.google.com/p/protobuf/
00059     """
00060 
00061     #: by default the :class:`ProtobufRequestMixin` will raise a
00062     #: :exc:`~werkzeug.exceptions.BadRequest` if the object is not
00063     #: initialized.  You can bypass that check by setting this
00064     #: attribute to `False`.
00065     protobuf_check_initialization = True
00066 
00067     def parse_protobuf(self, proto_type):
00068         """Parse the data into an instance of proto_type."""
00069         if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''):
00070             raise BadRequest('Not a Protobuf request')
00071 
00072         obj = proto_type()
00073         try:
00074             obj.ParseFromString(self.data)
00075         except Exception:
00076             raise BadRequest("Unable to parse Protobuf request")
00077 
00078         # Fail if not all required fields are set
00079         if self.protobuf_check_initialization and not obj.IsInitialized():
00080             raise BadRequest("Partial Protobuf request")
00081 
00082         return obj
00083 
00084 
00085 class RoutingArgsRequestMixin(object):
00086     """This request mixin adds support for the wsgiorg routing args
00087     `specification`_.
00088 
00089     .. _specification: http://www.wsgi.org/wsgi/Specifications/routing_args
00090     """
00091 
00092     def _get_routing_args(self):
00093         return self.environ.get('wsgiorg.routing_args', (()))[0]
00094 
00095     def _set_routing_args(self, value):
00096         if self.shallow:
00097             raise RuntimeError('A shallow request tried to modify the WSGI '
00098                                'environment.  If you really want to do that, '
00099                                'set `shallow` to False.')
00100         self.environ['wsgiorg.routing_args'] = (value, self.routing_vars)
00101 
00102     routing_args = property(_get_routing_args, _set_routing_args, doc='''
00103         The positional URL arguments as `tuple`.''')
00104     del _get_routing_args, _set_routing_args
00105 
00106     def _get_routing_vars(self):
00107         rv = self.environ.get('wsgiorg.routing_args')
00108         if rv is not None:
00109             return rv[1]
00110         rv = {}
00111         if not self.shallow:
00112             self.routing_vars = rv
00113         return rv
00114 
00115     def _set_routing_vars(self, value):
00116         if self.shallow:
00117             raise RuntimeError('A shallow request tried to modify the WSGI '
00118                                'environment.  If you really want to do that, '
00119                                'set `shallow` to False.')
00120         self.environ['wsgiorg.routing_args'] = (self.routing_args, value)
00121 
00122     routing_vars = property(_get_routing_vars, _set_routing_vars, doc='''
00123         The keyword URL arguments as `dict`.''')
00124     del _get_routing_vars, _set_routing_vars
00125 
00126 
00127 class ReverseSlashBehaviorRequestMixin(object):
00128     """This mixin reverses the trailing slash behavior of :attr:`script_root`
00129     and :attr:`path`.  This makes it possible to use :func:`~urlparse.urljoin`
00130     directly on the paths.
00131 
00132     Because it changes the behavior or :class:`Request` this class has to be
00133     mixed in *before* the actual request class::
00134 
00135         class MyRequest(ReverseSlashBehaviorRequestMixin, Request):
00136             pass
00137 
00138     This example shows the differences (for an application mounted on
00139     `/application` and the request going to `/application/foo/bar`):
00140 
00141         +---------------+-------------------+---------------------+
00142         |               | normal behavior   | reverse behavior    |
00143         +===============+===================+=====================+
00144         | `script_root` | ``/application``  | ``/application/``   |
00145         +---------------+-------------------+---------------------+
00146         | `path`        | ``/foo/bar``      | ``foo/bar``         |
00147         +---------------+-------------------+---------------------+
00148     """
00149 
00150     @cached_property
00151     def path(self):
00152         """Requested path as unicode.  This works a bit like the regular path
00153         info in the WSGI environment but will not include a leading slash.
00154         """
00155         path = (self.environ.get('PATH_INFO') or '').lstrip('/')
00156         return _decode_unicode(path, self.charset, self.encoding_errors)
00157 
00158     @cached_property
00159     def script_root(self):
00160         """The root path of the script includling a trailing slash."""
00161         path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/') + '/'
00162         return _decode_unicode(path, self.charset, self.encoding_errors)