changeset 4614:159902268129

upgraded werkzeug
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sun, 01 Mar 2009 03:18:57 +0100
parents e7f1cf9eeb96
children 8456e790eb58
files MoinMoin/support/werkzeug/__init__.py MoinMoin/support/werkzeug/contrib/sessions.py MoinMoin/support/werkzeug/datastructures.py MoinMoin/support/werkzeug/serving.py MoinMoin/support/werkzeug/test.py MoinMoin/support/werkzeug/utils.py MoinMoin/support/werkzeug/wrappers.py
diffstat 7 files changed, 127 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/support/werkzeug/__init__.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/__init__.py	Sun Mar 01 03:18:57 2009 +0100
@@ -52,7 +52,7 @@
                              'LanguageAccept', 'RequestCacheControl',
                              'ResponseCacheControl', 'ETags', 'HeaderSet',
                              'WWWAuthenticate', 'Authorization',
-                             'CacheControl', 'FileMultiDict'],
+                             'CacheControl', 'FileMultiDict', 'CallbackDict'],
     'werkzeug.useragents':  ['UserAgent'],
     'werkzeug.http':        ['parse_etags', 'parse_date', 'parse_cache_control_header',
                              'is_resource_modified', 'parse_accept_header',
--- a/MoinMoin/support/werkzeug/contrib/sessions.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/contrib/sessions.py	Sun Mar 01 03:18:57 2009 +0100
@@ -55,7 +55,9 @@
 except ImportError:
     from sha import new as sha1
 from cPickle import dump, load, HIGHEST_PROTOCOL
+
 from werkzeug.utils import ClosingIterator, dump_cookie, parse_cookie
+from werkzeug.datastructures import CallbackDict
 
 
 _sha1_re = re.compile(r'^[a-fA-F0-9]{40}$')
@@ -71,18 +73,15 @@
     return sha1('%s%s%s' % (salt, time(), _urandom())).hexdigest()
 
 
-class ModificationTrackingDict(dict):
+class ModificationTrackingDict(CallbackDict):
     __slots__ = ('modified',)
 
     def __init__(self, *args, **kwargs):
-        dict.__init__(self, *args, **kwargs)
+        def on_update(self):
+            self.modified = True
         self.modified = False
-
-    def __repr__(self):
-        return '<%s %s%s>' % (
-            self.__class__.__name__,
-            dict.__repr__(self)
-        )
+        CallbackDict.__init__(self, on_update=on_update)
+        dict.update(self, *args, **kwargs)
 
     def copy(self):
         """Create a flat copy of the dict."""
@@ -97,29 +96,6 @@
     def __copy__(self):
         return self.copy()
 
-    def call_with_modification(f):
-        def oncall(self, *args, **kw):
-            try:
-                return f(self, *args, **kw)
-            finally:
-                self.modified = True
-        try:
-            oncall.__name__ = f.__name__
-            oncall.__doc__ = f.__doc__
-            oncall.__module__ = f.__module__
-        except:
-            pass
-        return oncall
-
-    __setitem__ = call_with_modification(dict.__setitem__)
-    __delitem__ = call_with_modification(dict.__delitem__)
-    clear = call_with_modification(dict.clear)
-    pop = call_with_modification(dict.pop)
-    popitem = call_with_modification(dict.popitem)
-    setdefault = call_with_modification(dict.setdefault)
-    update = call_with_modification(dict.update)
-    del call_with_modification
-
 
 class Session(ModificationTrackingDict):
     """
--- a/MoinMoin/support/werkzeug/datastructures.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/datastructures.py	Sun Mar 01 03:18:57 2009 +0100
@@ -143,6 +143,7 @@
     __setitem__ = calls_update('__setitem__')
     __delitem__ = calls_update('__delitem__')
     clear = calls_update('clear')
+    pop = calls_update('pop')
     popitem = calls_update('popitem')
     setdefault = calls_update('setdefault')
     update = calls_update('update')
@@ -1443,6 +1444,22 @@
 _CacheControl.cache_property = staticmethod(cache_property)
 
 
+class CallbackDict(UpdateDictMixin, dict):
+    """A dict that calls a function passed every time something is changed.
+    The function is passed the dict instance.
+    """
+
+    def __init__(self, initial=None, on_update=None):
+        dict.__init__(self, initial or ())
+        self.on_update = on_update
+
+    def __repr__(self):
+        return '<%s %s>' % (
+            self.__class__.__name__,
+            dict.__repr__(self)
+        )
+
+
 class HeaderSet(object):
     """Similar to the :class:`ETags` class this implements a set-like structure.
     Unlike :class:`ETags` this is case insensitive and used for vary, allow, and
--- a/MoinMoin/support/werkzeug/serving.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/serving.py	Sun Mar 01 03:18:57 2009 +0100
@@ -82,8 +82,11 @@
         if path_info:
             from urllib import unquote
             environ['PATH_INFO'] = unquote(path_info)
+
         for key, value in self.headers.items():
-            environ['HTTP_' + key.upper().replace('-', '_')] = value
+            key = 'HTTP_' + key.upper().replace('-', '_')
+            if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+                environ[key] = value
 
         headers_set = []
         headers_sent = []
--- a/MoinMoin/support/werkzeug/test.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/test.py	Sun Mar 01 03:18:57 2009 +0100
@@ -278,6 +278,7 @@
         self.environ_base = environ_base
         self.environ_overrides = environ_overrides
         self.input_stream = input_stream
+        self.content_length = content_length
         self.closed = False
 
         if data:
@@ -364,7 +365,10 @@
         return self.headers.get('Content-Length', type=int)
 
     def _set_content_length(self, value):
-        self.headers['Content-Length'] = str(value)
+        if value is None:
+            self.headers.pop('Content-Length', None)
+        else:
+            self.headers['Content-Length'] = str(value)
 
     content_length = property(_get_content_length, _set_content_length, doc='''
         The content length as integer.  Reflected from and to the
--- a/MoinMoin/support/werkzeug/utils.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/utils.py	Sun Mar 01 03:18:57 2009 +0100
@@ -177,7 +177,7 @@
         manager = ResourceManager()
         filesystem_bound = isinstance(provider, DefaultProvider)
         def loader(path):
-            if not provider.has_resource(path):
+            if path is None or not provider.has_resource(path):
                 return None, None
             basename = posixpath.basename(path)
             if filesystem_bound:
@@ -192,7 +192,10 @@
 
     def get_directory_loader(self, directory):
         def loader(path):
-            path = os.path.join(directory, path)
+            if path is not None:
+                path = os.path.join(directory, path)
+            else:
+                path = directory
             if os.path.isfile(path):
                 return os.path.basename(path), self._opener(path)
             return None, None
--- a/MoinMoin/support/werkzeug/wrappers.py	Sun Mar 01 03:02:57 2009 +0100
+++ b/MoinMoin/support/werkzeug/wrappers.py	Sun Mar 01 03:18:57 2009 +0100
@@ -28,7 +28,8 @@
      parse_date, generate_etag, is_resource_modified, unquote_etag, \
      quote_etag, parse_set_header, parse_authorization_header, \
      parse_www_authenticate_header, remove_entity_headers, \
-     default_stream_factory
+     default_stream_factory, parse_options_header, \
+     dump_options_header
 from werkzeug.utils import cached_property, environ_property, \
      get_current_url, url_encode, run_wsgi_app, get_host, \
      cookie_date, parse_cookie, dump_cookie, http_date, escape, \
@@ -36,7 +37,7 @@
 from werkzeug.datastructures import MultiDict, CombinedMultiDict, Headers, \
      EnvironHeaders, ImmutableMultiDict, ImmutableTypeConversionDict, \
      ImmutableList, MIMEAccept, CharsetAccept, LanguageAccept, \
-     ResponseCacheControl, RequestCacheControl
+     ResponseCacheControl, RequestCacheControl, CallbackDict
 from werkzeug._internal import _empty_stream, _decode_unicode, \
      _patch_wrapper
 
@@ -935,6 +936,73 @@
         return ResponseStream(self)
 
 
+class CommonRequestDescriptorsMixin(object):
+    """A mixin for :class:`BaseRequest` subclasses.  Request objects that
+    mix this class in will automatically get descriptors for a coupl eof
+    HTTP headers with automatic type conversion.
+
+    .. versionadded:: 0.5
+    """
+
+    content_type = environ_property('CONTENT_TYPE', doc='''
+         The Content-Type entity-header field indicates the media type of
+         the entity-body sent to the recipient or, in the case of the HEAD
+         method, the media type that would have been sent had the request
+         been a GET.''')
+    content_length = environ_property('CONTENT_LENGTH', None, int, str, doc='''
+         The Content-Length entity-header field indicates the size of the
+         entity-body in bytes or, in the case of the HEAD method, the size of
+         the entity-body that would have been sent had the request been a
+         GET.''')
+    referrer = environ_property('HTTP_REFERER', doc='''
+        The Referer[sic] request-header field allows the client to specify,
+        for the server's benefit, the address (URI) of the resource from which
+        the Request-URI was obtained (the "referrer", although the header
+        field is misspelled).''')
+    date = environ_property('HTTP_DATE', None, parse_date, doc='''
+        The Date general-header field represents the date and time at which
+        the message was originated, having the same semantics as orig-date
+        in RFC 822.''')
+    max_forwards = environ_property('HTTP_MAX_FORWARDS', None, int, doc='''
+         The Max-Forwards request-header field provides a mechanism with the
+         TRACE and OPTIONS methods to limit the number of proxies or gateways
+         that can forward the request to the next inbound server.''')
+
+    def _parse_content_type(self):
+        if not hasattr(self, '_parsed_content_type'):
+            self._parsed_content_type = \
+                parse_options_header(self.environ.get('CONTENT_TYPE', ''))
+
+    @property
+    def mimetype(self):
+        """Like :attr:`content_type` but without parameters (eg, without
+        charset, type etc.).  For example if the content
+        type is ``text/html; charset=utf-8`` the mimetype would be
+        ``'text/html'``.
+        """
+        self._parse_content_type()
+        return self._parsed_content_type[0]
+
+    @property
+    def mimetype_params(self):
+        """The mimetype parameters as dict.  For example if the content
+        type is ``text/html; charset=utf-8`` the params would be
+        ``{'charset': 'utf-8'}``.
+        """
+        self._parse_content_type()
+        return self._parsed_content_type[1]
+
+    @cached_property
+    def pragma(self):
+        """The Pragma general-header field is used to include
+        implementation-specific directives that might apply to any recipient
+        along the request/response chain.  All pragma directives specify
+        optional behavior from the viewpoint of the protocol; however, some
+        systems MAY require that behavior be consistent with the directives.
+        """
+        return parse_set_header(self.environ.get('HTTP_PRAGMA', ''))
+
+
 class CommonResponseDescriptorsMixin(object):
     """A mixin for :class:`BaseResponse` subclasses.  Response objects that
     mix this class in will automatically get descriptors for a couple of
@@ -942,16 +1010,29 @@
     """
 
     def _get_mimetype(self):
-        """The mimetype (content type without charset etc.)"""
-        ct = self.headers.get('Content-Type')
+        ct = self.headers.get('content-type')
         if ct:
             return ct.split(';')[0].strip()
 
     def _set_mimetype(self, value):
         self.headers['Content-Type'] = get_content_type(value, self.charset)
 
+    def _get_mimetype_params(self):
+        def on_update(d):
+            self.headers['Content-Type'] = \
+                dump_options_header(self.mimetype, d)
+        d = parse_options_header(self.headers.get('content-type', ''))[1]
+        return CallbackDict(d, on_update)
+
     mimetype = property(_get_mimetype, _set_mimetype, doc='''
         The mimetype (content type without charset etc.)''')
+    mimetype_params = property(_get_mimetype_params, doc='''
+        The mimetype parameters as dict.  For example if the content
+        type is ``text/html; charset=utf-8`` the params would be
+        ``{'charset': 'utf-8'}``.
+
+        .. versionadded:: 0.5
+        ''')
     location = header_property('Location', doc='''
         The Location response-header field is used to redirect the recipient
         to a location other than the Request-URI for completion of the request
@@ -1076,13 +1157,15 @@
 
 
 class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
-              UserAgentMixin, AuthorizationMixin):
+              UserAgentMixin, AuthorizationMixin,
+              CommonRequestDescriptorsMixin):
     """Full featured request object implementing the following mixins:
 
     - :class:`AcceptMixin` for accept header parsing
     - :class:`ETagRequestMixin` for etag and cache control handling
     - :class:`UserAgentMixin` for user agent introspection
     - :class:`AuthorizationMixin` for http auth handling
+    - :class:`CommonRequestDescriptorsMixin` for common headers
     """