Issue #19674: inspect.signature() now produces a correct signature · pythoncapi/cpython@44e2eaa · GitHub
Skip to content

Commit 44e2eaa

Browse files
committed
Issue python#19674: inspect.signature() now produces a correct signature
for some builtins.
1 parent 7fa6e1a commit 44e2eaa

18 files changed

Lines changed: 343 additions & 136 deletions

Lib/inspect.py

Lines changed: 62 additions & 0 deletions

Lib/pydoc.py

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -916,20 +916,18 @@ def docroutine(self, object, name=None, mod=None,
916916
reallink = realname
917917
title = '<a name="%s"><strong>%s</strong></a> = %s' % (
918918
anchor, name, reallink)
919-
if inspect.isfunction(object):
920-
args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \
921-
inspect.getfullargspec(object)
922-
argspec = inspect.formatargspec(
923-
args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann,
924-
formatvalue=self.formatvalue,
925-
formatannotation=inspect.formatannotationrelativeto(object))
926-
if realname == '<lambda>':
927-
title = '<strong>%s</strong> <em>lambda</em> ' % name
928-
# XXX lambda's won't usually have func_annotations['return']
929-
# since the syntax doesn't support but it is possible.
930-
# So removing parentheses isn't truly safe.
931-
argspec = argspec[1:-1] # remove parentheses
932-
else:
919+
argspec = None
920+
if inspect.isfunction(object) or inspect.isbuiltin(object):
921+
signature = inspect.signature(object)
922+
if signature:
923+
argspec = str(signature)
924+
if realname == '<lambda>':
925+
title = '<strong>%s</strong> <em>lambda</em> ' % name
926+
# XXX lambda's won't usually have func_annotations['return']
927+
# since the syntax doesn't support but it is possible.
928+
# So removing parentheses isn't truly safe.
929+
argspec = argspec[1:-1] # remove parentheses
930+
if not argspec:
933931
argspec = '(...)'
934932

935933
decl = title + argspec + (note and self.grey(
@@ -1313,20 +1311,18 @@ def docroutine(self, object, name=None, mod=None, cl=None):
13131311
cl.__dict__[realname] is object):
13141312
skipdocs = 1
13151313
title = self.bold(name) + ' = ' + realname
1316-
if inspect.isfunction(object):
1317-
args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \
1318-
inspect.getfullargspec(object)
1319-
argspec = inspect.formatargspec(
1320-
args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann,
1321-
formatvalue=self.formatvalue,
1322-
formatannotation=inspect.formatannotationrelativeto(object))
1323-
if realname == '<lambda>':
1324-
title = self.bold(name) + ' lambda '
1325-
# XXX lambda's won't usually have func_annotations['return']
1326-
# since the syntax doesn't support but it is possible.
1327-
# So removing parentheses isn't truly safe.
1328-
argspec = argspec[1:-1] # remove parentheses
1329-
else:
1314+
argspec = None
1315+
if inspect.isfunction(object) or inspect.isbuiltin(object):
1316+
signature = inspect.signature(object)
1317+
if signature:
1318+
argspec = str(signature)
1319+
if realname == '<lambda>':
1320+
title = self.bold(name) + ' lambda '
1321+
# XXX lambda's won't usually have func_annotations['return']
1322+
# since the syntax doesn't support but it is possible.
1323+
# So removing parentheses isn't truly safe.
1324+
argspec = argspec[1:-1] # remove parentheses
1325+
if not argspec:
13301326
argspec = '(...)'
13311327
decl = title + argspec + note
13321328

Lib/test/test_capi.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,35 @@ def __len__(self):
109109
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
110110
Z(),[b'1'],3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
111111

112+
def test_docstring_signature_parsing(self):
113+
114+
self.assertEqual(_testcapi.no_docstring.__doc__, None)
115+
self.assertEqual(_testcapi.no_docstring.__text_signature__, None)
116+
117+
self.assertEqual(_testcapi.docstring_empty.__doc__, "")
118+
self.assertEqual(_testcapi.docstring_empty.__text_signature__, None)
119+
120+
self.assertEqual(_testcapi.docstring_no_signature.__doc__,
121+
"This docstring has no signature.")
122+
self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None)
123+
124+
self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__,
125+
"docstring_with_invalid_signature (boo)\n"
126+
"\n"
127+
"This docstring has an invalid signature."
128+
)
129+
self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None)
130+
131+
self.assertEqual(_testcapi.docstring_with_signature.__doc__,
132+
"This docstring has a valid signature.")
133+
self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "(sig)")
134+
135+
self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__,
136+
"This docstring has a valid signature and some extra newlines.")
137+
self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__,
138+
"(parameter)")
139+
140+
112141
@unittest.skipUnless(threading, 'Threading required for this test.')
113142
class TestPendingCalls(unittest.TestCase):
114143

