summaryrefslogtreecommitdiff
path: root/numpy/core/overrides.py
diff options
context:
space:
mode:
authorStephan Hoyer <shoyer@google.com>2019-05-11 12:20:12 -0700
committerStephan Hoyer <shoyer@google.com>2019-05-11 12:25:45 -0700
commit34bca67c9c918e90db4b33336a1d9ed2a57350d4 (patch)
tree6ef514d2a12c5b73067da6eadafac388a97b14a7 /numpy/core/overrides.py
parent7efa6192f9393621190e85927a612d11c986718b (diff)
downloadnumpy-34bca67c9c918e90db4b33336a1d9ed2a57350d4.tar.gz
ENH: use exec() instead array_function_dispatch to improve tracebacks
xref GH-12028 Current behavior: >>> np.dot(None, None) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/shoyer/dev/numpy/numpy/core/overrides.py", line 175, in public_api implementation, public_api, relevant_args, args, kwargs) TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType' >>> np.stack([], invalid=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/shoyer/dev/numpy/numpy/core/overrides.py", line 148, in public_api relevant_args = dispatcher(*args, **kwargs) TypeError: _stack_dispatcher() got an unexpected keyword argument 'invalid' With this change: >>> np.dot(None, None) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 6, in dot TypeError: unsupported operand type(s) for *: 'NoneType' and 'NoneType' >>> np.stack([], invalid=True) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<string>", line 4, in stack TypeError: _stack_dispatcher() got an unexpected keyword argument 'invalid'
Diffstat (limited to 'numpy/core/overrides.py')
-rw-r--r--numpy/core/overrides.py22
1 files changed, 20 insertions, 2 deletions
diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py
index 9f91adc83..a12da54b4 100644
--- a/numpy/core/overrides.py
+++ b/numpy/core/overrides.py
@@ -1,6 +1,7 @@
"""Implementation of __array_function__ overrides from NEP-18."""
import collections
import functools
+import textwrap
from numpy.core._multiarray_umath import (
add_docstring, implement_array_function, _get_implementing_args)
@@ -143,11 +144,28 @@ def array_function_dispatch(dispatcher, module=None, verify=True,
if docs_from_dispatcher:
add_docstring(implementation, dispatcher.__doc__)
+ # Equivalently, we could define this function directly instead of using
+ # exec. This version has the advantage of giving the helper function a
+ # more interpettable name. Otherwise, the original function does not
+ # show up at all in many cases, e.g., if it's written in C or if the
+ # dispatcher gets an invalid keyword argument.
+ source = textwrap.dedent("""
@functools.wraps(implementation)
- def public_api(*args, **kwargs):
+ def {name}(*args, **kwargs):
relevant_args = dispatcher(*args, **kwargs)
return implement_array_function(
- implementation, public_api, relevant_args, args, kwargs)
+ implementation, {name}, relevant_args, args, kwargs)
+ """).format(name=implementation.__name__)
+
+ scope = {
+ 'implementation': implementation,
+ 'dispatcher': dispatcher,
+ 'functools': functools,
+ 'implement_array_function': implement_array_function,
+ }
+ exec(source, scope)
+
+ public_api = scope[implementation.__name__]
if module is not None:
public_api.__module__ = module