-
-
Notifications
You must be signed in to change notification settings - Fork 32k
gh-101552: Allow pydoc to display signatures in source format #124669
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
Changes from 9 commits
f806791
4e319cf
38c4b42
c33c4cc
9c7b972
b14a719
33ef93d
afdf4fc
1549080
5831cc4
35ee105
c549c70
e448b82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -694,7 +694,7 @@ and its return annotation. To retrieve a :class:`!Signature` object, | |
use the :func:`!signature` | ||
function. | ||
|
||
.. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) | ||
.. function:: signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False, annotation_format=Format.VALUE) | ||
|
||
Return a :class:`Signature` object for the given *callable*: | ||
|
||
|
@@ -722,18 +722,20 @@ function. | |
``from __future__ import annotations`` was used), :func:`signature` will | ||
attempt to automatically un-stringize the annotations using | ||
:func:`annotationlib.get_annotations`. The | ||
*globals*, *locals*, and *eval_str* parameters are passed | ||
*globals*, *locals*, *eval_str*, and *annotation_format* parameters are passed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a minor thing, but: I realize the documentation doesn't explicitly detail where these parameters go, so this would be all-new text. And when I try it in my head it always comes across as clumsy. So... should we even bother to try? Would anybody be confused if we didn't bother to explain it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, I will add a separate sentence about the |
||
into :func:`!annotationlib.get_annotations` when resolving the | ||
annotations; see the documentation for :func:`!annotationlib.get_annotations` | ||
for instructions on how to use these parameters. | ||
for instructions on how to use these parameters. For example, use | ||
``annotation_format=annotationlib.Format.STRING`` to return annotations in string | ||
format. | ||
|
||
Raises :exc:`ValueError` if no signature can be provided, and | ||
:exc:`TypeError` if that type of object is not supported. Also, | ||
if the annotations are stringized, and *eval_str* is not false, | ||
the ``eval()`` call(s) to un-stringize the annotations in :func:`annotationlib.get_annotations` | ||
could potentially raise any kind of exception. | ||
|
||
A slash(/) in the signature of a function denotes that the parameters prior | ||
A slash (/) in the signature of a function denotes that the parameters prior | ||
to it are positional-only. For more info, see | ||
:ref:`the FAQ entry on positional-only parameters <faq-positional-only-arguments>`. | ||
|
||
|
@@ -746,6 +748,9 @@ function. | |
.. versionchanged:: 3.10 | ||
The *globals*, *locals*, and *eval_str* parameters were added. | ||
|
||
.. versionchanged:: 3.14 | ||
The *annotation_format* parameter was added. | ||
|
||
.. note:: | ||
|
||
Some callables may not be introspectable in certain implementations of | ||
|
@@ -838,7 +843,7 @@ function. | |
:class:`Signature` objects are also supported by the generic function | ||
:func:`copy.replace`. | ||
|
||
.. method:: format(*, max_width=None) | ||
.. method:: format(*, max_width=None, unquote_annotations=False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't feel like a totally correct use of "unquote" to me... Maybe the parameter could be called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use "unquote" in various other places (e.g., https://docs.python.org/3.13/library/csv.html#csv.QUOTE_NOTNULL); I feel it's more clear and concise There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "unquoted" is used in https://docs.python.org/3.13/library/csv.html#csv.QUOTE_NOTNULL as an adjective rather than a verb. I think it makes sense to talk about something being "unquoted" but less sense to talk about "unquoting" something. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "dequote"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm reading the code correctly, if this is false (the default) we use I suggest the clearest way to express this is to flip it. Negate the boolean and rename it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, changing that. |
||
|
||
Create a string representation of the :class:`Signature` object. | ||
|
||
|
@@ -847,8 +852,17 @@ function. | |
If the signature is longer than *max_width*, | ||
all parameters will be on separate lines. | ||
|
||
If *unquote_annotations* is True, :term:`annotations <annotation>` | ||
in the signature are displayed without opening and closing quotation | ||
marks. This is useful if the signature was created with the | ||
:attr:`~annotationlib.Format.STRING` format or if | ||
``from __future__ import annotations`` was used. | ||
|
||
.. versionadded:: 3.13 | ||
|
||
.. versionchanged:: 3.14 | ||
The *unquote_annotations* parameter was added. | ||
|
||
.. classmethod:: Signature.from_callable(obj, *, follow_wrapped=True, globals=None, locals=None, eval_str=False) | ||
|
||
Return a :class:`Signature` (or its subclass) object for a given callable | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
def f(x: undefined): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from annotationlib import Format, ForwardRef | ||
import asyncio | ||
import builtins | ||
import collections | ||
|
@@ -22,7 +23,6 @@ | |
import types | ||
import tempfile | ||
import textwrap | ||
from typing import Unpack | ||
import unicodedata | ||
import unittest | ||
import unittest.mock | ||
|
@@ -46,6 +46,7 @@ | |
from test.test_inspect import inspect_fodder as mod | ||
from test.test_inspect import inspect_fodder2 as mod2 | ||
from test.test_inspect import inspect_stringized_annotations | ||
from test.test_inspect import inspect_deferred_annotations | ||
|
||
|
||
# Functions tested in this suite: | ||
|
@@ -4565,6 +4566,18 @@ def func( | |
expected_multiline, | ||
) | ||
|
||
def test_signature_format_unquote(self): | ||
def func(x: 'int') -> 'str': ... | ||
|
||
self.assertEqual( | ||
inspect.signature(func).format(), | ||
"(x: 'int') -> 'str'" | ||
) | ||
self.assertEqual( | ||
inspect.signature(func).format(unquote_annotations=True), | ||
"(x: int) -> str" | ||
) | ||
|
||
def test_signature_replace_parameters(self): | ||
def test(a, b) -> 42: | ||
pass | ||
|
@@ -4797,6 +4810,26 @@ def test_signature_eval_str(self): | |
par('b', PORK, annotation=tuple), | ||
))) | ||
|
||
def test_signature_annotation_format(self): | ||
ida = inspect_deferred_annotations | ||
sig = inspect.Signature | ||
par = inspect.Parameter | ||
PORK = inspect.Parameter.POSITIONAL_OR_KEYWORD | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐷 |
||
for signature_func in (inspect.signature, inspect.Signature.from_callable): | ||
with self.subTest(signature_func=signature_func): | ||
self.assertEqual( | ||
signature_func(ida.f, annotation_format=Format.STRING), | ||
sig([par("x", PORK, annotation="undefined")]) | ||
) | ||
self.assertEqual( | ||
signature_func(ida.f, annotation_format=Format.FORWARDREF), | ||
sig([par("x", PORK, annotation=ForwardRef("undefined"))]) | ||
) | ||
with self.assertRaisesRegex(NameError, "undefined"): | ||
signature_func(ida.f, annotation_format=Format.VALUE) | ||
with self.assertRaisesRegex(NameError, "undefined"): | ||
signature_func(ida.f) | ||
|
||
def test_signature_none_annotation(self): | ||
class funclike: | ||
# Has to be callable, and have correct | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Add a *format* parameter to :func:`inspect.signature`. Add an | ||
*unquote_annotations* parameter to :meth:`inspect.Signature.format`. Use the | ||
JelleZijlstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
new functionality to improve the display of annotations in signatures in | ||
:mod:`pydoc`. Patch by Jelle Zijlstra. |
Uh oh!
There was an error while loading. Please reload this page.