Lib/test/test_inspect.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,10 +1588,9 @@ def test_signature_on_builtin_function(self):
15881588
with self.assertRaisesRegex(ValueError, 'not supported by signature'):
15891589
# support for 'method-wrapper'
15901590
inspect.signature(min.__call__)
1591-
with self.assertRaisesRegex(ValueError,
1592-
'no signature found for builtin function'):
1593-
# support for 'method-wrapper'
1594-
inspect.signature(min)
1591+
self.assertEqual(inspect.signature(min), None)
1592+
signature = inspect.signature(os.stat)
1593+
self.assertTrue(isinstance(signature, inspect.Signature))
15951594

15961595
def test_signature_on_non_function(self):
15971596
with self.assertRaisesRegex(TypeError, 'is not a callable object'):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ Core and Builtins
6868
Library
6969
-------
7070

71+
- Issue #19674: inspect.signature() now produces a correct signature
72+
for some builtins.
73+
7174
- Issue #19722: Added opcode.stack_effect(), which
7275
computes the stack effect of bytecode instructions.
7376

Modules/_cursesmodule.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ typedef chtype attr_t; /* No attr_t type is available */
134134
#define STRICT_SYSV_CURSES
135135
#endif
136136

137+
/*[clinic]
138+
module curses
139+
class curses.window
140+
[clinic]*/
141+
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
142+
137143
/* Definition of exception curses.error */
138144

139145
static PyObject *PyCursesError;
@@ -550,8 +556,6 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
550556
/* Addch, Addstr, Addnstr */
551557

552558
/*[clinic]
553-
module curses
554-
class curses.window
555559
556560
curses.window.addch
557561
@@ -580,9 +584,9 @@ current settings for the window object.
580584
[clinic]*/
581585

582586
PyDoc_STRVAR(curses_window_addch__doc__,
587+
"addch([x, y,] ch, [attr])\n"
583588
"Paint character ch at (y, x) with attributes attr.\n"
584589
"\n"
585-
"curses.window.addch([x, y,] ch, [attr])\n"
586590
" x\n"
587591
" X-coordinate.\n"
588592
" y\n"
@@ -646,7 +650,7 @@ curses_window_addch(PyObject *self, PyObject *args)
646650

647651
static PyObject *
648652
curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr)
649-
/*[clinic checksum: 094d012af1019387c0219a9c0bc76e90729c833f]*/
653+
/*[clinic checksum: 44ed958b891cde91205e584c766e048f3999714f]*/
650654
{
651655
PyCursesWindowObject *cwself = (PyCursesWindowObject *)self;
652656
int coordinates_group = group_left_1;

Modules/_datetimemodule.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#include "datetime.h"
1717
#undef Py_BUILD_CORE
1818

19+
/*[clinic]
20+
module datetime
21+
class datetime.datetime
22+
[clinic]*/
23+
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
24+
1925
/* We require that C int be at least 32 bits, and use int virtually
2026
* everywhere. In just a few cases we use a temp long, where a Python
2127
* API returns a C long. In such cases, we have to ensure that the
@@ -4140,8 +4146,6 @@ datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo)
41404146
}
41414147

41424148
/*[clinic]
4143-
module datetime
4144-
class datetime.datetime
41454149
41464150
@classmethod
41474151
datetime.datetime.now
@@ -4155,9 +4159,9 @@ If no tz is specified, uses local timezone.
41554159
[clinic]*/
41564160

41574161
PyDoc_STRVAR(datetime_datetime_now__doc__,
4162+
"now(tz=None)\n"
41584163
"Returns new datetime object representing current time local to tz.\n"
41594164
"\n"
4160-
"datetime.datetime.now(tz=None)\n"
41614165
" tz\n"
41624166
" Timezone object.\n"
41634167
"\n"
@@ -4188,7 +4192,7 @@ datetime_datetime_now(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
41884192

41894193
static PyObject *
41904194
datetime_datetime_now_impl(PyTypeObject *cls, PyObject *tz)
4191-
/*[clinic checksum: 5e61647d5d1feaf1ab096c5406ccea17bb7b061c]*/
4195+
/*[clinic checksum: ca3d26a423b3f633b260c7622e303f0915a96f7c]*/
41924196
{
41934197
PyObject *self;
41944198

Modules/_dbmmodule.c

Lines changed: 11 additions & 12 deletions

0 commit comments

Comments
 (0)