-
-
Notifications
You must be signed in to change notification settings - Fork 31
ENH: add mypy plugin for more precise ufunc signatures #56
Conversation
@@ -3,3 +3,4 @@ | |||
np.sin.nin + 'foo' # E: Unsupported operand types | |||
np.sin(1, foo='bar') # E: Unexpected keyword argument | |||
np.sin(1, extobj=['foo', 'foo', 'foo']) # E: incompatible type | |||
np.sin(1, 1) # E: Too many positional arguments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The punchline: we statically know that sin takes one argument.
tests/mypy.ini
Outdated
@@ -0,0 +1,2 @@ | |||
[mypy] | |||
plugins = numpy_ufuncs_plugin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have our own tests opt into using the plugin.
numpy_ufuncs_plugin.py
Outdated
from mypy.plugin import Plugin | ||
from mypy.types import CallableType | ||
|
||
UFUNC_ARITIES = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would need a big dictionary of the ufuncs and their arities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UFuncs should provide all the information you need for this, no? Such as np.sin.nin
, np.sin.nout
, and np.sin.signature
(which is None for normal ufuncs).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been assuming it's bad practice to import NumPy instead of working just from the AST, but heck I don't know it seems pretty reasonable here... maybe we should just do it? Worried about triggering https://xkcd.com/292/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, I didn't think about that and I guess it is a mypy/typing code smell question and I have no nose for typing :). I suppose we thus need something like a decorator for ufuncs that duplicates some of the information? (Its not like we can get the decorator information to the C-level cleanly probably).
Also, it is not like NumPy is the only one with ufuncs. SciPy will need to use this hook as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to pulling it off the ufunc object in f8c5979. One (and only one!) of the CI builds is failing though...
Interestingly, PEP 612: https://www.python.org/dev/peps/pep-0612/ might also allow us to better type ufuncs by making the ufunc class generic over a parameter specification. |
And here's a branch https://github.com/person142/numpy-stubs/tree/ufunc-plugin-with-literal with a plugin that abuses
So I guess we've got some options:
|
My main concern is that we really should have a solution that will work also for other libraries, and while importing numpy is possibly fine, we should maybe not force the library to do the same? I am still curious if we cannot do the plugin solution anyway, but provide a way to "expand" the ufuncs, if necessary by telling projects how to create a very simply plugin themselves? The literal solution seems fine, although having |
Closing this; it was a good experiment with plugins (I think I feel more confident that they will be a big part of supporting types in NumPy), but it's not really "production grade". |
Keeping this as a draft as it needs discussion (if we decide its ok I'll need to add more data on ufunc arities to the plugin).
This adds a mypy plugin that alters ufunc signatures to have the correct arity at type checking time. The tl;dr version is:
The broader issue is:
To work around that, the plugin lets us halt each time a
numpy.ufunc.__call__
node is reached in the AST and modify the signature. This just does arities, but you can imagine that whenndarray
is generic over dtypes then we can potentially do even fancier things like adding overloads for all the appropriate dispatches for the ufunc.Now, I imagine one concern people might have is "doesn't this lock us into mypy", and the answer is "not really". The plugin is purely opt in-you have to include it in your
mypy.ini
for it to be activated. So we can easily have the stubs, which describe things as best they can, and a set of plugins that give more precise types for people who happen to be using mypy.Thoughts?