Skip to content
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

gh-81677: basic support for annotations in __text_signature__'s #101872

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3c23b22
gh-81677: basic support for annotations in signature strings
skirpichev Feb 12, 2023
e4745d2
Merge branch 'main' into text_sign_annot
skirpichev Nov 11, 2023
9b44682
Redo parse_annotation() to accept annotation directly
skirpichev Nov 11, 2023
d85dd6c
Move assert
skirpichev Nov 11, 2023
c64280b
Match wrap_value() logic in parse_annotation()
skirpichev Nov 11, 2023
83f1a1b
Merge branch 'main' into text_sign_annot
skirpichev Jan 17, 2024
94467bc
Merge branch 'main' into text_sign_annot
skirpichev Jan 23, 2024
68cf261
Merge branch 'main' into text_sign_annot
skirpichev Feb 8, 2024
177c993
Merge branch 'master' into text_sign_annot
skirpichev Feb 26, 2024
3dc4125
gh-114949: fix signature in the type.__prepare__() doc string
skirpichev Feb 3, 2024
ff758ab
gh-115231: fill __module__ for built-in class/staticmethods
skirpichev Feb 26, 2024
83d21b2
gh-82062: correctly set module for built-in instance methods in inspe…
skirpichev Feb 26, 2024
5ae8583
Merge branch 'master' into text_sign_annot
skirpichev Mar 1, 2024
1f3c7f3
gh-116110: remove extra processing for the __signature__ attribute
skirpichev Feb 27, 2024
eb8dcca
Merge branch 'main' into text_sign_annot
skirpichev May 14, 2024
b8c44b5
Merge branch 'main' into text_sign_annot
skirpichev May 15, 2024
0720f4d
Merge branch 'main' into text_sign_annot
skirpichev May 31, 2024
897547a
Merge branch 'main' into text_sign_annot
skirpichev Jul 19, 2024
494d4e7
Revert "gh-114949: fix signature in the type.__prepare__() doc string"
skirpichev Jul 19, 2024
38b7d53
Revert "gh-115231: fill __module__ for built-in class/staticmethods"
skirpichev Jul 19, 2024
1173cac
Revert "gh-116110: remove extra processing for the __signature__ attr…
skirpichev Jul 19, 2024
76ab235
- news
skirpichev Jul 19, 2024
9c7c0ed
revert test_misc.py
skirpichev Jul 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2276,10 +2276,22 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True):

def parse_name(node):
assert isinstance(node, ast.arg)
if node.annotation is not None:
raise ValueError("Annotations are not currently supported")
return node.arg

def parse_annotation(node):
assert isinstance(node, (ast.arg, ast.FunctionDef))
if isinstance(node, ast.arg):
annotation = node.annotation
else:
annotation = node.returns
if annotation:
expr = ast.unparse(annotation)
try:
return eval(expr, sys_module_dict)
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
except NameError:
raise ValueError
return empty

def wrap_value(s):
try:
value = eval(s, module_dict)
Expand Down Expand Up @@ -2334,7 +2346,11 @@ def p(name_node, default_node, default=empty):
default = ast.literal_eval(default_node)
except ValueError:
raise ValueError("{!r} builtin has invalid signature".format(obj)) from None
parameters.append(Parameter(name, kind, default=default, annotation=empty))
try:
annotation = parse_annotation(name_node)
skirpichev marked this conversation as resolved.
Show resolved Hide resolved
except ValueError:
raise ValueError("{!r} builtin has invalid signature".format(obj)) from None
parameters.append(Parameter(name, kind, default=default, annotation=annotation))

# non-keyword-only parameters
total_non_kw_args = len(f.args.posonlyargs) + len(f.args.args)
Expand Down Expand Up @@ -2381,7 +2397,12 @@ def p(name_node, default_node, default=empty):
p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY)
parameters[0] = p

return cls(parameters, return_annotation=cls.empty)
try:
return_annotation = parse_annotation(f)
except ValueError:
raise ValueError("{!r} builtin has invalid signature".format(obj)) from None

return cls(parameters, return_annotation=return_annotation)


def _signature_from_builtin(cls, func, skip_bound_arg=True):
Expand Down
27 changes: 27 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4807,6 +4807,33 @@ class MyBufferedReader(BufferedReader):
sig = inspect.signature(MyBufferedReader)
self.assertEqual(str(sig), '(raw, buffer_size=8192)')

def test_annotations_in_text_signature(self):
def func(*args, **kwargs):
pass

func.__text_signature__ = '($self, a: int) -> list'
sig = inspect.signature(func)
self.assertIsNotNone(sig)
self.assertEqual(str(sig), '(self, /, a: int) -> list')

func.__text_signature__ = '($self, a: int | float)'
sig = inspect.signature(func)
self.assertIsNotNone(sig)
self.assertEqual(str(sig), '(self, /, a: int | float)')

func.__text_signature__ = '($self, a: tuple[int, ...])'
sig = inspect.signature(func)
self.assertIsNotNone(sig)
self.assertEqual(str(sig), '(self, /, a: tuple[int, ...])')

func.__text_signature__ = '(self, x: spam)'
with self.assertRaises(ValueError):
inspect.signature(func)

func.__text_signature__ = '(self, x) -> spam'
with self.assertRaises(ValueError):
inspect.signature(func)


class NTimesUnwrappable:
def __init__(self, n):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Basic support of annotations in :func:`inspect.signature` handling of
``__text_signature__``. Patch by Sergey B Kirpichev.
11 changes: 6 additions & 5 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,8 @@ find_signature(const char *name, const char *doc)
return doc;
}

#define SIGNATURE_END_MARKER ")\n--\n\n"
#define SIGNATURE_END_MARKER_LENGTH 6
#define SIGNATURE_END_MARKER "\n--\n\n"
#define SIGNATURE_END_MARKER_LENGTH 5
/*
* skips past the end of the docstring's introspection signature.
* (assumes doc starts with a valid signature prefix.)
Expand Down Expand Up @@ -623,11 +623,12 @@ _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_d
Py_RETURN_NONE;
}

/* back "end" up until it points just past the final ')' */
end -= SIGNATURE_END_MARKER_LENGTH - 1;
/* back "end" up until it points just to the end marker */
end -= SIGNATURE_END_MARKER_LENGTH;
assert((end - start) >= 2); /* should be "()" at least */
assert(end[-1] == ')');
assert(end[0] == '\n');
assert(end[1] == '-');
assert(end[2] == '-');
return PyUnicode_FromStringAndSize(start, end - start);
}

Expand Down
Loading