diff options
author | Stephan Hoyer <shoyer@google.com> | 2019-05-11 12:20:12 -0700 |
---|---|---|
committer | Stephan Hoyer <shoyer@google.com> | 2019-05-11 12:25:45 -0700 |
commit | 34bca67c9c918e90db4b33336a1d9ed2a57350d4 (patch) | |
tree | 6ef514d2a12c5b73067da6eadafac388a97b14a7 /numpy/core/overrides.py | |
parent | 7efa6192f9393621190e85927a612d11c986718b (diff) | |
download | numpy-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.py | 22 |
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 |