Coverage for src/decopatch/utils_calls.py: 71%
21 statements
« prev ^ index » next coverage.py v6.4.1, created at 2022-07-06 15:13 +0000
« prev ^ index » next coverage.py v6.4.1, created at 2022-07-06 15:13 +0000
1from decopatch.utils_disambiguation import FirstArgDisambiguation
4class AmbiguousFirstArgumentTypeError(TypeError):
5 pass
8class InvalidMandatoryArgError(TypeError):
9 pass
12def call_in_appropriate_mode(impl_function,
13 dk, # type: DecoratorUsageInfo
14 disambiguation_result # type: FirstArgDisambiguation
15 ):
16 """
19 :param impl_function:
20 :param dk:
21 :param disambiguation_result:
22 :return:
23 """
24 if disambiguation_result is FirstArgDisambiguation.is_decorated_target:
25 # (1) NO-parenthesis usage: @foo_decorator
26 if dk.sig_info.is_first_arg_mandatory:
27 # that's not possible
28 raise InvalidMandatoryArgError("function '%s' requires a mandatory argument '%s'. Provided value '%s' does "
29 "not pass its validation criteria"
30 "" % (impl_function.__name__, dk.sig_info.first_arg_name_with_possible_star,
31 dk.first_arg_value))
32 else:
33 # ok: do it
34 return no_parenthesis_usage(impl_function, dk.first_arg_value)
36 elif disambiguation_result is FirstArgDisambiguation.is_normal_arg: 36 ↛ 40line 36 didn't jump to line 40, because the condition on line 36 was never false
37 # (2) WITH-parenthesis usage: @foo_decorator(*args, **kwargs).
38 return with_parenthesis_usage(impl_function, *dk.args, **dk.kwargs)
40 elif disambiguation_result is FirstArgDisambiguation.is_ambiguous:
41 # (3) STILL AMBIGUOUS
42 # By default we are very conservative: we do not allow the first argument to be a callable or class if user did
43 # not provide a way to disambiguate it
44 if dk.sig_info.is_first_arg_mandatory:
45 raise AmbiguousFirstArgumentTypeError(
46 "function '%s' requires a mandatory argument '%s'. It cannot be a class nor a callable."
47 " If you think that it should, then ask your decorator provider to protect his decorator (see "
48 "decopath documentation)" % (impl_function.__name__, dk.sig_info.first_arg_name_with_possible_star))
49 else:
50 raise AmbiguousFirstArgumentTypeError(
51 "Argument '%s' of generated decorator function '%s' is the *first* argument in the signature. "
52 "When the decorator is called (1) with only this argument as non-default value and (2) if this "
53 "argument is a callable or class, then it is not possible to determine if that call was a "
54 "no-parenthesis decorator usage or a with-args decorator usage. If you think that this particular "
55 "usage should be allowed, then ask your decorator provider to protect his decorator (see decopath "
56 "documentation)" % (dk.sig_info.first_arg_name_with_possible_star, impl_function.__name__))
58 else:
59 raise ValueError("single-argument disambiguation did not return properly: received %s" % disambiguation_result)
62def no_parenthesis_usage(decorator_function, decorated):
63 """
64 called with no arg NOR parenthesis: @foo_decorator
65 we have to directly apply the decorator
67 :param decorated:
68 :param decorator_function:
69 :return:
70 """
71 return decorator_function()(decorated)
74def with_parenthesis_usage(decorator_function, *args, **kwargs):
75 """
76 called with no args BUT parenthesis: @foo_decorator().
77 we have to return a nested function to apply the decorator
79 :param decorator_function:
80 :param args:
81 :param kwargs:
82 :return:
83 """
84 return decorator_function(*args, **kwargs)