Flask update

This commit is contained in:
Ruud
2011-08-24 20:59:59 +02:00
parent 970b400a0c
commit d7f758dff4
90 changed files with 3328 additions and 1236 deletions
+201 -47
View File
@@ -5,7 +5,7 @@
This module provides mixins and classes with an immutable interface.
:copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details.
:copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
import re
@@ -291,11 +291,6 @@ class MultiDict(TypeConversionDict):
or `None`.
"""
# the key error this class raises. Because of circular dependencies
# with the http exception module this class is created at the end of
# this module.
KeyError = None
def __init__(self, mapping=None):
if isinstance(mapping, MultiDict):
dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
@@ -333,7 +328,7 @@ class MultiDict(TypeConversionDict):
"""
if key in self:
return dict.__getitem__(self, key)[0]
raise self.KeyError(key)
raise BadRequestKeyError(key)
def __setitem__(self, key, value):
"""Like :meth:`add` but removes an existing key first.
@@ -539,7 +534,7 @@ class MultiDict(TypeConversionDict):
except KeyError, e:
if default is not _missing:
return default
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
def popitem(self):
"""Pop an item from the dict."""
@@ -547,7 +542,7 @@ class MultiDict(TypeConversionDict):
item = dict.popitem(self)
return (item[0], item[1][0])
except KeyError, e:
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
def poplist(self, key):
"""Pop the list for a key from the dict. If the key is not in the dict
@@ -564,7 +559,7 @@ class MultiDict(TypeConversionDict):
try:
return dict.popitem(self)
except KeyError, e:
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
@@ -617,11 +612,6 @@ class OrderedMultiDict(MultiDict):
the internal bucket objects are exposed.
"""
# the key error this class raises. Because of circular dependencies
# with the http exception module this class is created at the end of
# this module.
KeyError = None
def __init__(self, mapping=None):
dict.__init__(self)
self._first_bucket = self._last_bucket = None
@@ -670,7 +660,7 @@ class OrderedMultiDict(MultiDict):
def __getitem__(self, key):
if key in self:
return dict.__getitem__(self, key)[0].value
raise self.KeyError(key)
raise BadRequestKeyError(key)
def __setitem__(self, key, value):
self.poplist(key)
@@ -755,7 +745,7 @@ class OrderedMultiDict(MultiDict):
except KeyError, e:
if default is not _missing:
return default
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
for bucket in buckets:
bucket.unlink(self)
return buckets[0].value
@@ -764,7 +754,7 @@ class OrderedMultiDict(MultiDict):
try:
key, buckets = dict.popitem(self)
except KeyError, e:
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
for bucket in buckets:
bucket.unlink(self)
return key, buckets[0].value
@@ -773,7 +763,7 @@ class OrderedMultiDict(MultiDict):
try:
key, buckets = dict.popitem(self)
except KeyError, e:
raise self.KeyError(str(e))
raise BadRequestKeyError(str(e))
for bucket in buckets:
bucket.unlink(self)
return key, [x.value for x in buckets]
@@ -810,11 +800,6 @@ class Headers(object):
:param defaults: The list of default values for the :class:`Headers`.
"""
# the key error this class raises. Because of circular dependencies
# with the http exception module this class is created at the end of
# this module.
KeyError = None
def __init__(self, defaults=None, _list=None):
if _list is None:
_list = []
@@ -856,7 +841,7 @@ class Headers(object):
# key error instead of our special one.
if _get_mode:
raise KeyError()
raise self.KeyError(key)
raise BadRequestKeyError(key)
def __eq__(self, other):
return other.__class__ is self.__class__ and \
@@ -1267,7 +1252,7 @@ class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict):
for d in self.dicts:
if key in d:
return d[key]
raise self.KeyError(key)
raise BadRequestKeyError(key)
def get(self, key, default=None, type=None):
for d in self.dicts:
@@ -1385,16 +1370,18 @@ class FileMultiDict(MultiDict):
:param content_type: an optional content type
"""
if isinstance(file, FileStorage):
self[name] = file
return
if isinstance(file, basestring):
if filename is None:
filename = file
file = open(file, 'rb')
if filename and content_type is None:
content_type = mimetypes.guess_type(filename)[0] or \
'application/octet-stream'
self[name] = FileStorage(file, filename, name, content_type)
value = file
else:
if isinstance(file, basestring):
if filename is None:
filename = file
file = open(file, 'rb')
if filename and content_type is None:
content_type = mimetypes.guess_type(filename)[0] or \
'application/octet-stream'
value = FileStorage(file, filename, name, content_type)
self.add(name, value)
class ImmutableDict(ImmutableDictMixin, dict):
@@ -1651,6 +1638,11 @@ class MIMEAccept(Accept):
'application/xml' in self
)
@property
def accept_json(self):
"""True if this object accepts JSON."""
return 'application/json' in self
class LanguageAccept(Accept):
"""Like :class:`Accept` but with normalization for languages."""
@@ -2068,6 +2060,165 @@ class ETags(object):
return '<%s %r>' % (self.__class__.__name__, str(self))
class IfRange(object):
"""Very simple object that represents the `If-Range` header in parsed
form. It will either have neither a etag or date or one of either but
never both.
.. versionadded:: 0.7
"""
def __init__(self, etag=None, date=None):
#: The etag parsed and unquoted. Ranges always operate on strong
#: etags so the weakness information is not necessary.
self.etag = etag
#: The date in parsed format or `None`.
self.date = date
def to_header(self):
"""Converts the object back into an HTTP header."""
if self.date is not None:
return http_date(self.date)
if self.etag is not None:
return quote_etag(self.etag)
return ''
def __str__(self):
return self.to_header()
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, str(self))
class Range(object):
"""Represents a range header. All the methods are only supporting bytes
as unit. It does store multiple ranges but :meth:`range_for_length` will
only work if only one range is provided.
.. versionadded:: 0.7
"""
def __init__(self, units, ranges):
#: The units of this range. Usually "bytes".
self.units = units
#: A list of ``(begin, end)`` tuples for the range header provided.
#: The ranges are non-inclusive.
self.ranges = ranges
def range_for_length(self, length):
"""If the range is for bytes, the length is not None and there is
exactly one range and it is satisfiable it returns a ``(start, stop)``
tuple, otherwise `None`.
"""
if self.units != 'bytes' or length is None or len(self.ranges) != 1:
return None
start, end = self.ranges[0]
if end is None:
end = length
if start < 0:
start += length
if is_byte_range_valid(start, end, length):
return start, min(end, length)
def make_content_range(self, length):
"""Creates a :class:`~werkzeug.datastructures.ContentRange` object
from the current range and given content length.
"""
rng = self.range_for_length(length)
if rng is not None:
return ContentRange(self.units, rng[0], rng[1], length)
def to_header(self):
"""Converts the object back into an HTTP header."""
ranges = []
for begin, end in self.ranges:
if end is None:
ranges.append(begin >= 0 and '%s-' % begin or str(begin))
else:
ranges.append('%s-%s' % (begin, end - 1))
return '%s=%s' % (self.units, ','.join(ranges))
def __str__(self):
return self.to_header()
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, str(self))
class ContentRange(object):
"""Represents the content range header.
.. versionadded:: 0.7
"""
def __init__(self, units, start, stop, length=None, on_update=None):
assert is_byte_range_valid(start, stop, length), \
'Bad range provided'
self.on_update = on_update
self.set(start, stop, length, units)
def _callback_property(name):
def fget(self):
return getattr(self, name)
def fset(self, value):
setattr(self, name, value)
if self.on_update is not None:
self.on_update(self)
return property(fget, fset)
#: The units to use, usually "bytes"
units = _callback_property('_units')
#: The start point of the range or `None`.
start = _callback_property('_start')
#: The stop point of the range (non-inclusive) or `None`. Can only be
#: `None` if also start is `None`.
stop = _callback_property('_stop')
#: The length of the range or `None`.
length = _callback_property('_length')
def set(self, start, stop, length=None, units='bytes'):
"""Simple method to update the ranges."""
assert is_byte_range_valid(start, stop, length), \
'Bad range provided'
self._units = units
self._start = start
self._stop = stop
self._length = length
if self.on_update is not None:
self.on_update(self)
def unset(self):
"""Sets the units to `None` which indicates that the header should
no longer be used.
"""
self.set(None, None, units=None)
def to_header(self):
if self.units is None:
return ''
if self.length is None:
length = '*'
else:
length = self.length
if self.start is None:
return '%s */%s' % (self.units, length)
return '%s %s-%s/%s' % (
self.units,
self.start,
self.stop - 1,
length
)
def __nonzero__(self):
return self.units is not None
def __str__(self):
return self.to_header()
def __repr__(self):
return '<%s %r>' % (self.__class__.__name__, str(self))
class Authorization(ImmutableDictMixin, dict):
"""Represents an `Authorization` header sent by the client. You should
not create this kind of object yourself but use it when it's returned by
@@ -2275,7 +2426,17 @@ class FileStorage(object):
headers=None):
self.name = name
self.stream = stream or _empty_stream
self.filename = filename or getattr(stream, 'name', None)
# if no filename is provided we can attempt to get the filename
# from the stream object passed. There we have to be careful to
# skip things like <fdopen>, <stderr> etc. Python marks these
# special filenames with angular brackets.
if filename is None:
filename = getattr(stream, 'name', None)
if filename and filename[0] == '<' and filename[-1] == '>':
filename = None
self.filename = filename
self.content_type = content_type
self.content_length = content_length
if headers is None:
@@ -2361,13 +2522,6 @@ class FileStorage(object):
# circular dependencies
from werkzeug.http import dump_options_header, dump_header, generate_etag, \
quote_header_value, parse_set_header, unquote_etag, \
parse_options_header
# create all the special key errors now that the classes are defined.
from werkzeug.exceptions import BadRequest
for _cls in MultiDict, OrderedMultiDict, CombinedMultiDict, Headers, \
EnvironHeaders:
_cls.KeyError = BadRequest.wrap(KeyError, _cls.__name__ + '.KeyError')
del _cls
quote_header_value, parse_set_header, unquote_etag, quote_etag, \
parse_options_header, http_date, is_byte_range_valid
from werkzeug.exceptions import BadRequestKeyError