537 lines
16 KiB
Python
Executable file
537 lines
16 KiB
Python
Executable file
# -*- coding: utf-8 -*-
|
|
from copy import copy
|
|
import pickle
|
|
|
|
from cStringIO import StringIO
|
|
from nose.tools import assert_raises
|
|
from werkzeug.datastructures import FileStorage, MultiDict, \
|
|
ImmutableMultiDict, CombinedMultiDict, ImmutableTypeConversionDict, \
|
|
ImmutableDict, Headers, ImmutableList, EnvironHeaders, \
|
|
OrderedMultiDict, ImmutableOrderedMultiDict, HeaderSet
|
|
|
|
|
|
def test_multidict_pickle():
|
|
"""MultiDict types are pickle-able"""
|
|
for protocol in xrange(pickle.HIGHEST_PROTOCOL + 1):
|
|
print 'pickling protocol', protocol
|
|
d = MultiDict()
|
|
d.setlist('foo', [1, 2, 3, 4])
|
|
d.setlist('bar', 'foo bar baz'.split())
|
|
s = pickle.dumps(d, protocol)
|
|
ud = pickle.loads(s)
|
|
assert type(ud) is type(d)
|
|
assert ud == d
|
|
assert pickle.loads(s.replace('werkzeug.datastructures', 'werkzeug')) == d
|
|
ud['newkey'] = 'bla'
|
|
assert ud != d
|
|
|
|
d2 = OrderedMultiDict(d)
|
|
d2.add('foo', 5)
|
|
s = pickle.dumps(d2, protocol)
|
|
ud = pickle.loads(s)
|
|
assert type(ud) is type(d2)
|
|
assert ud == d2
|
|
ud['newkey'] = 'bla'
|
|
print ud
|
|
print d2
|
|
assert ud != d2
|
|
|
|
im = ImmutableMultiDict(d)
|
|
assert im == d
|
|
s = pickle.dumps(im, protocol)
|
|
ud = pickle.loads(s)
|
|
assert ud == im
|
|
assert type(ud) is type(im)
|
|
|
|
c = CombinedMultiDict([ud, im])
|
|
cc = pickle.loads(pickle.dumps(c, protocol))
|
|
assert c == cc
|
|
assert type(c) is type(cc)
|
|
|
|
|
|
def test_immutable_dict_pickle():
|
|
"""ImmutableDicts are pickle-able"""
|
|
for protocol in xrange(pickle.HIGHEST_PROTOCOL + 1):
|
|
d = dict(foo="bar", blub="blah", meh=42)
|
|
for dtype in ImmutableDict, ImmutableTypeConversionDict:
|
|
nd = dtype(d)
|
|
od = pickle.loads(pickle.dumps(nd, protocol))
|
|
assert od == nd
|
|
assert pickle.loads(pickle.dumps(nd, protocol) \
|
|
.replace('werkzeug.datastructures', 'werkzeug')) == nd
|
|
assert type(od) is type(nd)
|
|
|
|
|
|
def test_immutable_list_pickle():
|
|
"""ImmutableLists are pickle-able"""
|
|
for protocol in xrange(pickle.HIGHEST_PROTOCOL + 1):
|
|
l = ImmutableList(range(100))
|
|
ul = pickle.loads(pickle.dumps(l, protocol))
|
|
assert l == ul
|
|
assert pickle.loads(pickle.dumps(l, protocol) \
|
|
.replace('werkzeug.datastructures', 'werkzeug')) == l
|
|
assert type(l) is type(ul)
|
|
|
|
|
|
def test_file_storage_truthiness():
|
|
"""Test FileStorage truthiness"""
|
|
fs = FileStorage()
|
|
assert not fs, 'should be False'
|
|
|
|
fs = FileStorage(StringIO('Hello World'), filename='foo.txt')
|
|
assert fs, 'should be True because of a provided filename'
|
|
|
|
|
|
def test_multidict():
|
|
"""Multidict behavior"""
|
|
md = MultiDict()
|
|
assert isinstance(md, dict)
|
|
|
|
mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3),
|
|
('a', 1), ('a', 3), ('d', 4), ('c', 3)]
|
|
md = MultiDict(mapping)
|
|
|
|
# simple getitem gives the first value
|
|
assert md['a'] == 1
|
|
assert md['c'] == 3
|
|
assert_raises(KeyError, lambda: md['e'])
|
|
assert md.get('a') == 1
|
|
|
|
# list getitem
|
|
assert md.getlist('a') == [1, 2, 1, 3]
|
|
assert md.getlist('d') == [3, 4]
|
|
# do not raise if key not found
|
|
assert md.getlist('x') == []
|
|
|
|
# simple setitem overwrites all values
|
|
md['a'] = 42
|
|
assert md.getlist('a') == [42]
|
|
|
|
# list setitem
|
|
md.setlist('a', [1, 2, 3])
|
|
assert md['a'] == 1
|
|
assert md.getlist('a') == [1, 2, 3]
|
|
|
|
# verify that it does not change original lists
|
|
l1 = [1, 2, 3]
|
|
md.setlist('a', l1)
|
|
del l1[:]
|
|
assert md['a'] == 1
|
|
|
|
# setdefault, setlistdefault
|
|
assert md.setdefault('u', 23) == 23
|
|
assert md.getlist('u') == [23]
|
|
del md['u']
|
|
|
|
assert md.setlistdefault('u', [-1, -2]) == [-1, -2]
|
|
assert md.getlist('u') == [-1, -2]
|
|
assert md['u'] == -1
|
|
|
|
# delitem
|
|
del md['u']
|
|
assert_raises(KeyError, lambda: md['u'])
|
|
del md['d']
|
|
assert md.getlist('d') == []
|
|
|
|
# keys, values, items, lists
|
|
assert list(sorted(md.keys())) == ['a', 'b', 'c']
|
|
assert list(sorted(md.iterkeys())) == ['a', 'b', 'c']
|
|
|
|
assert list(sorted(md.values())) == [1, 2, 3]
|
|
assert list(sorted(md.itervalues())) == [1, 2, 3]
|
|
|
|
assert list(sorted(md.items())) == [('a', 1), ('b', 2), ('c', 3)]
|
|
assert list(sorted(md.items(multi=True))) == \
|
|
[('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)]
|
|
assert list(sorted(md.iteritems())) == [('a', 1), ('b', 2), ('c', 3)]
|
|
assert list(sorted(md.iteritems(multi=True))) == \
|
|
[('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)]
|
|
|
|
assert list(sorted(md.lists())) == [('a', [1, 2, 3]), ('b', [2]), ('c', [3])]
|
|
assert list(sorted(md.iterlists())) == [('a', [1, 2, 3]), ('b', [2]), ('c', [3])]
|
|
|
|
# copy method
|
|
copy = md.copy()
|
|
assert copy['a'] == 1
|
|
assert copy.getlist('a') == [1, 2, 3]
|
|
|
|
# update with a multidict
|
|
od = MultiDict([('a', 4), ('a', 5), ('y', 0)])
|
|
md.update(od)
|
|
assert md.getlist('a') == [1, 2, 3, 4, 5]
|
|
assert md.getlist('y') == [0]
|
|
|
|
# update with a regular dict
|
|
md = copy
|
|
od = {'a': 4, 'y': 0}
|
|
md.update(od)
|
|
assert md.getlist('a') == [1, 2, 3, 4]
|
|
assert md.getlist('y') == [0]
|
|
|
|
# pop, poplist, popitem, popitemlist
|
|
assert md.pop('y') == 0
|
|
assert 'y' not in md
|
|
assert md.poplist('a') == [1, 2, 3, 4]
|
|
assert 'a' not in md
|
|
assert md.poplist('missing') == []
|
|
|
|
# remaining: b=2, c=3
|
|
popped = md.popitem()
|
|
assert popped in [('b', 2), ('c', 3)]
|
|
popped = md.popitemlist()
|
|
assert popped in [('b', [2]), ('c', [3])]
|
|
|
|
# type conversion
|
|
md = MultiDict({'a': '4', 'b': ['2', '3']})
|
|
assert md.get('a', type=int) == 4
|
|
assert md.getlist('b', type=int) == [2, 3]
|
|
|
|
# repr
|
|
md = MultiDict([('a', 1), ('a', 2), ('b', 3)])
|
|
assert "('a', 1)" in repr(md)
|
|
assert "('a', 2)" in repr(md)
|
|
assert "('b', 3)" in repr(md)
|
|
|
|
# add and getlist
|
|
md.add('c', '42')
|
|
md.add('c', '23')
|
|
assert md.getlist('c') == ['42', '23']
|
|
md.add('c', 'blah')
|
|
assert md.getlist('c', type=int) == [42, 23]
|
|
|
|
# iter interfaces
|
|
assert list(zip(md.keys(), md.listvalues())) == list(md.lists())
|
|
assert list(zip(md, md.iterlistvalues())) == list(md.iterlists())
|
|
assert list(zip(md.iterkeys(), md.iterlistvalues())) == list(md.iterlists())
|
|
|
|
# setdefault
|
|
md = MultiDict()
|
|
md.setdefault('x', []).append(42)
|
|
md.setdefault('x', []).append(23)
|
|
assert md['x'] == [42, 23]
|
|
|
|
# to dict
|
|
md = MultiDict()
|
|
md['foo'] = 42
|
|
md.add('bar', 1)
|
|
md.add('bar', 2)
|
|
assert md.to_dict() == {'foo': 42, 'bar': 1}
|
|
assert md.to_dict(flat=False) == {'foo': [42], 'bar': [1, 2]}
|
|
|
|
# popitem from empty dict
|
|
assert_raises(KeyError, MultiDict().popitem)
|
|
assert_raises(KeyError, MultiDict().popitemlist)
|
|
|
|
# key errors are of a special type
|
|
assert_raises(MultiDict.KeyError, MultiDict().__getitem__, 42)
|
|
|
|
# setlist works
|
|
md = MultiDict()
|
|
md['foo'] = 42
|
|
md.setlist('foo', [1, 2])
|
|
assert md.getlist('foo') == [1, 2]
|
|
|
|
|
|
def test_combined_multidict():
|
|
"""Combined multidict behavior"""
|
|
d1 = MultiDict([('foo', '1')])
|
|
d2 = MultiDict([('bar', '2'), ('bar', '3')])
|
|
d = CombinedMultiDict([d1, d2])
|
|
|
|
# lookup
|
|
assert d['foo'] == '1'
|
|
assert d['bar'] == '2'
|
|
assert d.getlist('bar') == ['2', '3']
|
|
|
|
assert sorted(d.items()) == [('bar', '2'), ('foo', '1')], d.items()
|
|
assert sorted(d.items(multi=True)) == [('bar', '2'), ('bar', '3'), ('foo', '1')]
|
|
assert 'missingkey' not in d
|
|
assert 'foo' in d
|
|
|
|
# type lookup
|
|
assert d.get('foo', type=int) == 1
|
|
assert d.getlist('bar', type=int) == [2, 3]
|
|
|
|
# get key errors for missing stuff
|
|
assert_raises(KeyError, lambda: d["missing"])
|
|
|
|
# make sure that they are immutable
|
|
def test_assign():
|
|
d['foo'] = 'blub'
|
|
assert_raises(TypeError, test_assign)
|
|
|
|
# copies are immutable
|
|
d = d.copy()
|
|
assert_raises(TypeError, test_assign)
|
|
|
|
# make sure lists merges
|
|
md1 = MultiDict((("foo", "bar"),))
|
|
md2 = MultiDict((("foo", "blafasel"),))
|
|
x = CombinedMultiDict((md1, md2))
|
|
assert x.lists() == [('foo', ['bar', 'blafasel'])]
|
|
|
|
|
|
def test_immutable_dict_copies_are_mutable():
|
|
for cls in ImmutableTypeConversionDict, ImmutableMultiDict, ImmutableDict, \
|
|
ImmutableOrderedMultiDict:
|
|
immutable = cls({'a': 1})
|
|
assert_raises(TypeError, immutable.pop, 'a')
|
|
|
|
mutable = immutable.copy()
|
|
mutable.pop('a')
|
|
assert 'a' in immutable
|
|
assert mutable is not immutable
|
|
|
|
assert copy(immutable) is immutable
|
|
|
|
|
|
def test_headers():
|
|
# simple header tests
|
|
headers = Headers()
|
|
headers.add('Content-Type', 'text/plain')
|
|
headers.add('X-Foo', 'bar')
|
|
assert 'x-Foo' in headers
|
|
assert 'Content-type' in headers
|
|
|
|
headers['Content-Type'] = 'foo/bar'
|
|
assert headers['Content-Type'] == 'foo/bar'
|
|
assert len(headers.getlist('Content-Type')) == 1
|
|
|
|
# list conversion
|
|
assert headers.to_list() == [
|
|
('Content-Type', 'foo/bar'),
|
|
('X-Foo', 'bar')
|
|
]
|
|
assert str(headers) == (
|
|
"Content-Type: foo/bar\r\n"
|
|
"X-Foo: bar\r\n"
|
|
"\r\n")
|
|
assert str(Headers()) == "\r\n"
|
|
|
|
# extended add
|
|
headers.add('Content-Disposition', 'attachment', filename='foo')
|
|
assert headers['Content-Disposition'] == 'attachment; filename=foo'
|
|
|
|
headers.add('x', 'y', z='"')
|
|
assert headers['x'] == r'y; z="\""'
|
|
|
|
# defaults
|
|
headers = Headers([
|
|
('Content-Type', 'text/plain'),
|
|
('X-Foo', 'bar'),
|
|
('X-Bar', '1'),
|
|
('X-Bar', '2')
|
|
])
|
|
assert headers.getlist('x-bar') == ['1', '2']
|
|
assert headers.get('x-Bar') == '1'
|
|
assert headers.get('Content-Type') == 'text/plain'
|
|
|
|
assert headers.setdefault('X-Foo', 'nope') == 'bar'
|
|
assert headers.setdefault('X-Bar', 'nope') == '1'
|
|
assert headers.setdefault('X-Baz', 'quux') == 'quux'
|
|
assert headers.setdefault('X-Baz', 'nope') == 'quux'
|
|
headers.pop('X-Baz')
|
|
|
|
# type conversion
|
|
assert headers.get('x-bar', type=int) == 1
|
|
assert headers.getlist('x-bar', type=int) == [1, 2]
|
|
|
|
# list like operations
|
|
assert headers[0] == ('Content-Type', 'text/plain')
|
|
assert headers[:1] == Headers([('Content-Type', 'text/plain')])
|
|
del headers[:2]
|
|
del headers[-1]
|
|
assert headers == Headers([('X-Bar', '1')])
|
|
|
|
# copying
|
|
a = Headers([('foo', 'bar')])
|
|
b = a.copy()
|
|
a.add('foo', 'baz')
|
|
assert a.getlist('foo') == ['bar', 'baz']
|
|
assert b.getlist('foo') == ['bar']
|
|
|
|
headers = Headers([('a', 1)])
|
|
assert headers.pop('a') == 1
|
|
assert headers.pop('b', 2) == 2
|
|
assert_raises(KeyError, headers.pop, 'c')
|
|
|
|
# set replaces and accepts same arguments as add
|
|
a = Headers()
|
|
a.set('Content-Disposition', 'useless')
|
|
a.set('Content-Disposition', 'attachment', filename='foo')
|
|
assert a['Content-Disposition'] == 'attachment; filename=foo'
|
|
|
|
|
|
def test_header_set():
|
|
"""Test the header set"""
|
|
hs = HeaderSet()
|
|
hs.add('foo')
|
|
hs.add('bar')
|
|
assert 'Bar' in hs
|
|
assert hs.find('foo') == 0
|
|
assert hs.find('BAR') == 1
|
|
assert hs.find('baz') < 0
|
|
hs.discard('missing')
|
|
hs.discard('foo')
|
|
assert hs.find('foo') < 0
|
|
assert hs.find('bar') == 0
|
|
assert_raises(IndexError, hs.index, 'missing')
|
|
assert hs.index('bar') == 0
|
|
assert hs
|
|
hs.clear()
|
|
assert not hs
|
|
|
|
|
|
def test_environ_headers_counts():
|
|
"""Ensure that the EnvironHeaders count correctly."""
|
|
# this happens in multiple WSGI servers because they
|
|
# use a vary naive way to convert the headers;
|
|
broken_env = {
|
|
'HTTP_CONTENT_TYPE': 'text/html',
|
|
'CONTENT_TYPE': 'text/html',
|
|
'HTTP_CONTENT_LENGTH': '0',
|
|
'CONTENT_LENGTH': '0',
|
|
'HTTP_ACCEPT': '*',
|
|
'wsgi.version': (1, 0)
|
|
}
|
|
headers = EnvironHeaders(broken_env)
|
|
assert headers
|
|
assert len(headers) == 3
|
|
assert sorted(headers) == [
|
|
('Accept', '*'),
|
|
('Content-Length', '0'),
|
|
('Content-Type', 'text/html')
|
|
]
|
|
assert not EnvironHeaders({'wsgi.version': (1, 0)})
|
|
assert len(EnvironHeaders({'wsgi.version': (1, 0)})) == 0
|
|
|
|
|
|
def test_multidict_pop():
|
|
"""Ensure pop from multidict works like it should"""
|
|
make_d = lambda: MultiDict({'foo': [1, 2, 3, 4]})
|
|
d = make_d()
|
|
assert d.pop('foo') == 1
|
|
assert not d
|
|
d = make_d()
|
|
assert d.pop('foo', 32) == 1
|
|
assert not d
|
|
d = make_d()
|
|
assert d.pop('foos', 32) == 32
|
|
assert d
|
|
assert_raises(KeyError, d.pop, 'foos')
|
|
|
|
|
|
def test_ordered_multidict():
|
|
"""Test the OrderedMultiDict"""
|
|
d = OrderedMultiDict()
|
|
assert not d
|
|
d.add('foo', 'bar')
|
|
assert len(d) == 1
|
|
d.add('foo', 'baz')
|
|
assert len(d) == 1
|
|
assert d.items() == [('foo', 'bar')]
|
|
assert list(d) == ['foo']
|
|
assert d.items(multi=True) == [('foo', 'bar'),
|
|
('foo', 'baz')]
|
|
del d['foo']
|
|
assert not d
|
|
assert len(d) == 0
|
|
assert list(d) == []
|
|
|
|
d.update([('foo', 1), ('foo', 2), ('bar', 42)])
|
|
d.add('foo', 3)
|
|
assert d.getlist('foo') == [1, 2, 3]
|
|
assert d.getlist('bar') == [42]
|
|
assert d.items() == [('foo', 1), ('bar', 42)]
|
|
assert d.keys() == list(d) == list(d.iterkeys()) == ['foo', 'bar']
|
|
assert d.items(multi=True) == [('foo', 1), ('foo', 2),
|
|
('bar', 42), ('foo', 3)]
|
|
assert len(d) == 2
|
|
|
|
assert d.pop('foo') == 1
|
|
assert d.pop('blafasel', None) is None
|
|
assert d.pop('blafasel', 42) == 42
|
|
assert len(d) == 1
|
|
assert d.poplist('bar') == [42]
|
|
assert not d
|
|
|
|
d.get('missingkey') is None
|
|
|
|
d.add('foo', 42)
|
|
d.add('foo', 23)
|
|
d.add('bar', 2)
|
|
d.add('foo', 42)
|
|
assert d == MultiDict(d)
|
|
id = ImmutableOrderedMultiDict(d)
|
|
assert d == id
|
|
d.add('foo', 2)
|
|
assert d != id
|
|
|
|
d.update({'blah': [1, 2, 3]})
|
|
assert d['blah'] == 1
|
|
assert d.getlist('blah') == [1, 2, 3]
|
|
|
|
# setlist works
|
|
d = OrderedMultiDict()
|
|
d['foo'] = 42
|
|
d.setlist('foo', [1, 2])
|
|
assert d.getlist('foo') == [1, 2]
|
|
|
|
assert_raises(OrderedMultiDict.KeyError, d.pop, 'missing')
|
|
assert_raises(OrderedMultiDict.KeyError, d.__getitem__, 'missing')
|
|
|
|
# popping
|
|
d = OrderedMultiDict()
|
|
d.add('foo', 23)
|
|
d.add('foo', 42)
|
|
d.add('foo', 1)
|
|
assert d.popitem() == ('foo', 23)
|
|
assert_raises(OrderedMultiDict.KeyError, d.popitem)
|
|
assert not d
|
|
|
|
d.add('foo', 23)
|
|
d.add('foo', 42)
|
|
d.add('foo', 1)
|
|
assert d.popitemlist() == ('foo', [23, 42, 1])
|
|
assert_raises(OrderedMultiDict.KeyError, d.popitemlist)
|
|
|
|
|
|
def test_immutable_structures():
|
|
"""Test immutable structures"""
|
|
l = ImmutableList([1, 2, 3])
|
|
assert_raises(TypeError, l.__delitem__, 0)
|
|
assert_raises(TypeError, l.__delslice__, 0, 1)
|
|
assert_raises(TypeError, l.__iadd__, [1, 2])
|
|
assert_raises(TypeError, l.__setitem__, 0, 1)
|
|
assert_raises(TypeError, l.__setslice__, 0, 1, [2, 3])
|
|
assert_raises(TypeError, l.append, 42)
|
|
assert_raises(TypeError, l.insert, 0, 32)
|
|
assert_raises(TypeError, l.pop)
|
|
assert_raises(TypeError, l.extend, [2, 3])
|
|
assert_raises(TypeError, l.reverse)
|
|
assert_raises(TypeError, l.sort)
|
|
assert l == [1, 2, 3]
|
|
|
|
d = ImmutableDict(foo=23, bar=42)
|
|
assert_raises(TypeError, d.setdefault, 'baz')
|
|
assert_raises(TypeError, d.update, {2: 3})
|
|
assert_raises(TypeError, d.popitem)
|
|
assert_raises(TypeError, d.__delitem__, 'foo')
|
|
assert_raises(TypeError, d.clear)
|
|
assert d == dict(foo=23, bar=42)
|
|
|
|
d = ImmutableMultiDict(d)
|
|
assert_raises(TypeError, d.add, 'fuss', 44)
|
|
assert_raises(TypeError, d.popitemlist)
|
|
assert_raises(TypeError, d.poplist, 'foo')
|
|
assert_raises(TypeError, d.setlist, 'tadaa', [1, 2])
|
|
assert_raises(TypeError, d.setlistdefault, 'tadaa')
|
|
|
|
d = EnvironHeaders({'HTTP_X_FOO': 'test'})
|
|
assert_raises(TypeError, d.__delitem__, 0)
|
|
assert_raises(TypeError, d.add, 42)
|
|
assert_raises(TypeError, d.pop, 'x-foo')
|
|
assert_raises(TypeError, d.popitem)
|
|
assert_raises(TypeError, d.setdefault, 'foo', 42)
|
|
assert dict(d.items()) == {'X-Foo': 'test'}
|
|
assert_raises(TypeError, d.copy)
|