mirror of
https://github.com/python/cpython.git
synced 2024-11-25 19:03:49 +08:00
3a652b1d0a
svn+ssh://pythondev@svn.python.org/python/trunk ........ r70546 | antoine.pitrou | 2009-03-23 19:41:45 +0100 (lun., 23 mars 2009) | 9 lines Issue #4688: Add a heuristic so that tuples and dicts containing only untrackable objects are not tracked by the garbage collector. This can reduce the size of collections and therefore the garbage collection overhead on long-running programs, depending on their particular use of datatypes. (trivia: this makes the "binary_trees" benchmark from the Computer Language Shootout 40% faster) ........
787 lines
24 KiB
Python
787 lines
24 KiB
Python
import unittest
|
|
from test import support
|
|
|
|
import sys, collections, random, string
|
|
import gc, weakref
|
|
|
|
|
|
class DictTest(unittest.TestCase):
|
|
|
|
def test_constructor(self):
|
|
# calling built-in types without argument must return empty
|
|
self.assertEqual(dict(), {})
|
|
self.assert_(dict() is not {})
|
|
|
|
def test_literal_constructor(self):
|
|
# check literal constructor for different sized dicts (to exercise the BUILD_MAP oparg
|
|
for n in (0, 1, 6, 256, 400):
|
|
items = [(''.join([random.choice(string.ascii_letters)
|
|
for j in range(8)]),
|
|
i)
|
|
for i in range(n)]
|
|
random.shuffle(items)
|
|
dictliteral = '{' + ', '.join('%r: %d' % item for item in items) + '}'
|
|
self.assertEqual(eval(dictliteral), dict(items))
|
|
|
|
def test_bool(self):
|
|
self.assert_(not {})
|
|
self.assert_({1: 2})
|
|
self.assert_(bool({}) is False)
|
|
self.assert_(bool({1: 2}) is True)
|
|
|
|
def test_keys(self):
|
|
d = {}
|
|
self.assertEqual(set(d.keys()), set())
|
|
d = {'a': 1, 'b': 2}
|
|
k = d.keys()
|
|
self.assert_('a' in d)
|
|
self.assert_('b' in d)
|
|
self.assertRaises(TypeError, d.keys, None)
|
|
self.assertEqual(repr(dict(a=1).keys()), "dict_keys(['a'])")
|
|
|
|
def test_values(self):
|
|
d = {}
|
|
self.assertEqual(set(d.values()), set())
|
|
d = {1:2}
|
|
self.assertEqual(set(d.values()), {2})
|
|
self.assertRaises(TypeError, d.values, None)
|
|
self.assertEqual(repr(dict(a=1).values()), "dict_values([1])")
|
|
|
|
def test_items(self):
|
|
d = {}
|
|
self.assertEqual(set(d.items()), set())
|
|
|
|
d = {1:2}
|
|
self.assertEqual(set(d.items()), {(1, 2)})
|
|
self.assertRaises(TypeError, d.items, None)
|
|
self.assertEqual(repr(dict(a=1).items()), "dict_items([('a', 1)])")
|
|
|
|
def test_contains(self):
|
|
d = {}
|
|
self.assert_(not ('a' in d))
|
|
self.assert_('a' not in d)
|
|
d = {'a': 1, 'b': 2}
|
|
self.assert_('a' in d)
|
|
self.assert_('b' in d)
|
|
self.assert_('c' not in d)
|
|
|
|
self.assertRaises(TypeError, d.__contains__)
|
|
|
|
def test_len(self):
|
|
d = {}
|
|
self.assertEqual(len(d), 0)
|
|
d = {'a': 1, 'b': 2}
|
|
self.assertEqual(len(d), 2)
|
|
|
|
def test_getitem(self):
|
|
d = {'a': 1, 'b': 2}
|
|
self.assertEqual(d['a'], 1)
|
|
self.assertEqual(d['b'], 2)
|
|
d['c'] = 3
|
|
d['a'] = 4
|
|
self.assertEqual(d['c'], 3)
|
|
self.assertEqual(d['a'], 4)
|
|
del d['b']
|
|
self.assertEqual(d, {'a': 4, 'c': 3})
|
|
|
|
self.assertRaises(TypeError, d.__getitem__)
|
|
|
|
class BadEq(object):
|
|
def __eq__(self, other):
|
|
raise Exc()
|
|
def __hash__(self):
|
|
return 24
|
|
|
|
d = {}
|
|
d[BadEq()] = 42
|
|
self.assertRaises(KeyError, d.__getitem__, 23)
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class BadHash(object):
|
|
fail = False
|
|
def __hash__(self):
|
|
if self.fail:
|
|
raise Exc()
|
|
else:
|
|
return 42
|
|
|
|
x = BadHash()
|
|
d[x] = 42
|
|
x.fail = True
|
|
self.assertRaises(Exc, d.__getitem__, x)
|
|
|
|
def test_clear(self):
|
|
d = {1:1, 2:2, 3:3}
|
|
d.clear()
|
|
self.assertEqual(d, {})
|
|
|
|
self.assertRaises(TypeError, d.clear, None)
|
|
|
|
def test_update(self):
|
|
d = {}
|
|
d.update({1:100})
|
|
d.update({2:20})
|
|
d.update({1:1, 2:2, 3:3})
|
|
self.assertEqual(d, {1:1, 2:2, 3:3})
|
|
|
|
d.update()
|
|
self.assertEqual(d, {1:1, 2:2, 3:3})
|
|
|
|
self.assertRaises((TypeError, AttributeError), d.update, None)
|
|
|
|
class SimpleUserDict:
|
|
def __init__(self):
|
|
self.d = {1:1, 2:2, 3:3}
|
|
def keys(self):
|
|
return self.d.keys()
|
|
def __getitem__(self, i):
|
|
return self.d[i]
|
|
d.clear()
|
|
d.update(SimpleUserDict())
|
|
self.assertEqual(d, {1:1, 2:2, 3:3})
|
|
|
|
class Exc(Exception): pass
|
|
|
|
d.clear()
|
|
class FailingUserDict:
|
|
def keys(self):
|
|
raise Exc
|
|
self.assertRaises(Exc, d.update, FailingUserDict())
|
|
|
|
class FailingUserDict:
|
|
def keys(self):
|
|
class BogonIter:
|
|
def __init__(self):
|
|
self.i = 1
|
|
def __iter__(self):
|
|
return self
|
|
def __next__(self):
|
|
if self.i:
|
|
self.i = 0
|
|
return 'a'
|
|
raise Exc
|
|
return BogonIter()
|
|
def __getitem__(self, key):
|
|
return key
|
|
self.assertRaises(Exc, d.update, FailingUserDict())
|
|
|
|
class FailingUserDict:
|
|
def keys(self):
|
|
class BogonIter:
|
|
def __init__(self):
|
|
self.i = ord('a')
|
|
def __iter__(self):
|
|
return self
|
|
def __next__(self):
|
|
if self.i <= ord('z'):
|
|
rtn = chr(self.i)
|
|
self.i += 1
|
|
return rtn
|
|
raise StopIteration
|
|
return BogonIter()
|
|
def __getitem__(self, key):
|
|
raise Exc
|
|
self.assertRaises(Exc, d.update, FailingUserDict())
|
|
|
|
class badseq(object):
|
|
def __iter__(self):
|
|
return self
|
|
def __next__(self):
|
|
raise Exc()
|
|
|
|
self.assertRaises(Exc, {}.update, badseq())
|
|
|
|
self.assertRaises(ValueError, {}.update, [(1, 2, 3)])
|
|
|
|
def test_fromkeys(self):
|
|
self.assertEqual(dict.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
|
|
d = {}
|
|
self.assert_(not(d.fromkeys('abc') is d))
|
|
self.assertEqual(d.fromkeys('abc'), {'a':None, 'b':None, 'c':None})
|
|
self.assertEqual(d.fromkeys((4,5),0), {4:0, 5:0})
|
|
self.assertEqual(d.fromkeys([]), {})
|
|
def g():
|
|
yield 1
|
|
self.assertEqual(d.fromkeys(g()), {1:None})
|
|
self.assertRaises(TypeError, {}.fromkeys, 3)
|
|
class dictlike(dict): pass
|
|
self.assertEqual(dictlike.fromkeys('a'), {'a':None})
|
|
self.assertEqual(dictlike().fromkeys('a'), {'a':None})
|
|
self.assert_(type(dictlike.fromkeys('a')) is dictlike)
|
|
self.assert_(type(dictlike().fromkeys('a')) is dictlike)
|
|
class mydict(dict):
|
|
def __new__(cls):
|
|
return collections.UserDict()
|
|
ud = mydict.fromkeys('ab')
|
|
self.assertEqual(ud, {'a':None, 'b':None})
|
|
self.assert_(isinstance(ud, collections.UserDict))
|
|
self.assertRaises(TypeError, dict.fromkeys)
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class baddict1(dict):
|
|
def __init__(self):
|
|
raise Exc()
|
|
|
|
self.assertRaises(Exc, baddict1.fromkeys, [1])
|
|
|
|
class BadSeq(object):
|
|
def __iter__(self):
|
|
return self
|
|
def __next__(self):
|
|
raise Exc()
|
|
|
|
self.assertRaises(Exc, dict.fromkeys, BadSeq())
|
|
|
|
class baddict2(dict):
|
|
def __setitem__(self, key, value):
|
|
raise Exc()
|
|
|
|
self.assertRaises(Exc, baddict2.fromkeys, [1])
|
|
|
|
# test fast path for dictionary inputs
|
|
d = dict(zip(range(6), range(6)))
|
|
self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
|
|
|
|
def test_copy(self):
|
|
d = {1:1, 2:2, 3:3}
|
|
self.assertEqual(d.copy(), {1:1, 2:2, 3:3})
|
|
self.assertEqual({}.copy(), {})
|
|
self.assertRaises(TypeError, d.copy, None)
|
|
|
|
def test_get(self):
|
|
d = {}
|
|
self.assert_(d.get('c') is None)
|
|
self.assertEqual(d.get('c', 3), 3)
|
|
d = {'a' : 1, 'b' : 2}
|
|
self.assert_(d.get('c') is None)
|
|
self.assertEqual(d.get('c', 3), 3)
|
|
self.assertEqual(d.get('a'), 1)
|
|
self.assertEqual(d.get('a', 3), 1)
|
|
self.assertRaises(TypeError, d.get)
|
|
self.assertRaises(TypeError, d.get, None, None, None)
|
|
|
|
def test_setdefault(self):
|
|
# dict.setdefault()
|
|
d = {}
|
|
self.assert_(d.setdefault('key0') is None)
|
|
d.setdefault('key0', [])
|
|
self.assert_(d.setdefault('key0') is None)
|
|
d.setdefault('key', []).append(3)
|
|
self.assertEqual(d['key'][0], 3)
|
|
d.setdefault('key', []).append(4)
|
|
self.assertEqual(len(d['key']), 2)
|
|
self.assertRaises(TypeError, d.setdefault)
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class BadHash(object):
|
|
fail = False
|
|
def __hash__(self):
|
|
if self.fail:
|
|
raise Exc()
|
|
else:
|
|
return 42
|
|
|
|
x = BadHash()
|
|
d[x] = 42
|
|
x.fail = True
|
|
self.assertRaises(Exc, d.setdefault, x, [])
|
|
|
|
def test_popitem(self):
|
|
# dict.popitem()
|
|
for copymode in -1, +1:
|
|
# -1: b has same structure as a
|
|
# +1: b is a.copy()
|
|
for log2size in range(12):
|
|
size = 2**log2size
|
|
a = {}
|
|
b = {}
|
|
for i in range(size):
|
|
a[repr(i)] = i
|
|
if copymode < 0:
|
|
b[repr(i)] = i
|
|
if copymode > 0:
|
|
b = a.copy()
|
|
for i in range(size):
|
|
ka, va = ta = a.popitem()
|
|
self.assertEqual(va, int(ka))
|
|
kb, vb = tb = b.popitem()
|
|
self.assertEqual(vb, int(kb))
|
|
self.assert_(not(copymode < 0 and ta != tb))
|
|
self.assert_(not a)
|
|
self.assert_(not b)
|
|
|
|
d = {}
|
|
self.assertRaises(KeyError, d.popitem)
|
|
|
|
def test_pop(self):
|
|
# Tests for pop with specified key
|
|
d = {}
|
|
k, v = 'abc', 'def'
|
|
d[k] = v
|
|
self.assertRaises(KeyError, d.pop, 'ghi')
|
|
|
|
self.assertEqual(d.pop(k), v)
|
|
self.assertEqual(len(d), 0)
|
|
|
|
self.assertRaises(KeyError, d.pop, k)
|
|
|
|
# verify longs/ints get same value when key > 32 bits (for 64-bit archs)
|
|
# see SF bug #689659
|
|
x = 4503599627370496
|
|
y = 4503599627370496
|
|
h = {x: 'anything', y: 'something else'}
|
|
self.assertEqual(h[x], h[y])
|
|
|
|
self.assertEqual(d.pop(k, v), v)
|
|
d[k] = v
|
|
self.assertEqual(d.pop(k, 1), v)
|
|
|
|
self.assertRaises(TypeError, d.pop)
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class BadHash(object):
|
|
fail = False
|
|
def __hash__(self):
|
|
if self.fail:
|
|
raise Exc()
|
|
else:
|
|
return 42
|
|
|
|
x = BadHash()
|
|
d[x] = 42
|
|
x.fail = True
|
|
self.assertRaises(Exc, d.pop, x)
|
|
|
|
def test_mutatingiteration(self):
|
|
d = {}
|
|
d[1] = 1
|
|
try:
|
|
for i in d:
|
|
d[i+1] = 1
|
|
except RuntimeError:
|
|
pass
|
|
else:
|
|
self.fail("changing dict size during iteration doesn't raise Error")
|
|
|
|
def test_repr(self):
|
|
d = {}
|
|
self.assertEqual(repr(d), '{}')
|
|
d[1] = 2
|
|
self.assertEqual(repr(d), '{1: 2}')
|
|
d = {}
|
|
d[1] = d
|
|
self.assertEqual(repr(d), '{1: {...}}')
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class BadRepr(object):
|
|
def __repr__(self):
|
|
raise Exc()
|
|
|
|
d = {1: BadRepr()}
|
|
self.assertRaises(Exc, repr, d)
|
|
|
|
def test_eq(self):
|
|
self.assertEqual({}, {})
|
|
self.assertEqual({1: 2}, {1: 2})
|
|
|
|
class Exc(Exception): pass
|
|
|
|
class BadCmp(object):
|
|
def __eq__(self, other):
|
|
raise Exc()
|
|
def __hash__(self):
|
|
return 1
|
|
|
|
d1 = {BadCmp(): 1}
|
|
d2 = {1: 1}
|
|
try:
|
|
d1 == d2
|
|
except Exc:
|
|
pass
|
|
else:
|
|
self.fail("< didn't raise Exc")
|
|
|
|
def test_keys_contained(self):
|
|
self.helper_keys_contained(lambda x: x.keys())
|
|
self.helper_keys_contained(lambda x: x.items())
|
|
|
|
def helper_keys_contained(self, fn):
|
|
# Test rich comparisons against dict key views, which should behave the
|
|
# same as sets.
|
|
empty = fn(dict())
|
|
empty2 = fn(dict())
|
|
smaller = fn({1:1, 2:2})
|
|
larger = fn({1:1, 2:2, 3:3})
|
|
larger2 = fn({1:1, 2:2, 3:3})
|
|
larger3 = fn({4:1, 2:2, 3:3})
|
|
|
|
self.assertTrue(smaller < larger)
|
|
self.assertTrue(smaller <= larger)
|
|
self.assertTrue(larger > smaller)
|
|
self.assertTrue(larger >= smaller)
|
|
|
|
self.assertFalse(smaller >= larger)
|
|
self.assertFalse(smaller > larger)
|
|
self.assertFalse(larger <= smaller)
|
|
self.assertFalse(larger < smaller)
|
|
|
|
self.assertFalse(smaller < larger3)
|
|
self.assertFalse(smaller <= larger3)
|
|
self.assertFalse(larger3 > smaller)
|
|
self.assertFalse(larger3 >= smaller)
|
|
|
|
# Inequality strictness
|
|
self.assertTrue(larger2 >= larger)
|
|
self.assertTrue(larger2 <= larger)
|
|
self.assertFalse(larger2 > larger)
|
|
self.assertFalse(larger2 < larger)
|
|
|
|
self.assertTrue(larger == larger2)
|
|
self.assertTrue(smaller != larger)
|
|
|
|
# There is an optimization on the zero-element case.
|
|
self.assertTrue(empty == empty2)
|
|
self.assertFalse(empty != empty2)
|
|
self.assertFalse(empty == smaller)
|
|
self.assertTrue(empty != smaller)
|
|
|
|
# With the same size, an elementwise compare happens
|
|
self.assertTrue(larger != larger3)
|
|
self.assertFalse(larger == larger3)
|
|
|
|
def test_errors_in_view_containment_check(self):
|
|
class C:
|
|
def __eq__(self, other):
|
|
raise RuntimeError
|
|
d1 = {1: C()}
|
|
d2 = {1: C()}
|
|
self.assertRaises(RuntimeError, lambda: d1.items() == d2.items())
|
|
self.assertRaises(RuntimeError, lambda: d1.items() != d2.items())
|
|
self.assertRaises(RuntimeError, lambda: d1.items() <= d2.items())
|
|
self.assertRaises(RuntimeError, lambda: d1.items() >= d2.items())
|
|
d3 = {1: C(), 2: C()}
|
|
self.assertRaises(RuntimeError, lambda: d2.items() < d3.items())
|
|
self.assertRaises(RuntimeError, lambda: d3.items() > d2.items())
|
|
|
|
def test_dictview_set_operations_on_keys(self):
|
|
k1 = {1:1, 2:2}.keys()
|
|
k2 = {1:1, 2:2, 3:3}.keys()
|
|
k3 = {4:4}.keys()
|
|
|
|
self.assertEquals(k1 - k2, set())
|
|
self.assertEquals(k1 - k3, {1,2})
|
|
self.assertEquals(k2 - k1, {3})
|
|
self.assertEquals(k3 - k1, {4})
|
|
self.assertEquals(k1 & k2, {1,2})
|
|
self.assertEquals(k1 & k3, set())
|
|
self.assertEquals(k1 | k2, {1,2,3})
|
|
self.assertEquals(k1 ^ k2, {3})
|
|
self.assertEquals(k1 ^ k3, {1,2,4})
|
|
|
|
def test_dictview_set_operations_on_items(self):
|
|
k1 = {1:1, 2:2}.items()
|
|
k2 = {1:1, 2:2, 3:3}.items()
|
|
k3 = {4:4}.items()
|
|
|
|
self.assertEquals(k1 - k2, set())
|
|
self.assertEquals(k1 - k3, {(1,1), (2,2)})
|
|
self.assertEquals(k2 - k1, {(3,3)})
|
|
self.assertEquals(k3 - k1, {(4,4)})
|
|
self.assertEquals(k1 & k2, {(1,1), (2,2)})
|
|
self.assertEquals(k1 & k3, set())
|
|
self.assertEquals(k1 | k2, {(1,1), (2,2), (3,3)})
|
|
self.assertEquals(k1 ^ k2, {(3,3)})
|
|
self.assertEquals(k1 ^ k3, {(1,1), (2,2), (4,4)})
|
|
|
|
def test_dictview_mixed_set_operations(self):
|
|
# Just a few for .keys()
|
|
self.assertTrue({1:1}.keys() == {1})
|
|
self.assertTrue({1} == {1:1}.keys())
|
|
self.assertEquals({1:1}.keys() | {2}, {1, 2})
|
|
self.assertEquals({2} | {1:1}.keys(), {1, 2})
|
|
# And a few for .items()
|
|
self.assertTrue({1:1}.items() == {(1,1)})
|
|
self.assertTrue({(1,1)} == {1:1}.items())
|
|
self.assertEquals({1:1}.items() | {2}, {(1,1), 2})
|
|
self.assertEquals({2} | {1:1}.items(), {(1,1), 2})
|
|
|
|
def test_missing(self):
|
|
# Make sure dict doesn't have a __missing__ method
|
|
self.assertEqual(hasattr(dict, "__missing__"), False)
|
|
self.assertEqual(hasattr({}, "__missing__"), False)
|
|
# Test several cases:
|
|
# (D) subclass defines __missing__ method returning a value
|
|
# (E) subclass defines __missing__ method raising RuntimeError
|
|
# (F) subclass sets __missing__ instance variable (no effect)
|
|
# (G) subclass doesn't define __missing__ at a all
|
|
class D(dict):
|
|
def __missing__(self, key):
|
|
return 42
|
|
d = D({1: 2, 3: 4})
|
|
self.assertEqual(d[1], 2)
|
|
self.assertEqual(d[3], 4)
|
|
self.assert_(2 not in d)
|
|
self.assert_(2 not in d.keys())
|
|
self.assertEqual(d[2], 42)
|
|
class E(dict):
|
|
def __missing__(self, key):
|
|
raise RuntimeError(key)
|
|
e = E()
|
|
try:
|
|
e[42]
|
|
except RuntimeError as err:
|
|
self.assertEqual(err.args, (42,))
|
|
else:
|
|
self.fail("e[42] didn't raise RuntimeError")
|
|
class F(dict):
|
|
def __init__(self):
|
|
# An instance variable __missing__ should have no effect
|
|
self.__missing__ = lambda key: None
|
|
f = F()
|
|
try:
|
|
f[42]
|
|
except KeyError as err:
|
|
self.assertEqual(err.args, (42,))
|
|
else:
|
|
self.fail("f[42] didn't raise KeyError")
|
|
class G(dict):
|
|
pass
|
|
g = G()
|
|
try:
|
|
g[42]
|
|
except KeyError as err:
|
|
self.assertEqual(err.args, (42,))
|
|
else:
|
|
self.fail("g[42] didn't raise KeyError")
|
|
|
|
def test_tuple_keyerror(self):
|
|
# SF #1576657
|
|
d = {}
|
|
try:
|
|
d[(1,)]
|
|
except KeyError as e:
|
|
self.assertEqual(e.args, ((1,),))
|
|
else:
|
|
self.fail("missing KeyError")
|
|
|
|
def test_bad_key(self):
|
|
# Dictionary lookups should fail if __eq__() raises an exception.
|
|
class CustomException(Exception):
|
|
pass
|
|
|
|
class BadDictKey:
|
|
def __hash__(self):
|
|
return hash(self.__class__)
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, self.__class__):
|
|
raise CustomException
|
|
return other
|
|
|
|
d = {}
|
|
x1 = BadDictKey()
|
|
x2 = BadDictKey()
|
|
d[x1] = 1
|
|
for stmt in ['d[x2] = 2',
|
|
'z = d[x2]',
|
|
'x2 in d',
|
|
'd.get(x2)',
|
|
'd.setdefault(x2, 42)',
|
|
'd.pop(x2)',
|
|
'd.update({x2: 2})']:
|
|
try:
|
|
exec(stmt, locals())
|
|
except CustomException:
|
|
pass
|
|
else:
|
|
self.fail("Statement %r didn't raise exception" % stmt)
|
|
|
|
def test_resize1(self):
|
|
# Dict resizing bug, found by Jack Jansen in 2.2 CVS development.
|
|
# This version got an assert failure in debug build, infinite loop in
|
|
# release build. Unfortunately, provoking this kind of stuff requires
|
|
# a mix of inserts and deletes hitting exactly the right hash codes in
|
|
# exactly the right order, and I can't think of a randomized approach
|
|
# that would be *likely* to hit a failing case in reasonable time.
|
|
|
|
d = {}
|
|
for i in range(5):
|
|
d[i] = i
|
|
for i in range(5):
|
|
del d[i]
|
|
for i in range(5, 9): # i==8 was the problem
|
|
d[i] = i
|
|
|
|
def test_resize2(self):
|
|
# Another dict resizing bug (SF bug #1456209).
|
|
# This caused Segmentation faults or Illegal instructions.
|
|
|
|
class X(object):
|
|
def __hash__(self):
|
|
return 5
|
|
def __eq__(self, other):
|
|
if resizing:
|
|
d.clear()
|
|
return False
|
|
d = {}
|
|
resizing = False
|
|
d[X()] = 1
|
|
d[X()] = 2
|
|
d[X()] = 3
|
|
d[X()] = 4
|
|
d[X()] = 5
|
|
# now trigger a resize
|
|
resizing = True
|
|
d[9] = 6
|
|
|
|
def test_empty_presized_dict_in_freelist(self):
|
|
# Bug #3537: if an empty but presized dict with a size larger
|
|
# than 7 was in the freelist, it triggered an assertion failure
|
|
try:
|
|
d = {'a': 1/0, 'b': None, 'c': None, 'd': None, 'e': None,
|
|
'f': None, 'g': None, 'h': None}
|
|
except ZeroDivisionError:
|
|
pass
|
|
d = {}
|
|
|
|
def test_container_iterator(self):
|
|
# Bug #3680: tp_traverse was not implemented for dictiter and
|
|
# dictview objects.
|
|
class C(object):
|
|
pass
|
|
views = (dict.items, dict.values, dict.keys)
|
|
for v in views:
|
|
obj = C()
|
|
ref = weakref.ref(obj)
|
|
container = {obj: 1}
|
|
obj.v = v(container)
|
|
obj.x = iter(obj.v)
|
|
del obj, container
|
|
gc.collect()
|
|
self.assert_(ref() is None, "Cycle was not collected")
|
|
|
|
def _not_tracked(self, t):
|
|
# Nested containers can take several collections to untrack
|
|
gc.collect()
|
|
gc.collect()
|
|
self.assertFalse(gc.is_tracked(t), t)
|
|
|
|
def _tracked(self, t):
|
|
self.assertTrue(gc.is_tracked(t), t)
|
|
gc.collect()
|
|
gc.collect()
|
|
self.assertTrue(gc.is_tracked(t), t)
|
|
|
|
def test_track_literals(self):
|
|
# Test GC-optimization of dict literals
|
|
x, y, z, w = 1.5, "a", (1, None), []
|
|
|
|
self._not_tracked({})
|
|
self._not_tracked({x:(), y:x, z:1})
|
|
self._not_tracked({1: "a", "b": 2})
|
|
self._not_tracked({1: 2, (None, True, False, ()): int})
|
|
self._not_tracked({1: object()})
|
|
|
|
# Dicts with mutable elements are always tracked, even if those
|
|
# elements are not tracked right now.
|
|
self._tracked({1: []})
|
|
self._tracked({1: ([],)})
|
|
self._tracked({1: {}})
|
|
self._tracked({1: set()})
|
|
|
|
def test_track_dynamic(self):
|
|
# Test GC-optimization of dynamically-created dicts
|
|
class MyObject(object):
|
|
pass
|
|
x, y, z, w, o = 1.5, "a", (1, object()), [], MyObject()
|
|
|
|
d = dict()
|
|
self._not_tracked(d)
|
|
d[1] = "a"
|
|
self._not_tracked(d)
|
|
d[y] = 2
|
|
self._not_tracked(d)
|
|
d[z] = 3
|
|
self._not_tracked(d)
|
|
self._not_tracked(d.copy())
|
|
d[4] = w
|
|
self._tracked(d)
|
|
self._tracked(d.copy())
|
|
d[4] = None
|
|
self._not_tracked(d)
|
|
self._not_tracked(d.copy())
|
|
|
|
# dd isn't tracked right now, but it may mutate and therefore d
|
|
# which contains it must be tracked.
|
|
d = dict()
|
|
dd = dict()
|
|
d[1] = dd
|
|
self._not_tracked(dd)
|
|
self._tracked(d)
|
|
dd[1] = d
|
|
self._tracked(dd)
|
|
|
|
d = dict.fromkeys([x, y, z])
|
|
self._not_tracked(d)
|
|
dd = dict()
|
|
dd.update(d)
|
|
self._not_tracked(dd)
|
|
d = dict.fromkeys([x, y, z, o])
|
|
self._tracked(d)
|
|
dd = dict()
|
|
dd.update(d)
|
|
self._tracked(dd)
|
|
|
|
d = dict(x=x, y=y, z=z)
|
|
self._not_tracked(d)
|
|
d = dict(x=x, y=y, z=z, w=w)
|
|
self._tracked(d)
|
|
d = dict()
|
|
d.update(x=x, y=y, z=z)
|
|
self._not_tracked(d)
|
|
d.update(w=w)
|
|
self._tracked(d)
|
|
|
|
d = dict([(x, y), (z, 1)])
|
|
self._not_tracked(d)
|
|
d = dict([(x, y), (z, w)])
|
|
self._tracked(d)
|
|
d = dict()
|
|
d.update([(x, y), (z, 1)])
|
|
self._not_tracked(d)
|
|
d.update([(x, y), (z, w)])
|
|
self._tracked(d)
|
|
|
|
def test_track_subtypes(self):
|
|
# Dict subtypes are always tracked
|
|
class MyDict(dict):
|
|
pass
|
|
self._tracked(MyDict())
|
|
|
|
|
|
from test import mapping_tests
|
|
|
|
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
|
|
type2test = dict
|
|
|
|
class Dict(dict):
|
|
pass
|
|
|
|
class SubclassMappingTests(mapping_tests.BasicTestMappingProtocol):
|
|
type2test = Dict
|
|
|
|
def test_main():
|
|
support.run_unittest(
|
|
DictTest,
|
|
GeneralMappingTests,
|
|
SubclassMappingTests,
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
test_main()
|