1 from decopatch.utils_disambiguation import FirstArgDisambiguation
2
3
4 class AmbiguousFirstArgumentTypeError(TypeError):
5 pass
6
7
8 class InvalidMandatoryArgError(TypeError):
9 pass
10
11
12 def call_in_appropriate_mode(impl_function,
-
F821
Undefined name 'DecoratorUsageInfo'
13 dk, # type: DecoratorUsageInfo
14 disambiguation_result # type: FirstArgDisambiguation
15 ):
16 """
17
18
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)
35
36 elif disambiguation_result is FirstArgDisambiguation.is_normal_arg:
37 # (2) WITH-parenthesis usage: @foo_decorator(*args, **kwargs).
38 return with_parenthesis_usage(impl_function, *dk.args, **dk.kwargs)
39
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__))
57
58 else:
59 raise ValueError("single-argument disambiguation did not return properly: received %s" % disambiguation_result)
60
61
62 def no_parenthesis_usage(decorator_function, decorated):
63 """
64 called with no arg NOR parenthesis: @foo_decorator
65 we have to directly apply the decorator
66
67 :param decorated:
68 :param decorator_function:
69 :return:
70 """
71 return decorator_function()(decorated)
72
73
74 def 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
78
79 :param decorator_function:
80 :param args:
81 :param kwargs:
82 :return:
83 """
84 return decorator_function(*args, **kwargs)