-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
ENH: Automate signature computation for builtin functions #17818
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
ENH: Automate signature computation for builtin functions #17818
Conversation
tests are missing, and I'm using an internal API of inspect |
f84a4fa
to
a22c8e9
Compare
If this is for C docstrings, python has a builtin mechanism for this via docstrings containing |
Yeah, but I cannot inject a |
a22c8e9
to
4d9881a
Compare
You can, you do it in the same way that we currently inject docs into builtins - all that you need is Note that the problem I ran into last time is that inspect.Signature cannot parse our default values |
ff13987
to
2b9390c
Compare
2b9390c
to
0df2c9f
Compare
Now that validation passes, I can propose to remove the current call to inspect internal API by a builtin-one that matches numpy formatting, which would fix the default argument you're mentioning (I don't get any error with current parser though, but maybe that was a sillent error?). @eric-wieser, what do you suggest? |
Require a few signature patching, but nothing terrible See numpy/numpy#17818
Require a few signature patching, but nothing terrible See numpy/numpy#17818
gentle ping :-) |
@serge-sans-paille could you add to the PR description an example of the change in user-visible behaviour? That would help in reviewing this PR. |
Also interested in how it helps Pythran (at least I assume it does?). |
It's actually a corner case I hit while developing https://github.com/serge-sans-paille/penty, a type inference engine for scientific code written in Python. This project is still in early stages, but the goal is to support numpy's implicit type system, including common cases where the array shape is known, and various kind of fancy indexing can be supported. The |
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.
This seems like a nice thing to do, and probably preferable to editing all docstrings to add --\n\n
and relying on Python to do this.
numpy/tests/test_public_api.py
Outdated
@@ -471,3 +472,18 @@ def check_importable(module_name): | ||
raise AssertionError("Modules that are not really public but looked " | ||
"public and can not be imported: " | ||
"{}".format(module_names)) | ||
|
||
if sys.flags.optimize < 2: |
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 normal way to write this would be to decorate the test_
function with @pytest.mark.skipif(sys.flags.optimize < 2)
numpy/core/overrides.py
Outdated
@@ -213,6 +223,9 @@ def decorator(implementation): | ||
if module is not None: | ||
public_api.__module__ = module | ||
|
||
if docs_from_dispatcher and dispatcher.__doc__: |
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 and dispatcher.__doc__
seems necessary; strangely enough there's unprotected uses of dispatcher.__doc__
a little higher up in array_function_dispatch
that don't seem to cause any issues. Unrelated to this PR, but we may want to check that.
numpy/core/overrides.py
Outdated
function.__signature__ = inspect._signature_fromstr( | ||
inspect.Signature, function, | ||
docstring.strip().split('\n')[0]) | ||
except ValueError: |
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.
This will need except Exception
probably, there's other ways this could conceivably fail.
numpy/core/overrides.py
Outdated
@@ -34,6 +35,15 @@ def set_array_function_like_doc(public_api): | ||
return public_api | ||
|
||
|
||
def add_signature(function, docstring): | ||
try: | ||
function.__signature__ = inspect._signature_fromstr( |
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.
Would indeed be nice to replace this with a vendored copy or something else that's not private API.
Nice PR - currently |
Thanks! That was the goal, but it got a bit obsolete... I'll try to resurrect it -) |
Awesome, thanks! :) |
0df2c9f
to
c7f8956
Compare
Sorry for never replying to the ping. I'm still not clear on what the resolution to this comment is. |
2d10ba7
to
d04fbf1
Compare
numpy/core/overrides.py
Outdated
objname = obj.__name__ | ||
if not head.startswith(objname): | ||
return None |
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.
This will require some minor modification of the ndarray
method docstrings, as they use a f"a.{objname}"
-pattern for their signatures:
numpy/numpy/core/_add_newdocs.py
Lines 3656 to 3658 in 8c9cf79
add_newdoc('numpy.core.multiarray', 'ndarray', ('ravel', | |
""" | |
a.ravel([order]) |
d04fbf1
to
4c03b06
Compare
New Update I'm super happy with: instead of getting the |
4c03b06
to
564f386
Compare
That's going to be significantly slower than setting text_signature, sadly |
for the record:
|
564f386
to
6851b20
Compare
numpy/core/overrides.py
Outdated
@@ -1,6 +1,7 @@ | ||
"""Implementation of __array_function__ overrides from NEP-18.""" | ||
import collections | ||
import functools | ||
import inspect |
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.
Is this needed?
101b541
to
1d8dad4
Compare
I don't have a CircleCI account so I cannot see what's the issue there :-/ |
The docs build ends with this error. I don't know how that is connected to this PR, maybe you need to rebase off master?
You might want to try building the docs locally. |
@mattip : it's autodoc that interacts in bad manner with |
This affects import time, right? Could you run with |
signature approach: cumulated time for the import of So basically if we want to extract the signature from the docstring while keeping the docstring easy to maintain, it actually costs more and it's in compatible with autodoc. And information is duplicated between the docstring and the actual signature. |
See also #10167, which proposes to not use the python-based add_newdocs at runtime at all. |
And for completeness: signature approach: 68437.1 us average import time over 100 run And because I had to mention it: ie. make the docstring looks less nice but not requiring any processing, as in: """empty_like(prototype, dtype=None, order='K', subok=True, shape=None)
--
Return a new array with the same shape and type as a given array.
Parameters |
What is the import time on main before this PR? |
68168.8 us in average |
1d8dad4
to
99e20c6
Compare
I don't really understand that; how is autodoc even seeing the |
This improves the compatibility with the inspect module.
99e20c6
to
25686d1
Compare
@eric-wieser I cannot tell for sure. that was just an educated guess as it was the only change. I don't quite want to dug further into that one. The performance impact onf startup of current approach looks very low, and from an engineering point of view, it looks easier to maintain. Is that a blocker? |
2 ms total impact (or <1 ms if it's the signature approach) on import time should not be a blocker. This is a correctness/completeness issue that people keep stumbling over. So +1 for the easier to maintain solution here. |
Despite the long and complicated discussion, this is actually a small patch that should be mergeable. Would you mind resolving the merge conflicts @serge-sans-paille? |
def can_cast(from_, to, casting=None): | ||
""" | ||
can_cast(from_, to, casting='safe') |
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.
Doesn't this mean that the generated signature is now going to say that the default for casting
is None
? In general, the dispatchers use None
as the default for everything, even though that's not reflective of their true signature.
These changes works with the current |
This improves the compatibility with the inspect module. Otherwise some functions are not compatible with it. For instance we currently have
while the docstring contains more information
With this patch, one gets