/rpython/rlib/test/test_objectmodel.py
Python | 840 lines | 719 code | 115 blank | 6 comment | 56 complexity | b3c363b5883770c48a8b3972164dec35 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- from collections import OrderedDict
- import py
- from rpython.rlib.objectmodel import (
- r_dict, UnboxedValue, Symbolic, compute_hash, compute_identity_hash,
- compute_unique_id, current_object_addr_as_int, we_are_translated,
- prepare_dict_update, reversed_dict, specialize, enforceargs, newlist_hint,
- resizelist_hint, is_annotation_constant, always_inline, NOT_CONSTANT,
- iterkeys_with_hash, iteritems_with_hash, contains_with_hash,
- setitem_with_hash, getitem_with_hash, delitem_with_hash, import_from_mixin,
- fetch_translated_config)
- from rpython.translator.translator import TranslationContext, graphof
- from rpython.rtyper.test.tool import BaseRtypingTest
- from rpython.rtyper.test.test_llinterp import interpret
- from rpython.conftest import option
- def strange_key_eq(key1, key2):
- return key1[0] == key2[0] # only the 1st character is relevant
- def strange_key_hash(key):
- return ord(key[0])
- def play_with_r_dict(d):
- d['hello'] = 41
- d['hello'] = 42
- assert d['hi there'] == 42
- try:
- unexpected = d["dumb"]
- except KeyError:
- pass
- else:
- assert False, "should have raised, got %s" % unexpected
- assert len(d) == 1
- assert 'oops' not in d
- count = 0
- for x in d:
- assert x == 'hello'
- count += 1
- assert count == 1
- assert d.get('hola', -1) == 42
- assert d.get('salut', -1) == -1
- d1 = d.copy()
- del d['hu!']
- assert len(d) == 0
- assert d1.keys() == ['hello']
- d.update(d1)
- assert d.values() == [42]
- lst = d.items()
- assert len(lst) == 1 and len(lst[0]) == 2
- assert lst[0][0] == 'hello' and lst[0][1] == 42
- count = 0
- for x in d.iterkeys():
- assert x == 'hello'
- count += 1
- assert count == 1
- count = 0
- for x in d.itervalues():
- assert x == 42
- count += 1
- assert count == 1
- count = 0
- for x in d.iteritems():
- assert len(x) == 2 and x[0] == 'hello' and x[1] == 42
- count += 1
- assert count == 1
- d.clear()
- assert d.keys() == []
- return True # for the tests below
- def test_recursive_r_dict_repr():
- import operator
- rdic = r_dict(operator.eq, hash)
- rdic['x'] = rdic
- assert str(rdic) == "r_dict({'x': r_dict({...})})"
- assert repr(rdic) == "r_dict({'x': r_dict({...})})"
- def func_r_dict():
- # NB. this test function is also annotated/rtyped by the next tests
- d = r_dict(strange_key_eq, strange_key_hash)
- return play_with_r_dict(d)
- class Strange:
- def key_eq(strange, key1, key2):
- return key1[0] == key2[0] # only the 1st character is relevant
- def key_hash(strange, key):
- return ord(key[0])
- def func_r_dict_bm():
- # NB. this test function is also annotated by the next tests
- strange = Strange()
- d = r_dict(strange.key_eq, strange.key_hash)
- return play_with_r_dict(d)
- def test_annotate_r_dict():
- t = TranslationContext()
- a = t.buildannotator()
- a.build_types(func_r_dict, [])
- #t.view()
- graph = graphof(t, strange_key_eq)
- assert a.binding(graph.getargs()[0]).knowntype == str
- assert a.binding(graph.getargs()[1]).knowntype == str
- graph = graphof(t, strange_key_hash)
- assert a.binding(graph.getargs()[0]).knowntype == str
- def test_annotate_r_dict_bm():
- t = TranslationContext()
- a = t.buildannotator()
- a.build_types(func_r_dict_bm, [])
- #t.view()
- strange_key_eq = Strange.key_eq.im_func
- strange_key_hash = Strange.key_hash.im_func
- Strange_def = a.bookkeeper.getuniqueclassdef(Strange)
- graph = graphof(t, strange_key_eq)
- assert a.binding(graph.getargs()[0]).classdef == Strange_def
- assert a.binding(graph.getargs()[1]).knowntype == str
- assert a.binding(graph.getargs()[2]).knowntype == str
- graph = graphof(t, strange_key_hash)
- assert a.binding(graph.getargs()[0]).classdef == Strange_def
- assert a.binding(graph.getargs()[1]).knowntype == str
- def test_unboxed_value():
- class Base(object):
- __slots__ = ()
- class C(Base, UnboxedValue):
- __slots__ = 'smallint'
- assert C(17).smallint == 17
- assert C(17).get_untagged_value() == 17
- class A(UnboxedValue):
- __slots__ = ['value']
- assert A(12098).value == 12098
- assert A(12098).get_untagged_value() == 12098
- def test_symbolic():
- py.test.skip("xxx no test here")
- def test_symbolic_raises():
- s1 = Symbolic()
- s2 = Symbolic()
- py.test.raises(TypeError, "s1 < s2")
- py.test.raises(TypeError, "hash(s1)")
- def test_compute_hash():
- from rpython.rlib.objectmodel import _hash_string, _hash_float, _hash_tuple
- assert compute_hash("Hello") == _hash_string("Hello")
- assert compute_hash(7) == 7
- assert compute_hash(-3.5) == _hash_float(-3.5)
- assert compute_hash(None) == 0
- assert compute_hash(("world", None, 7)) == _hash_tuple(("world", None, 7))
- #
- class Foo(object):
- def __hash__(self):
- return 42
- foo = Foo()
- h = compute_hash(foo)
- assert h == object.__hash__(foo)
- assert h == getattr(foo, '__precomputed_identity_hash')
- assert compute_hash(None) == 0
- def test_compute_hash_float():
- from rpython.rlib.rfloat import INFINITY, NAN
- assert compute_hash(INFINITY) == 314159
- assert compute_hash(-INFINITY) == -271828
- assert compute_hash(NAN) == 0
- def test_compute_identity_hash():
- class Foo(object):
- def __hash__(self):
- return 42
- foo = Foo()
- h = compute_identity_hash(foo)
- assert h == object.__hash__(foo)
- assert h == getattr(foo, '__precomputed_identity_hash')
- def test_compute_unique_id():
- from rpython.rlib.rarithmetic import intmask
- class Foo(object):
- pass
- foo = Foo()
- x = compute_unique_id(foo)
- assert type(x) is int
- assert x == intmask(id(foo))
- def test_current_object_addr_as_int():
- from rpython.rlib.rarithmetic import intmask
- class Foo(object):
- pass
- foo = Foo()
- assert current_object_addr_as_int(foo) == intmask(id(foo))
- class TestObjectModel(BaseRtypingTest):
- def test_we_are_translated(self):
- assert we_are_translated() is False
- def fn():
- return we_are_translated()
- res = self.interpret(fn, [])
- assert res is True
- def test_rtype_r_dict(self):
- res = self.interpret(func_r_dict, [])
- assert res is True
- def test_rtype_r_dict_bm(self):
- res = self.interpret(func_r_dict_bm, [])
- assert res is True
- def test_rtype_constant_r_dicts(self):
- d1 = r_dict(strange_key_eq, strange_key_hash)
- d1['hello'] = 666
- d2 = r_dict(strange_key_eq, strange_key_hash)
- d2['hello'] = 777
- d2['world'] = 888
- def fn(i):
- if i == 1:
- d = d1
- else:
- d = d2
- return len(d)
- res = self.interpret(fn, [1])
- assert res == 1
- res = self.interpret(fn, [2])
- assert res == 2
- def test_rtype_r_dict_singlefrozen_func(self):
- class FreezingClass(Strange):
- def _freeze_(self):
- return True
- obj = FreezingClass()
- def fn():
- d = r_dict(obj.key_eq, obj.key_hash)
- return play_with_r_dict(d)
- assert self.interpret(fn, []) is True
- def test_rtype_r_dict_singlefrozen_func_pbc(self):
- class FreezingClass(Strange):
- def _freeze_(self):
- return True
- obj = FreezingClass()
- pbc_d = r_dict(obj.key_eq, obj.key_hash)
- def fn():
- return play_with_r_dict(pbc_d)
- assert self.interpret(fn, []) is True
- def test_rtype_r_dict_exceptions(self):
- def raising_hash(obj):
- if obj.startswith("bla"):
- raise TypeError
- return 1
- def eq(obj1, obj2):
- return obj1 is obj2
- def f():
- d1 = r_dict(eq, raising_hash)
- d1['xxx'] = 1
- try:
- x = d1["blabla"]
- except Exception:
- return 42
- return x
- res = self.interpret(f, [])
- assert res == 42
- def f():
- d1 = r_dict(eq, raising_hash)
- d1['xxx'] = 1
- try:
- x = d1["blabla"]
- except TypeError:
- return 42
- return x
- res = self.interpret(f, [])
- assert res == 42
- def f():
- d1 = r_dict(eq, raising_hash)
- d1['xxx'] = 1
- try:
- d1["blabla"] = 2
- except TypeError:
- return 42
- return 0
- res = self.interpret(f, [])
- assert res == 42
- def test_access_in_try(self):
- h = lambda x: 1
- eq = lambda x, y: x == y
- def f(d):
- try:
- return d[2]
- except ZeroDivisionError:
- return 42
- return -1
- def g(n):
- d = r_dict(eq, h)
- d[1] = n
- d[2] = 2 * n
- return f(d)
- res = self.interpret(g, [3])
- assert res == 6
- def test_access_in_try_set(self):
- h = lambda x: 1
- eq = lambda x, y: x == y
- def f(d):
- try:
- d[2] = 77
- except ZeroDivisionError:
- return 42
- return -1
- def g(n):
- d = r_dict(eq, h)
- d[1] = n
- f(d)
- return d[2]
- res = self.interpret(g, [3])
- assert res == 77
- def test_prepare_dict_update(self):
- def g(n):
- d = {}
- prepare_dict_update(d, n)
- return 42
- res = self.interpret(g, [3])
- assert res == 42 # "did not crash"
- def test_prepare_dict_update_2(self):
- def g(n):
- d = OrderedDict()
- prepare_dict_update(d, n)
- return 42
- res = self.interpret(g, [3])
- assert res == 42 # "did not crash"
- def test_reversed_dict(self):
- d1 = {2: 3, 4: 5, 6: 7}
- def g():
- n1 = 0
- for key in d1:
- n1 = n1 * 10 + key
- n2 = 0
- for key in reversed_dict(d1):
- n2 = n2 * 10 + key
- return n1 * 10000 + n2
- got = str(g())
- assert len(got) == 7 and got[3] == '0' and got[:3] == got[6:3:-1]
- got = str(self.interpret(g, []))
- assert len(got) == 7 and got[3] == '0' and got[:3] == got[6:3:-1]
- def test_compute_hash(self):
- class Foo(object):
- pass
- def f(i):
- assert compute_hash(i) == compute_hash(42)
- assert compute_hash(i + 1.0) == compute_hash(43.0)
- assert compute_hash("Hello" + str(i)) == compute_hash("Hello42")
- if i == 42:
- p = None
- else:
- p = Foo()
- assert compute_hash(p) == compute_hash(None)
- assert (compute_hash(("world", None, i, 7.5)) ==
- compute_hash(("world", None, 42, 7.5)))
- q = Foo()
- assert compute_hash(q) == compute_identity_hash(q)
- from rpython.rlib.rfloat import INFINITY, NAN
- assert compute_hash(INFINITY) == 314159
- assert compute_hash(-INFINITY) == -271828
- assert compute_hash(NAN) == 0
- return i * 2
- res = self.interpret(f, [42])
- assert res == 84
- def test_isconstant(self):
- @specialize.arg_or_var(0)
- def f(arg):
- if is_annotation_constant(arg):
- return 1
- return 10
- def fn(arg):
- return f(arg) + f(3)
- assert self.interpret(fn, [15]) == 11
- def test_rtype_keepalive(self):
- from rpython.rlib import objectmodel
- def f():
- x = [1]
- y = ['b']
- objectmodel.keepalive_until_here(x, y)
- return 1
- res = self.interpret(f, [])
- assert res == 1
- def test_compute_hash_across_translation(self):
- class Foo(object):
- pass
- q = Foo()
- def f(i):
- assert compute_hash(None) == 0
- assert compute_hash(i) == h_42
- assert compute_hash(i + 1.0) == h_43_dot_0
- assert compute_hash((i + 3) / 6.0) == h_7_dot_5
- assert compute_hash("Hello" + str(i)) == h_Hello42
- if i == 42:
- p = None
- else:
- p = Foo()
- assert compute_hash(p) == h_None
- assert compute_hash(("world", None, i, 7.5)) == h_tuple
- assert compute_hash(q) == h_q
- return i * 2
- h_42 = compute_hash(42)
- h_43_dot_0 = compute_hash(43.0)
- h_7_dot_5 = compute_hash(7.5)
- h_Hello42 = compute_hash("Hello42")
- h_None = compute_hash(None)
- h_tuple = compute_hash(("world", None, 42, 7.5))
- h_q = compute_hash(q)
- res = self.interpret(f, [42])
- assert res == 84
- def test_fetch_translated_config(self):
- assert fetch_translated_config() is None
- def f():
- return fetch_translated_config().translation.continuation
- res = self.interpret(f, [])
- assert res is False
- def test_specialize_decorator():
- def f():
- pass
- specialize.memo()(f)
- assert f._annspecialcase_ == 'specialize:memo'
- specialize.arg(0)(f)
- assert f._annspecialcase_ == 'specialize:arg(0)'
- specialize.arg(1)(f)
- assert f._annspecialcase_ == 'specialize:arg(1)'
- def test_enforceargs_decorator():
- @enforceargs(int, str, None)
- def f(a, b, c):
- return a, b, c
- f.foo = 'foo'
- assert f._annenforceargs_ == (int, str, None)
- assert f.func_name == 'f'
- assert f.foo == 'foo'
- assert f(1, 'hello', 42) == (1, 'hello', 42)
- exc = py.test.raises(TypeError, "f(1, 2, 3)")
- assert exc.value.message == "f argument 'b' must be of type <type 'str'>"
- py.test.raises(TypeError, "f('hello', 'world', 3)")
- def test_always_inline():
- @always_inline
- def f(a, b, c):
- return a, b, c
- assert f._always_inline_ is True
- def test_enforceargs_defaults():
- @enforceargs(int, int)
- def f(a, b=40):
- return a + b
- assert f(2) == 42
- def test_enforceargs_keywords():
- @enforceargs(b=int)
- def f(a, b, c):
- return a + b
- assert f._annenforceargs_ == (None, int, None)
- def test_enforceargs_int_float_promotion():
- @enforceargs(float)
- def f(x):
- return x
- # in RPython there is an implicit int->float promotion
- assert f(42) == 42
- def test_enforceargs_None_string():
- @enforceargs(str, unicode)
- def f(a, b):
- return a, b
- assert f(None, None) == (None, None)
- def test_enforceargs_complex_types():
- @enforceargs([int], {str: int})
- def f(a, b):
- return a, b
- x = [0, 1, 2]
- y = {'a': 1, 'b': 2}
- assert f(x, y) == (x, y)
- assert f([], {}) == ([], {})
- assert f(None, None) == (None, None)
- py.test.raises(TypeError, "f(['hello'], y)")
- py.test.raises(TypeError, "f(x, {'a': 'hello'})")
- py.test.raises(TypeError, "f(x, {0: 42})")
- def test_enforceargs_no_typecheck():
- @enforceargs(int, str, None, typecheck=False)
- def f(a, b, c):
- return a, b, c
- assert f._annenforceargs_ == (int, str, None)
- assert f(1, 2, 3) == (1, 2, 3) # no typecheck
- def test_enforceargs_translates():
- from rpython.rtyper.lltypesystem import lltype
- @enforceargs(int, float)
- def f(a, b):
- return a, b
- graph = getgraph(f, [int, int])
- TYPES = [v.concretetype for v in graph.getargs()]
- assert TYPES == [lltype.Signed, lltype.Float]
- def test_enforceargs_not_constant():
- from rpython.translator.translator import TranslationContext, graphof
- @enforceargs(NOT_CONSTANT)
- def f(a):
- return a
- def f42():
- return f(42)
- t = TranslationContext()
- a = t.buildannotator()
- s = a.build_types(f42, [])
- assert not hasattr(s, 'const')
- def getgraph(f, argtypes):
- from rpython.translator.translator import TranslationContext, graphof
- from rpython.translator.backendopt.all import backend_optimizations
- t = TranslationContext()
- a = t.buildannotator()
- typer = t.buildrtyper()
- a.build_types(f, argtypes)
- typer.specialize()
- backend_optimizations(t)
- graph = graphof(t, f)
- if option.view:
- graph.show()
- return graph
- def test_newlist():
- def f(z):
- x = newlist_hint(sizehint=38)
- if z < 0:
- x.append(1)
- return len(x)
- graph = getgraph(f, [int])
- for llop in graph.startblock.operations:
- if llop.opname == 'malloc_varsize':
- break
- assert llop.args[2].value == 38
- def test_newlist_nonconst():
- def f(z):
- x = newlist_hint(sizehint=z)
- return len(x)
- graph = getgraph(f, [int])
- for llop in graph.startblock.operations:
- if llop.opname == 'malloc_varsize':
- break
- assert llop.args[2] is graph.startblock.inputargs[0]
- def test_resizelist_hint():
- def f(z):
- x = []
- resizelist_hint(x, 39)
- return len(x)
- graph = getgraph(f, [int])
- for _, op in graph.iterblockops():
- if op.opname == 'direct_call':
- break
- call_name = op.args[0].value._obj.graph.name
- assert call_name.startswith('_ll_list_resize_hint')
- call_arg2 = op.args[2].value
- assert call_arg2 == 39
- def test_resizelist_hint_len():
- def f(i):
- l = [44]
- resizelist_hint(l, i)
- return len(l)
- r = interpret(f, [29])
- assert r == 1
- def test_iterkeys_with_hash():
- def f(i):
- d = {i + .0: 5, i + .5: 6}
- total = 0
- for k, h in iterkeys_with_hash(d):
- total += k * h
- total -= (i + 0.0) * compute_hash(i + 0.0)
- total -= (i + 0.5) * compute_hash(i + 0.5)
- return total
- assert f(29) == 0.0
- r = interpret(f, [29])
- assert r == 0.0
- def test_iteritems_with_hash():
- def f(i):
- d = {i + .0: 5, i + .5: 6}
- total = 0
- for k, v, h in iteritems_with_hash(d):
- total += k * h * v
- total -= (i + 0.0) * compute_hash(i + 0.0) * 5
- total -= (i + 0.5) * compute_hash(i + 0.5) * 6
- return total
- assert f(29) == 0.0
- r = interpret(f, [29])
- assert r == 0.0
- def test_contains_with_hash():
- def f(i):
- d = {i + .5: 5}
- assert contains_with_hash(d, i + .5, compute_hash(i + .5))
- assert not contains_with_hash(d, i + .3, compute_hash(i + .3))
- return 0
- f(29)
- interpret(f, [29])
- def test_setitem_with_hash():
- def f(i):
- d = {}
- setitem_with_hash(d, i + .5, compute_hash(i + .5), 42)
- setitem_with_hash(d, i + .6, compute_hash(i + .6), -612)
- return d[i + .5]
- assert f(29) == 42
- res = interpret(f, [27])
- assert res == 42
- def test_getitem_with_hash():
- def f(i):
- d = {i + .5: 42, i + .6: -612}
- return getitem_with_hash(d, i + .5, compute_hash(i + .5))
- assert f(29) == 42
- res = interpret(f, [27])
- assert res == 42
- def test_delitem_with_hash():
- def f(i):
- d = {i + .5: 42, i + .6: -612}
- delitem_with_hash(d, i + .5, compute_hash(i + .5))
- try:
- delitem_with_hash(d, i + .5, compute_hash(i + .5))
- except KeyError:
- pass
- else:
- raise AssertionError
- return 0
- f(29)
- interpret(f, [27])
- def test_rdict_with_hash():
- def f(i):
- d = r_dict(strange_key_eq, strange_key_hash)
- h = strange_key_hash("abc")
- assert h == strange_key_hash("aXX") and strange_key_eq("abc", "aXX")
- setitem_with_hash(d, "abc", h, i)
- assert getitem_with_hash(d, "aXX", h) == i
- try:
- getitem_with_hash(d, "bYY", strange_key_hash("bYY"))
- except KeyError:
- pass
- else:
- raise AssertionError
- return 0
- assert f(29) == 0
- interpret(f, [27])
- def test_import_from_mixin():
- class M: # old-style
- def f(self):
- pass
- class A: # old-style
- import_from_mixin(M)
- assert A.f.im_func is not M.f.im_func
- class M(object):
- def f(self):
- pass
- class A: # old-style
- import_from_mixin(M)
- assert A.f.im_func is not M.f.im_func
- class M: # old-style
- def f(self):
- pass
- class A(object):
- import_from_mixin(M)
- assert A.f.im_func is not M.f.im_func
- class M(object):
- def f(self):
- pass
- class A(object):
- import_from_mixin(M)
- assert A.f.im_func is not M.f.im_func
- class MBase(object):
- a = 42
- b = 43
- c = 1000
- def f(self):
- return "hi"
- def g(self):
- return self.c - 1
- class M(MBase):
- a = 84
- def f(self):
- return "there"
- class A(object):
- import_from_mixin(M)
- c = 88
- assert A.f.im_func is not M.f.im_func
- assert A.f.im_func is not MBase.f.im_func
- assert A.g.im_func is not MBase.g.im_func
- assert A().f() == "there"
- assert A.a == 84
- assert A.b == 43
- assert A.c == 88
- assert A().g() == 87
- try:
- class B(object):
- a = 63
- import_from_mixin(M)
- except Exception as e:
- assert ("would overwrite the value already defined locally for 'a'"
- in str(e))
- else:
- raise AssertionError("failed to detect overwritten attribute")
- class M(object):
- def __str__(self):
- return "m!"
- class A(object):
- import_from_mixin(M)
- class B(object):
- import_from_mixin(M, special_methods=['__str__'])
- assert str(A()).startswith('<')
- assert str(B()) == "m!"
- class M(object):
- pass
- class A(object):
- def __init__(self):
- self.foo = 42
- class B(A):
- import_from_mixin(M)
- assert B().foo == 42
- d = dict(__name__='foo')
- exec """class M(object):
- @staticmethod
- def f(): pass
- """ in d
- M = d['M']
- class A(object):
- import_from_mixin(M)
- assert A.f is not M.f
- assert A.f.__module__ != M.f.__module__
- def test_import_from_mixin_immutable_fields():
- class A(object):
- _immutable_fields_ = ['a']
- class B(object):
- _immutable_fields_ = ['b']
- import_from_mixin(A)
- assert B._immutable_fields_ == ['b', 'a']
- assert A._immutable_fields_ == ['a']
- class B(object):
- import_from_mixin(A)
- assert B._immutable_fields_ == ['a']
- class C(A):
- _immutable_fields_ = ['c']
- class B(object):
- import_from_mixin(C)
- assert B._immutable_fields_ == ['c', 'a']
- class B(object):
- _immutable_fields_ = ['b']
- import_from_mixin(C)
- assert B._immutable_fields_ == ['b', 'c', 'a']
- class B(object):
- _immutable_fields_ = ['b']
- class BA(B):
- import_from_mixin(C)
- assert BA._immutable_fields_ == ['c', 'a']