def test_start_end_block_inverse_cfg(): """Test _start_block and _end_block (inverse config).""" o = Dummy6(1, 2, 3, 4, 5, 6) p = ObjectPattern(PD6, config=CONFIG_INVERSE) print(p.config) m = p.match(o) a = 5 # existing, modified b = 6 # existing, deleted c = 7 # existing, unchanged with DummyContext() if sys.implementation.name == 'pypy' else pytest.warns(UserWarning): with m: # pylint: disable=used-before-assignment assert (a, b, c, d, e, f) == (1, 2, 3, 4, 5, 6) a = 6 del b d = 1 del e assert a == 6 with pytest.raises(NameError): print(b) assert c == 3 if sys.implementation.name == 'pypy': with pytest.raises(NameError): print(d) else: assert d == 1 assert e == 5
def test_object_pattern_basic(): """Test ObjectPattern (basic use cases).""" pattern = ObjectPattern({ 'obj': { 'eval': [lambda o: callable(o) or o is None] }, 'obj.__class__': { 'eval': [lambda o: isinstance(o, type)], 'bind': {'obj_type': None, 'obj_type_str': str} }, 'obj.__doc__': { 'bind': {'docstring': None} } }) m1 = pattern.match(None) m2 = pattern.match(ObjectPattern) m3 = pattern.match(set()) assert isinstance(m1, ObjectPatternMatch) assert isinstance(m2, ObjectPatternMatch) assert m3 is None assert bool(m1) assert bool(m2) with m1: assert 'obj_type' in globals() assert obj_type == type(None) assert isinstance(obj_type_str, str) assert docstring == None.__doc__ with m2: assert obj_type == type assert isinstance(obj_type_str, str) assert docstring == ObjectPattern.__doc__
def test_str_repr(): p = ObjectPattern({}) p2 = ObjectPattern({'obj': {'eval': [callable]}}) assert str(p) == '<ObjectPattern \n {}\n/>' assert repr(p) == '<ObjectPattern({}) />' m = p.match(None) assert str(m) == '<ObjectPatternMatch bindings={}/>' assert repr(m) == 'ObjectPatternMatch(None, <ObjectPattern({}) />, {})'
def test_basic_object_multi_pattern(): """Test ObjectMultiPattern.""" p1 = ObjectPattern({ 'obj': { 'eval': [ lambda o: isinstance(o, dict), lambda o: (all(isinstance(x, int) for x in o.keys()) and all(isinstance(x, str) for x in o.values())), ], 'bind': { 'keys': lambda o: list(o.keys()), 'values': lambda o: list(o.values()) } } }) p2 = ObjectPattern({ 'obj': { 'eval': [ lambda o: isinstance(o, (list, tuple)), lambda o: all(isinstance(x, tuple) and len(x) == 2 for x in o), lambda o: all(isinstance(x[0], int) for x in o), lambda o: all(isinstance(x[1], str) for x in o) ], 'bind': { 'keys': lambda o: [x[0] for x in o], 'values': lambda o: [x[1] for x in o] } } }) o1 = [(0, 'zero'), (1, 'one'), (2, 'two')] o2 = tuple(o1) o3 = dict(o1) for obj in (o1, o2, o3): with ObjectMultiPattern(obj, p1, p2): assert keys == [0, 1, 2] assert values == ['zero', 'one', 'two'] p3 = ObjectPattern({'obj': {'eval': [lambda o: isinstance(o, list)]}}) with pytest.warns(UserWarning) as e: with ObjectMultiPattern(o1, p1, p2, p3, allow_ambiguities=True): pass assert e[0].message.args[0] == 'Ambiguity: 2 patterns matched!' with pytest.raises(AmbiguityError) as e: with ObjectMultiPattern(o1, p1, p2, p3, allow_ambiguities=False): pass assert e.value.args[0] == f'{o1!r} matched 2 patterns!' with pytest.raises(NoMatchingPatternError) as e: with ObjectMultiPattern(None, p1, p2, p3): pass assert e.value.args[0] == f'{None!r} did not match any pattern!'
def test_start_end_block_deref(): o = Dummy6(1, 2, 3, 4, 5, 6) p = ObjectPattern(PD6, config=CONFIG_DEFAULT) m = p.match(o) a, b, c = 5, 6, 7 def closure(): nonlocal b with m: assert (a, b, c) == (1, 2, 3) b = 42 return a closure() assert (a, b, c) == (5, 42, 7) assert (d, e, f) == (4, 5, 6)
def test_start_end_block_closure(): o = Dummy6(1, 2, 3, 4, 5, 6) p = ObjectPattern(PD6, config=CONFIG_DEFAULT) m = p.match(o) a = 5 b = 6 c = 7 with m: # pylint: disable=used-before-assignment def myfunc(): return a, b, c, d, e, f tup1 = myfunc() assert tup1 == (1, 2, 3, 4, 5, 6) tup2 = myfunc() assert tup2 == (5, 6, 7, 4, 5, 6)
def test_context_handler(): p = ObjectPattern({ 'obj.keys': {'eval': [lambda o: all(isinstance(x, int) for x in o())], 'bind': {'keys': lambda o: o()}}, 'obj.values': {'bind': {'values': lambda o: o()}}, 'obj.items': {'bind': {'items': lambda o: o()}}, }) m = p.match({0: 1, 1: 'two', 2: 3.0}) values = [] with m: assert list(keys) == [0, 1, 2] assert list(values) == [1, 'two', 3.0] # with pytest.raises(NameError): # # 'items' is in co_consts instead of co_varnames # print(eval('items')) # pylint: disable=eval-used assert list(eval('items')) == [(0, 1), (1, 'two'), (2, 3.0)]
def test_start_end_block_default_cfg(): """Test _start_block and _end_block (default config).""" o = Dummy6(1, 2, 3, 4, 5, 6) p = ObjectPattern(PD6, config=CONFIG_DEFAULT) print(p.config) m = p.match(o) a = 5 # existing, modified b = 6 # existing, deleted c = 7 # existing, unchanged with m: # pylint: disable=used-before-assignment assert (a, b, c, d, e, f) == (1, 2, 3, 4, 5, 6) a = 6 del b d = 1 del e assert a == 5 assert b == 6 assert c == 7 assert d == 1 with pytest.raises(NameError): print(e) # works (because e is a global variable) assert f == 6
def test_object_pattern_context_handler(): class Dummy: # pylint: disable=too-few-public-methods,missing-class-docstring def __init__(self, a, b, c, d, e): self.a, self.b, self.c, self.d, self.e = a, b, c, d, e p = ObjectPattern({ 'obj': {'eval': [lambda x: isinstance(x, Dummy)]}, 'obj.a': {'bind': {'a': None}}, 'obj.b': {'bind': {'b': None}}, 'obj.c': {'bind': {'c': None}}, 'obj.d': {'bind': {'d': None}}, 'obj.e': {'bind': {'e': None}} }) o = Dummy('This', 'is', 1, 'stupid', 'test') m = p.match(o) a = 'That' d = 'smart' with m: c = m.bound['c'] print(a, b, c, d, e) # pylint: disable=used-before-assignment c = 'a' a = 'Dis' print(a, b, c, d, e) print('__exit__') # NOTE: this works with pypy, but insane defaults have been chosen to # guarantee consistency across implementations # if sys.implementation.name == 'pypy': # with pytest.raises(NameError): # BUG: `del b` does not work # print(b) # with pytest.raises(NameError): # BUG: broken #assignlocal/deletelocal # print(c) # with pytest.raises(NameError): # print(e) assert a == 'That' assert d == 'smart'
def test_object_pattern_special_cases(): """Test ObjectPattern (errors and warnings)""" p2 = ObjectPattern({'obj.keys': {'bind': {'keys': lambda o: list(o())}}}) assert p2.match(None) is None p2.verbose = True with pytest.warns(UserWarning) as e: assert p2.match(None) is None assert e[0].message.args[0] == ("Missing attribute? 'obj.keys', " "'NoneType' object has no attribute 'keys'") def tf(o): return isinstance(o(), list) p3 = ObjectPattern({'obj.keys': {'eval': [tf]}}, verbose=True) d = {0: 1} with pytest.warns(UserWarning) as e: assert p3.match(d) is None assert e[0].message.args[0] == f"Test failed: {tf!r} (('obj', '.keys'): {d.keys!r})" def tf(o): raise ValueError('DEAD') p4 = ObjectPattern({'obj.keys': {'eval': [tf]}}, verbose=True) with pytest.warns(UserWarning) as e: assert p4.match(d) is None assert e[0].message.args[0] == (f"Error running {tf!r} (('obj', '.keys'):" f" {d.keys!r}): {ValueError('DEAD')}") p5 = ObjectPattern({'obj.keys': {'bind': {'keys': lambda o: list(o())}}, 'obj.values': {'bind': {'keys': lambda o: list(o())}}}, verbose=True) with pytest.warns(UserWarning) as e: assert p5.match(d) is not None assert e[0].message.args[0] == f"Overwriting binding for 'keys'"