def test_replace_for_behaviour(self): m = Mock() class T1(object): pass class T2(object): pass class T3(object): pass class T4(object): pass t2 = T2() def job0(): return t2 @requires(T1) @returns(T3) def job1(obj): raise Exception() # pragma: nocover job2 = requires(T4)(m.job2) runner = Runner(job0, job1, job2) runner.replace(job1, requires(T2)(returns(T4)(m.job1))) runner() compare([ call.job1(t2), call.job2(m.job1.return_value), ], actual=m.mock_calls)
def test_supplied_explicitly(self): obj = object() rq = requires('foo') rt = returns('bar') result = CallPoint(obj, rq, rt)(self.context) compare(result, self.context.call.return_value) self.context.call.assert_called_with(obj, rq, rt)
def test_supplied_explicitly(self): obj = object() rq = requires('foo') rt = returns('bar') result = CallPoint(obj, rq, rt)(self.context) compare(result, self.context.extract.return_value) self.context.extract.assert_called_with(obj, rq, rt)
def test_repr_maximal(self): def foo(): pass point = CallPoint(foo, requires('foo'), returns('bar')) point.labels.add('baz') point.labels.add('bob') compare(repr(foo)+" requires('foo') returns('bar') <-- baz, bob", repr(point))
def test_attr_multiple(self): class T2: bar = 'baz' class T: foo = T2() m = Mock() def job1(): m.job1() return T() def job2(obj): m.job2(obj) runner = Runner() runner.add(job1) runner.add(job2, requires(attr(T, 'foo', 'bar'))) runner() compare([ call.job1(), call.job2('baz'), ], m.mock_calls)
def test_return_type_specified_imperative(self): m = Mock() class T1(object): pass class T2(object): pass t = T1() def job1(): m.job1() return t @requires(T2) def job2(obj): m.job2(obj) runner = Runner() runner.add(job1, returns=returns(T2)) runner.add(job2, requires(T2)) runner() compare([ call.job1(), call.job2(t), ], m.mock_calls)
def test_call_requires_missing(self): def foo(obj): return obj context = Context() with ShouldRaise(ContextError( "No <class 'mush.tests.test_context.TheType'> in context" )): context.call(foo, requires(TheType))
def test_default_requirements_for_function(self): def foo(a, b=None): pass check_extract(foo, expected_rq=requires('a', optional('b')), expected_rt=None)
def test_decorator_trumps_annotations(self): @requires('foo') @returns('bar') def foo(a: 'x') -> 'y': pass check_extract(foo, expected_rq=requires('foo'), expected_rt=returns('bar'))
def test_call_requires_missing(self): def foo(obj): return obj context = Context() with ShouldRaise(ContextError( "No <class 'mush.tests.test_context.TheType'> in context" )): context.call(foo, requires(TheType), result_type)
def test_call_requires_item_optional_present(self): def foo(x=1): return x context = Context() context.add(dict(bar='baz'), 'foo') result = context.call(foo, requires(item(optional('foo'), 'bar'))) compare(result, 'baz')
def test_call_requires_item(self): def foo(x): return x context = Context() context.add(dict(bar='baz'), 'foo') result = context.call(foo, requires(item('foo', 'bar'))) compare(result, 'baz')
def test_extract_from_partial_default_in_partial_kw(self): def foo(a=None): pass p = partial(foo, a=1) check_extract(p, expected_rq=requires(a=optional('a')), expected_rt=None)
def test_call_requires_optional_present(self): def foo(x=1): return x context = Context() context.add(2, TheType) result = context.call(foo, requires(optional(TheType))) compare(result, 2) compare({TheType: 2}, context)
def test_default_requirements_for_class(self): class MyClass(object): def __init__(self, a, b=None): pass check_extract(MyClass, expected_rq=requires('a', optional('b')), expected_rt=None)
def test_call_requires_string(self): def foo(obj): return obj context = Context() context.add('bar', 'baz') result = context.call(foo, requires('baz')) compare(result, 'bar') compare({'baz': 'bar'}, context)
def test_extract_from_partial_plus_one_required_in_partial_kw(self): def foo(b, a): pass p = partial(foo, a=1) check_extract(p, expected_rq=requires('b', a=optional('a')), expected_rt=None)
def test_call_requires_type(self): def foo(obj): return obj context = Context() context.add('bar', TheType) result = context.call(foo, requires(TheType)) compare(result, 'bar') compare({TheType: 'bar'}, context)
def test_call_requires_string(self): def foo(obj): return obj context = Context() context.add('bar', 'baz') result = context.call(foo, requires('baz'), result_type) compare(result, 'bar') compare({'baz': 'bar', str: 'bar'}, context)
def test_call_requires_item_optional_ContextError(self): def foo(x=1): return x context = Context() result = context.call(foo, requires(item(optional('foo'), 'bar')), result_type) compare(result, 1)
def test_extract_from_partial_plus_one_default_not_in_partial(self): def foo(b, a=None): pass p = partial(foo) check_extract(p, expected_rq=requires('b', optional('a')), expected_rt=None)
def test_call_requires_optional_ContextError(self): def foo(x=1): return x context = Context() result = context.call(foo, requires(optional(TheType)), result_type) compare(result, 1) compare({int: 1}, context)
def test_call_requires_item_missing(self): def foo(obj): return obj context = Context() context.add({}, TheType) with ShouldRaise(ContextError("No TheType['foo'] in context")): context.call(foo, requires(item(TheType, 'foo')), result_type)
def test_call_requires_item_missing(self): def foo(obj): return obj context = Context() context.add({}, TheType) with ShouldRaise(ContextError( "No TheType['foo'] in context" )): context.call(foo, requires(item(TheType, 'foo')), result_type)
def test_call_requires_attr(self): def foo(x): return x m = Mock() context = Context() context.add(m, 'foo') result = context.call(foo, requires(attr('foo', 'bar'))) compare(result, m.bar)
def test_call_requires_optional_string(self): def foo(x=1): return x context = Context() context.add(2, 'foo') result = context.call(foo, requires(optional('foo'))) compare(result, 2) compare({'foo': 2}, context)
def test_default_requirements(self): def foo(a, b=1, *, c, d=None): pass check_extract(foo, expected_rq=requires('a', optional('b'), c='c', d=optional('d')), expected_rt=None)
def test_call_requires_type(self): def foo(obj): return obj context = Context() context.add('bar', TheType) result = context.call(foo, requires(TheType), result_type) compare(result, 'bar') compare({TheType: 'bar', str: 'bar'}, context)
def test_call_requires_accidental_tuple(self): def foo(obj): return obj context = Context() with ShouldRaise(TypeError( "(<class 'mush.tests.test_context.TheType'>, " "<class 'mush.tests.test_context.TheType'>) " "is not a type or label" )): context.call(foo, requires((TheType, TheType)), result_type)
def test_call_requires_item_optional_present(self): def foo(x=1): return x context = Context() context.add(dict(bar='baz'), 'foo') result = context.call(foo, requires(item(optional('foo'), 'bar')), result_type) compare(result, 'baz')
def test_strings(self): r = requires('1', '2', x='3', y='4') compare(repr(r), "requires('1', '2', x='3', y='4')") compare({ (None, '1'), (None, '2'), ('x', '3'), ('y', '4'), }, set(r))
def test_call_requires_accidental_tuple(self): def foo(obj): return obj context = Context() with ShouldRaise(TypeError( "(<class 'mush.tests.test_context.TheType'>, " "<class 'mush.tests.test_context.TheType'>) " "is not a type or label" )): context.call(foo, requires((TheType, TheType)))
def test_types(self): r = requires(Type1, Type2, x=Type3, y=Type4) compare(repr(r), 'requires(Type1, Type2, x=Type3, y=Type4)') compare({ (None, Type1), (None, Type2), ('x', Type3), ('y', Type4), }, set(r))
def test_call_requires_item_attr(self): def foo(x): return x m = Mock() m.bar= dict(baz='bob') context = Context() context.add(m, 'foo') result = context.call(foo, requires(item(attr('foo', 'bar'), 'baz'))) compare(result, 'bob')
def test_extract_from_partial_plus_one_required_in_partial_arg(self): def foo(b, a): pass p = partial(foo, 1) check_extract( p, # since b is already bound: expected_rq=requires('a'), expected_rt=None)
def test_call_requires_attr(self): def foo(x): return x m = Mock() context = Context() context.add(m, 'foo') result = context.call(foo, requires(attr('foo', 'bar')), result_type) compare(result, m.bar)
def test_call_requires_optional_present(self): def foo(x=1): return x context = Context() context.add(2, TheType) result = context.call(foo, requires(optional(TheType)), result_type) compare(result, 2) compare({TheType: 2, int: 2}, context)
def test_call_requires_named_parameter(self): def foo(x, y): return x, y context = Context() context.add('foo', TheType) context.add('bar', 'baz') result = context.call(foo, requires(y='baz', x=TheType), result_type) compare(result, ('foo', 'bar')) compare({TheType: 'foo', 'baz': 'bar', tuple: ('foo', 'bar')}, context)
def test_call_requires_optional_string(self): def foo(x=1): return x context = Context() context.add(2, 'foo') result = context.call(foo, requires(optional('foo')), result_type) compare(result, 2) compare({'foo': 2, int: 2}, context)
def test_extract_from_partial(self): def foo(x, y, z, a=None): pass p = partial(foo, 1, y=2) check_extract(p, expected_rq=requires(z='z', a=optional('a'), y=optional('y')), expected_rt=None)
def test_explicit_trumps_decorators(self): @requires('foo') @returns('bar') def foo(): pass rq = requires('baz') rt = returns('bob') result = CallPoint(foo, requires=rq, returns=rt)(self.context) compare(result, self.context.call.return_value) self.context.call.assert_called_with(foo, rq, rt)
def test_call_requires_item_attr(self): def foo(x): return x m = Mock() m.bar= dict(baz='bob') context = Context() context.add(m, 'foo') result = context.call(foo, requires(item(attr('foo', 'bar'), 'baz')), result_type) compare(result, 'bob')
def test_extract_from_decorations(self): rq = requires('foo') rt = returns('bar') @rq @rt def foo(): pass result = CallPoint(foo)(self.context) compare(result, self.context.call.return_value) self.context.call.assert_called_with(foo, rq, rt)
def test_explicit_trumps_decorators(self): @requires('foo') @returns('bar') def foo(): pass rq = requires('baz') rt = returns('bob') result = CallPoint(foo, requires=rq, returns=rt)(self.context) compare(result, self.context.extract.return_value) self.context.extract.assert_called_with(foo, rq, rt)
def test_imperative(self): m = Mock() class T1(object): pass class T2(object): pass t1 = T1() t2 = T2() def job1(): m.job1() return t1 def job2(obj): m.job2(obj) return t2 def job3(t2_): m.job3(t2_) # imperative config trumps declarative @requires(T1) def job4(t2_): m.job4(t2_) runner = Runner() runner.add(job1) runner.add(job2, requires(T1)) runner.add(job3, requires(t2_=T2)) runner.add(job4, requires(T2)) runner() compare([ call.job1(), call.job2(t1), call.job3(t2), call.job4(t2), ], m.mock_calls)
def test_repr(self): class T1: pass class T2: pass m = Mock() runner = Runner() runner.add(m.job1, label='label1') runner.add(m.job2, requires('foo', T1), returns(T2), label='label2') runner.add(m.job3) compare('\n'.join(( '<Runner>', ' '+repr(m.job1)+' requires() returns_result_type() <-- label1', ' '+repr(m.job2)+" requires('foo', T1) returns(T2) <-- label2", ' '+repr(m.job3)+' requires() returns_result_type()', '</Runner>' )), repr(runner))
def test_nested(self): class T(object): foo = dict(baz='bar') m = Mock() def job1(): m.job1() return T() def job2(obj): m.job2(obj) runner = Runner() runner.add(job1) runner.add(job2, requires(item(attr(T, 'foo'), 'baz'))) runner() compare([ call.job1(), call.job2('bar'), ], m.mock_calls)
def test_item_multiple(self): class MyDict(dict): pass m = Mock() def job1(): m.job1() obj = MyDict() obj['the_thing'] = dict(other_thing=m.the_thing) return obj def job2(obj): m.job2(obj) runner = Runner() runner.add(job1) runner.add(job2, requires(item(MyDict, 'the_thing', 'other_thing'))) runner() compare([ call.job1(), call.job2(m.the_thing), ], m.mock_calls)
def test_extract_from_decorated_class(self, mock): class Wrapper(object): def __init__(self, func): self.func = func def __call__(self): return 'the '+self.func() def my_dec(func): return update_wrapper(Wrapper(func), func) @my_dec def foo(a: 'foo'=None) -> 'bar': return 'answer' compare(foo(), expected='the answer') check_extract(foo, expected_rq=requires(a='foo'), expected_rt=returns('bar'))
def test_missing_from_context_with_chain(self): class T(object): pass def job1(): pass def job2(): pass @requires(T) def job3(arg): pass # pragma: nocover def job4(): pass def job5(): pass runner = Runner() runner.add(job1, label='1') runner.add(job2) runner.add(job3) runner.add(job4, label='4') runner.add(job5, requires('foo', bar='baz'), returns('bob')) with ShouldRaise(ContextError) as s: runner() text = '\n'.join(( '', '', 'Already called:', repr(job1)+' requires() returns_result_type() <-- 1', repr(job2)+' requires() returns_result_type()', '', 'While calling: '+repr(job3)+' requires(T) returns_result_type()', 'with <Context: {}>:', '', 'No '+repr(T)+' in context', '', 'Still to call:', repr(job4)+' requires() returns_result_type() <-- 4', repr(job5)+" requires('foo', bar='baz') returns('bob')", )) compare(text, repr(s.raised)) compare(text, str(s.raised))
def test_extract_from_decorated_class(self): rq = requires('foo') rt = returns('bar') class Wrapper(object): def __init__(self, func): self.func = func def __call__(self): return 'the '+self.func() def my_dec(func): return update_wrapper(Wrapper(func), func) @my_dec @rq @rt def foo(): return 'answer' self.context.call.side_effect = lambda func, rq, rt: (func(), rq, rt) result = CallPoint(foo)(self.context) compare(result, expected=('the answer', rq, rt))
def test_how_instance_in_annotations(self): how = item('config', 'db_url') def foo(a: how): pass check_extract(foo, expected_rq=requires(a=how), expected_rt=None)