Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/backcall/backcall.py: 27%

66 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-20 06:09 +0000

1# -*- coding: utf-8 -*- 

2""" 

3Created on Mon Jan 13 18:17:15 2014 

4 

5@author: takluyver 

6""" 

7import sys 

8PY3 = (sys.version_info[0] >= 3) 

9 

10try: 

11 from inspect import signature, Parameter # Python >= 3.3 

12except ImportError: 

13 from ._signatures import signature, Parameter 

14 

15if PY3: 

16 from functools import wraps 

17else: 

18 from functools import wraps as _wraps 

19 def wraps(f): 

20 def dec(func): 

21 _wraps(f)(func) 

22 func.__wrapped__ = f 

23 return func 

24 

25 return dec 

26 

27def callback_prototype(prototype): 

28 """Decorator to process a callback prototype. 

29  

30 A callback prototype is a function whose signature includes all the values 

31 that will be passed by the callback API in question. 

32  

33 The original function will be returned, with a ``prototype.adapt`` attribute 

34 which can be used to prepare third party callbacks. 

35 """ 

36 protosig = signature(prototype) 

37 positional, keyword = [], [] 

38 for name, param in protosig.parameters.items(): 

39 if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD): 

40 raise TypeError("*args/**kwargs not supported in prototypes") 

41 

42 if (param.default is not Parameter.empty) \ 

43 or (param.kind == Parameter.KEYWORD_ONLY): 

44 keyword.append(name) 

45 else: 

46 positional.append(name) 

47 

48 kwargs = dict.fromkeys(keyword) 

49 def adapt(callback): 

50 """Introspect and prepare a third party callback.""" 

51 sig = signature(callback) 

52 try: 

53 # XXX: callback can have extra optional parameters - OK? 

54 sig.bind(*positional, **kwargs) 

55 return callback 

56 except TypeError: 

57 pass 

58 

59 # Match up arguments 

60 unmatched_pos = positional[:] 

61 unmatched_kw = kwargs.copy() 

62 unrecognised = [] 

63 # TODO: unrecognised parameters with default values - OK? 

64 for name, param in sig.parameters.items(): 

65 # print(name, param.kind) #DBG 

66 if param.kind == Parameter.POSITIONAL_ONLY: 

67 if len(unmatched_pos) > 0: 

68 unmatched_pos.pop(0) 

69 else: 

70 unrecognised.append(name) 

71 elif param.kind == Parameter.POSITIONAL_OR_KEYWORD: 

72 if (param.default is not Parameter.empty) and (name in unmatched_kw): 

73 unmatched_kw.pop(name) 

74 elif len(unmatched_pos) > 0: 

75 unmatched_pos.pop(0) 

76 else: 

77 unrecognised.append(name) 

78 elif param.kind == Parameter.VAR_POSITIONAL: 

79 unmatched_pos = [] 

80 elif param.kind == Parameter.KEYWORD_ONLY: 

81 if name in unmatched_kw: 

82 unmatched_kw.pop(name) 

83 else: 

84 unrecognised.append(name) 

85 else: # VAR_KEYWORD 

86 unmatched_kw = {} 

87 

88 # print(unmatched_pos, unmatched_kw, unrecognised) #DBG 

89 

90 if unrecognised: 

91 raise TypeError("Function {!r} had unmatched arguments: {}".format(callback, unrecognised)) 

92 

93 n_positional = len(positional) - len(unmatched_pos) 

94 

95 @wraps(callback) 

96 def adapted(*args, **kwargs): 

97 """Wrapper for third party callbacks that discards excess arguments""" 

98# print(args, kwargs) 

99 args = args[:n_positional] 

100 for name in unmatched_kw: 

101 # XXX: Could name not be in kwargs? 

102 kwargs.pop(name) 

103# print(args, kwargs, unmatched_pos, cut_positional, unmatched_kw) 

104 return callback(*args, **kwargs) 

105 

106 return adapted 

107 

108 prototype.adapt = adapt 

109 return prototype