comparison env/lib/python3.9/site-packages/pluggy/callers.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 Call loop machinery
3 """
4 import sys
5 import warnings
6
7 _py3 = sys.version_info > (3, 0)
8
9
10 if not _py3:
11 exec(
12 """
13 def _reraise(cls, val, tb):
14 raise cls, val, tb
15 """
16 )
17
18
19 def _raise_wrapfail(wrap_controller, msg):
20 co = wrap_controller.gi_code
21 raise RuntimeError(
22 "wrap_controller at %r %s:%d %s"
23 % (co.co_name, co.co_filename, co.co_firstlineno, msg)
24 )
25
26
27 class HookCallError(Exception):
28 """ Hook was called wrongly. """
29
30
31 class _Result(object):
32 def __init__(self, result, excinfo):
33 self._result = result
34 self._excinfo = excinfo
35
36 @property
37 def excinfo(self):
38 return self._excinfo
39
40 @property
41 def result(self):
42 """Get the result(s) for this hook call (DEPRECATED in favor of ``get_result()``)."""
43 msg = "Use get_result() which forces correct exception handling"
44 warnings.warn(DeprecationWarning(msg), stacklevel=2)
45 return self._result
46
47 @classmethod
48 def from_call(cls, func):
49 __tracebackhide__ = True
50 result = excinfo = None
51 try:
52 result = func()
53 except BaseException:
54 excinfo = sys.exc_info()
55
56 return cls(result, excinfo)
57
58 def force_result(self, result):
59 """Force the result(s) to ``result``.
60
61 If the hook was marked as a ``firstresult`` a single value should
62 be set otherwise set a (modified) list of results. Any exceptions
63 found during invocation will be deleted.
64 """
65 self._result = result
66 self._excinfo = None
67
68 def get_result(self):
69 """Get the result(s) for this hook call.
70
71 If the hook was marked as a ``firstresult`` only a single value
72 will be returned otherwise a list of results.
73 """
74 __tracebackhide__ = True
75 if self._excinfo is None:
76 return self._result
77 else:
78 ex = self._excinfo
79 if _py3:
80 raise ex[1].with_traceback(ex[2])
81 _reraise(*ex) # noqa
82
83
84 def _wrapped_call(wrap_controller, func):
85 """ Wrap calling to a function with a generator which needs to yield
86 exactly once. The yield point will trigger calling the wrapped function
87 and return its ``_Result`` to the yield point. The generator then needs
88 to finish (raise StopIteration) in order for the wrapped call to complete.
89 """
90 try:
91 next(wrap_controller) # first yield
92 except StopIteration:
93 _raise_wrapfail(wrap_controller, "did not yield")
94 call_outcome = _Result.from_call(func)
95 try:
96 wrap_controller.send(call_outcome)
97 _raise_wrapfail(wrap_controller, "has second yield")
98 except StopIteration:
99 pass
100 return call_outcome.get_result()
101
102
103 class _LegacyMultiCall(object):
104 """ execute a call into multiple python functions/methods. """
105
106 # XXX note that the __multicall__ argument is supported only
107 # for pytest compatibility reasons. It was never officially
108 # supported there and is explicitely deprecated since 2.8
109 # so we can remove it soon, allowing to avoid the below recursion
110 # in execute() and simplify/speed up the execute loop.
111
112 def __init__(self, hook_impls, kwargs, firstresult=False):
113 self.hook_impls = hook_impls
114 self.caller_kwargs = kwargs # come from _HookCaller.__call__()
115 self.caller_kwargs["__multicall__"] = self
116 self.firstresult = firstresult
117
118 def execute(self):
119 caller_kwargs = self.caller_kwargs
120 self.results = results = []
121 firstresult = self.firstresult
122
123 while self.hook_impls:
124 hook_impl = self.hook_impls.pop()
125 try:
126 args = [caller_kwargs[argname] for argname in hook_impl.argnames]
127 except KeyError:
128 for argname in hook_impl.argnames:
129 if argname not in caller_kwargs:
130 raise HookCallError(
131 "hook call must provide argument %r" % (argname,)
132 )
133 if hook_impl.hookwrapper:
134 return _wrapped_call(hook_impl.function(*args), self.execute)
135 res = hook_impl.function(*args)
136 if res is not None:
137 if firstresult:
138 return res
139 results.append(res)
140
141 if not firstresult:
142 return results
143
144 def __repr__(self):
145 status = "%d meths" % (len(self.hook_impls),)
146 if hasattr(self, "results"):
147 status = ("%d results, " % len(self.results)) + status
148 return "<_MultiCall %s, kwargs=%r>" % (status, self.caller_kwargs)
149
150
151 def _legacymulticall(hook_impls, caller_kwargs, firstresult=False):
152 return _LegacyMultiCall(
153 hook_impls, caller_kwargs, firstresult=firstresult
154 ).execute()
155
156
157 def _multicall(hook_impls, caller_kwargs, firstresult=False):
158 """Execute a call into multiple python functions/methods and return the
159 result(s).
160
161 ``caller_kwargs`` comes from _HookCaller.__call__().
162 """
163 __tracebackhide__ = True
164 results = []
165 excinfo = None
166 try: # run impl and wrapper setup functions in a loop
167 teardowns = []
168 try:
169 for hook_impl in reversed(hook_impls):
170 try:
171 args = [caller_kwargs[argname] for argname in hook_impl.argnames]
172 except KeyError:
173 for argname in hook_impl.argnames:
174 if argname not in caller_kwargs:
175 raise HookCallError(
176 "hook call must provide argument %r" % (argname,)
177 )
178
179 if hook_impl.hookwrapper:
180 try:
181 gen = hook_impl.function(*args)
182 next(gen) # first yield
183 teardowns.append(gen)
184 except StopIteration:
185 _raise_wrapfail(gen, "did not yield")
186 else:
187 res = hook_impl.function(*args)
188 if res is not None:
189 results.append(res)
190 if firstresult: # halt further impl calls
191 break
192 except BaseException:
193 excinfo = sys.exc_info()
194 finally:
195 if firstresult: # first result hooks return a single value
196 outcome = _Result(results[0] if results else None, excinfo)
197 else:
198 outcome = _Result(results, excinfo)
199
200 # run all wrapper post-yield blocks
201 for gen in reversed(teardowns):
202 try:
203 gen.send(outcome)
204 _raise_wrapfail(gen, "has second yield")
205 except StopIteration:
206 pass
207
208 return outcome.get_result()