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

1from decopatch.utils_disambiguation import FirstArgDisambiguation 

2 

3 

4class AmbiguousFirstArgumentTypeError(TypeError): 

5 pass 

6 

7 

8class InvalidMandatoryArgError(TypeError): 

9 pass 

10 

11 

12def call_in_appropriate_mode(impl_function, 

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: 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) 

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 

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 

66 

67 :param decorated: 

68 :param decorator_function: 

69 :return: 

70 """ 

71 return decorator_function()(decorated) 

72 

73 

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 

78 

79 :param decorator_function: 

80 :param args: 

81 :param kwargs: 

82 :return: 

83 """ 

84 return decorator_function(*args, **kwargs)