summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLarry Hastings <larry@hastings.org>2013-11-23 23:37:55 (GMT)
committerLarry Hastings <larry@hastings.org>2013-11-23 23:37:55 (GMT)
commit44e2eaab5491881120aab43e2838da8afe7ab70e (patch)
tree92847876fa89736ab40d027431ff27e4973409c9
parent7fa6e1aeea111e7d954b753fb483afc18f21add0 (diff)
downloadcpython-44e2eaab5491881120aab43e2838da8afe7ab70e.zip
cpython-44e2eaab5491881120aab43e2838da8afe7ab70e.tar.gz
cpython-44e2eaab5491881120aab43e2838da8afe7ab70e.tar.bz2
Issue #19674: inspect.signature() now produces a correct signature
for some builtins.
-rw-r--r--Lib/inspect.py62
-rwxr-xr-xLib/pydoc.py52
-rw-r--r--Lib/test/test_capi.py29
-rw-r--r--Lib/test/test_inspect.py7
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_cursesmodule.c12
-rw-r--r--Modules/_datetimemodule.c12
-rw-r--r--Modules/_dbmmodule.c23
-rw-r--r--Modules/_opcode.c19
-rw-r--r--Modules/_testcapimodule.c44
-rw-r--r--Modules/_weakref.c12
-rw-r--r--Modules/posixmodule.c19
-rw-r--r--Modules/unicodedata.c14
-rw-r--r--Modules/zlibmodule.c17
-rw-r--r--Objects/dictobject.c13
-rw-r--r--Objects/methodobject.c71
-rw-r--r--Objects/unicodeobject.c11
-rwxr-xr-xTools/clinic/clinic.py59
18 files changed, 343 insertions, 136 deletions
diff --git a/Lib/inspect.py b/Lib/inspect.py
index edbf927..dc94e44 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -31,6 +31,7 @@ Here are some of the useful functions provided by this module:
__author__ = ('Ka-Ping Yee <ping@lfw.org>',
'Yury Selivanov <yselivanov@sprymix.com>')
+import ast
import importlib.machinery
import itertools
import linecache
@@ -1461,6 +1462,9 @@ def signature(obj):
if isinstance(obj, types.FunctionType):
return Signature.from_function(obj)
+ if isinstance(obj, types.BuiltinFunctionType):
+ return Signature.from_builtin(obj)
+
if isinstance(obj, functools.partial):
sig = signature(obj.func)
@@ -1942,6 +1946,64 @@ class Signature:
return_annotation=annotations.get('return', _empty),
__validate_parameters__=False)
+ @classmethod
+ def from_builtin(cls, func):
+ s = getattr(func, "__text_signature__", None)
+ if not s:
+ return None
+
+ if s.endswith("/)"):
+ kind = Parameter.POSITIONAL_ONLY
+ s = s[:-2] + ')'
+ else:
+ kind = Parameter.POSITIONAL_OR_KEYWORD
+
+ s = "def foo" + s + ": pass"
+
+ try:
+ module = ast.parse(s)
+ except SyntaxError:
+ return None
+ if not isinstance(module, ast.Module):
+ return None
+
+ # ast.FunctionDef
+ f = module.body[0]
+
+ parameters = []
+ empty = Parameter.empty
+
+ def p(name_node, default_node, default=empty):
+ name = name_node.arg
+
+ if isinstance(default_node, ast.Num):
+ default = default.n
+ elif isinstance(default_node, ast.NameConstant):
+ default = default_node.value
+ parameters.append(Parameter(name, kind, default=default, annotation=empty))
+
+ # non-keyword-only parameters
+ for name, default in reversed(list(itertools.zip_longest(reversed(f.args.args), reversed(f.args.defaults), fillvalue=None))):
+ p(name, default)
+
+ # *args
+ if f.args.vararg:
+ kind = Parameter.VAR_POSITIONAL
+ p(f.args.vararg, empty)
+
+ # keyword-only arguments
+ kind = Parameter.KEYWORD_ONLY
+ for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults):
+ p(name, default)
+
+ # **kwargs
+ if f.args.kwarg:
+ kind = Parameter.VAR_KEYWORD
+ p(f.args.kwarg, empty)
+
+ return cls(parameters, return_annotation=cls.empty)
+
+
@property
def parameters(self):
return self._parameters
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index e8127a2..2e63226 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -916,20 +916,18 @@ class HTMLDoc(Doc):
reallink = realname
title = '<a name="%s"><strong>%s</strong></a> = %s' % (
anchor, name, reallink)
- if inspect.isfunction(object):
- args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \
- inspect.getfullargspec(object)
- argspec = inspect.formatargspec(
- args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann,
- formatvalue=self.formatvalue,
- formatannotation=inspect.formatannotationrelativeto(object))
- if realname == '<lambda>':
- title = '<strong>%s</strong> <em>lambda</em> ' % name
- # XXX lambda's won't usually have func_annotations['return']
- # since the syntax doesn't support but it is possible.
- # So removing parentheses isn't truly safe.
- argspec = argspec[1:-1] # remove parentheses
- else:
+ argspec = None
+ if inspect.isfunction(object) or inspect.isbuiltin(object):
+ signature = inspect.signature(object)
+ if signature:
+ argspec = str(signature)
+ if realname == '<lambda>':
+ title = '<strong>%s</strong> <em>lambda</em> ' % name
+ # XXX lambda's won't usually have func_annotations['return']
+ # since the syntax doesn't support but it is possible.
+ # So removing parentheses isn't truly safe.
+ argspec = argspec[1:-1] # remove parentheses
+ if not argspec:
argspec = '(...)'
decl = title + argspec + (note and self.grey(
@@ -1313,20 +1311,18 @@ location listed above.
cl.__dict__[realname] is object):
skipdocs = 1
title = self.bold(name) + ' = ' + realname
- if inspect.isfunction(object):
- args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \
- inspect.getfullargspec(object)
- argspec = inspect.formatargspec(
- args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann,
- formatvalue=self.formatvalue,
- formatannotation=inspect.formatannotationrelativeto(object))
- if realname == '<lambda>':
- title = self.bold(name) + ' lambda '
- # XXX lambda's won't usually have func_annotations['return']
- # since the syntax doesn't support but it is possible.
- # So removing parentheses isn't truly safe.
- argspec = argspec[1:-1] # remove parentheses
- else:
+ argspec = None
+ if inspect.isfunction(object) or inspect.isbuiltin(object):
+ signature = inspect.signature(object)
+ if signature:
+ argspec = str(signature)
+ if realname == '<lambda>':
+ title = self.bold(name) + ' lambda '
+ # XXX lambda's won't usually have func_annotations['return']
+ # since the syntax doesn't support but it is possible.
+ # So removing parentheses isn't truly safe.
+ argspec = argspec[1:-1] # remove parentheses
+ if not argspec:
argspec = '(...)'
decl = title + argspec + note
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 000079e..6f75b77 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -109,6 +109,35 @@ class CAPITest(unittest.TestCase):
self.assertRaises(TypeError, _posixsubprocess.fork_exec,
Z(),[b'1'],3,[1, 2],5,6,7,8,9,10,11,12,13,14,15,16,17)
+ def test_docstring_signature_parsing(self):
+
+ self.assertEqual(_testcapi.no_docstring.__doc__, None)
+ self.assertEqual(_testcapi.no_docstring.__text_signature__, None)
+
+ self.assertEqual(_testcapi.docstring_empty.__doc__, "")
+ self.assertEqual(_testcapi.docstring_empty.__text_signature__, None)
+
+ self.assertEqual(_testcapi.docstring_no_signature.__doc__,
+ "This docstring has no signature.")
+ self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None)
+
+ self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__,
+ "docstring_with_invalid_signature (boo)\n"
+ "\n"
+ "This docstring has an invalid signature."
+ )
+ self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None)
+
+ self.assertEqual(_testcapi.docstring_with_signature.__doc__,
+ "This docstring has a valid signature.")
+ self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "(sig)")
+
+ self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__,
+ "This docstring has a valid signature and some extra newlines.")
+ self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__,
+ "(parameter)")
+
+
@unittest.skipUnless(threading, 'Threading required for this test.')
class TestPendingCalls(unittest.TestCase):
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index 9d34904..0258d3df 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -1588,10 +1588,9 @@ class TestSignatureObject(unittest.TestCase):
with self.assertRaisesRegex(ValueError, 'not supported by signature'):
# support for 'method-wrapper'
inspect.signature(min.__call__)
- with self.assertRaisesRegex(ValueError,
- 'no signature found for builtin function'):
- # support for 'method-wrapper'
- inspect.signature(min)
+ self.assertEqual(inspect.signature(min), None)
+ signature = inspect.signature(os.stat)
+ self.assertTrue(isinstance(signature, inspect.Signature))
def test_signature_on_non_function(self):
with self.assertRaisesRegex(TypeError, 'is not a callable object'):
diff --git a/Misc/NEWS b/Misc/NEWS
index 1782312..cf6cc0e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -68,6 +68,9 @@ Core and Builtins
Library
-------
+- Issue #19674: inspect.signature() now produces a correct signature
+ for some builtins.
+
- Issue #19722: Added opcode.stack_effect(), which
computes the stack effect of bytecode instructions.
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 6e48693..98b81fe 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -134,6 +134,12 @@ typedef chtype attr_t; /* No attr_t type is available */
#define STRICT_SYSV_CURSES
#endif
+/*[clinic]
+module curses
+class curses.window
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
/* Definition of exception curses.error */
static PyObject *PyCursesError;
@@ -550,8 +556,6 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
/* Addch, Addstr, Addnstr */
/*[clinic]
-module curses
-class curses.window
curses.window.addch
@@ -580,9 +584,9 @@ current settings for the window object.
[clinic]*/
PyDoc_STRVAR(curses_window_addch__doc__,
+"addch([x, y,] ch, [attr])\n"
"Paint character ch at (y, x) with attributes attr.\n"
"\n"
-"curses.window.addch([x, y,] ch, [attr])\n"
" x\n"
" X-coordinate.\n"
" y\n"
@@ -646,7 +650,7 @@ curses_window_addch(PyObject *self, PyObject *args)
static PyObject *
curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr)
-/*[clinic checksum: 094d012af1019387c0219a9c0bc76e90729c833f]*/
+/*[clinic checksum: 44ed958b891cde91205e584c766e048f3999714f]*/
{
PyCursesWindowObject *cwself = (PyCursesWindowObject *)self;
int coordinates_group = group_left_1;
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 13c4ecc..e4dc1bf 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -16,6 +16,12 @@
#include "datetime.h"
#undef Py_BUILD_CORE
+/*[clinic]
+module datetime
+class datetime.datetime
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
/* We require that C int be at least 32 bits, and use int virtually
* everywhere. In just a few cases we use a temp long, where a Python
* 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)
}
/*[clinic]
-module datetime
-class datetime.datetime
@classmethod
datetime.datetime.now
@@ -4155,9 +4159,9 @@ If no tz is specified, uses local timezone.
[clinic]*/
PyDoc_STRVAR(datetime_datetime_now__doc__,
+"now(tz=None)\n"
"Returns new datetime object representing current time local to tz.\n"
"\n"
-"datetime.datetime.now(tz=None)\n"
" tz\n"
" Timezone object.\n"
"\n"
@@ -4188,7 +4192,7 @@ exit:
static PyObject *
datetime_datetime_now_impl(PyTypeObject *cls, PyObject *tz)
-/*[clinic checksum: 5e61647d5d1feaf1ab096c5406ccea17bb7b061c]*/
+/*[clinic checksum: ca3d26a423b3f633b260c7622e303f0915a96f7c]*/
{
PyObject *self;
diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c
index 10f872d..10f8fb9 100644
--- a/Modules/_dbmmodule.c
+++ b/Modules/_dbmmodule.c
@@ -28,6 +28,12 @@ static char *which_dbm = "Berkeley DB";
#error "No ndbm.h available!"
#endif
+/*[clinic]
+module dbm
+class dbm.dbm
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
typedef struct {
PyObject_HEAD
int di_size; /* -1 means recompute */
@@ -43,12 +49,6 @@ static PyTypeObject Dbmtype;
static PyObject *DbmError;
-/*[clinic]
-module dbm
-class dbm.dbm
-[clinic]*/
-/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
-
/*[python]
class dbmobject_converter(self_converter):
type = "dbmobject *"
@@ -278,9 +278,8 @@ Return the value for key if present, otherwise default.
[clinic]*/
PyDoc_STRVAR(dbm_dbm_get__doc__,
-"Return the value for key if present, otherwise default.\n"
-"\n"
-"dbm.dbm.get(key, [default])");
+"get(key, [default])\n"
+"Return the value for key if present, otherwise default.");
#define DBM_DBM_GET_METHODDEF \
{"get", (PyCFunction)dbm_dbm_get, METH_VARARGS, dbm_dbm_get__doc__},
@@ -318,7 +317,7 @@ dbm_dbm_get(PyObject *self, PyObject *args)
static PyObject *
dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, int group_right_1, PyObject *default_value)
-/*[clinic checksum: 5b4265e66568f163ef0fc7efec09410eaf793508]*/
+/*[clinic checksum: 28cf8928811bde51e535d67ae98ea039d79df717]*/
{
datum dbm_key, val;
@@ -461,9 +460,9 @@ Return a database object.
[clinic]*/
PyDoc_STRVAR(dbmopen__doc__,
+"open(filename, flags=\'r\', mode=0o666)\n"
"Return a database object.\n"
"\n"
-"dbm.open(filename, flags=\'r\', mode=0o666)\n"
" filename\n"
" The filename to open.\n"
" flags\n"
@@ -498,7 +497,7 @@ exit:
static PyObject *
dbmopen_impl(PyModuleDef *module, const char *filename, const char *flags, int mode)
-/*[clinic checksum: c1f2036017ec36a43ac6f59893732751e67c19d5]*/
+/*[clinic checksum: fb265f75641553ccd963f84c143b35c11f9121fc]*/
{
int iflags;
diff --git a/Modules/_opcode.c b/Modules/_opcode.c
index c53f0e0..55cffe1 100644
--- a/Modules/_opcode.c
+++ b/Modules/_opcode.c
@@ -1,10 +1,12 @@
#include "Python.h"
#include "opcode.h"
-
/*[clinic]
-
module _opcode
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+/*[clinic]
_opcode.stack_effect -> int
@@ -19,18 +21,17 @@ Compute the stack effect of the opcode.
[clinic]*/
PyDoc_STRVAR(_opcode_stack_effect__doc__,
-"Compute the stack effect of the opcode.\n"
-"\n"
-"_opcode.stack_effect(opcode, [oparg])");
+"stack_effect(opcode, [oparg])\n"
+"Compute the stack effect of the opcode.");
#define _OPCODE_STACK_EFFECT_METHODDEF \
{"stack_effect", (PyCFunction)_opcode_stack_effect, METH_VARARGS, _opcode_stack_effect__doc__},
static int
-_opcode_stack_effect_impl(PyObject *module, int opcode, int group_right_1, int oparg);
+_opcode_stack_effect_impl(PyModuleDef *module, int opcode, int group_right_1, int oparg);
static PyObject *
-_opcode_stack_effect(PyObject *module, PyObject *args)
+_opcode_stack_effect(PyModuleDef *module, PyObject *args)
{
PyObject *return_value = NULL;
int opcode;
@@ -62,8 +63,8 @@ exit:
}
static int
-_opcode_stack_effect_impl(PyObject *module, int opcode, int group_right_1, int oparg)
-/*[clinic checksum: 2312ded40abc9bcbce718942de21f53e61a2dfd3]*/
+_opcode_stack_effect_impl(PyModuleDef *module, int opcode, int group_right_1, int oparg)
+/*[clinic checksum: e880e62dc7b0de73403026eaf4f8074aa106358b]*/
{
int effect;
if (HAS_ARG(opcode)) {
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 3362454..9662eb8 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2842,6 +2842,33 @@ test_pyobject_setallocators(PyObject *self)
return test_setallocators(PYMEM_DOMAIN_OBJ);
}
+PyDoc_STRVAR(docstring_empty,
+""
+);
+
+PyDoc_STRVAR(docstring_no_signature,
+"This docstring has no signature."
+);
+
+PyDoc_STRVAR(docstring_with_invalid_signature,
+"docstring_with_invalid_signature (boo)\n"
+"\n"
+"This docstring has an invalid signature."
+);
+
+PyDoc_STRVAR(docstring_with_signature,
+"docstring_with_signature(sig)\n"
+"This docstring has a valid signature."
+);
+
+PyDoc_STRVAR(docstring_with_signature_and_extra_newlines,
+"docstring_with_signature_and_extra_newlines(parameter)\n"
+"\n"
+"\n"
+"\n"
+"This docstring has a valid signature and some extra newlines."
+);
+
static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@@ -2953,6 +2980,23 @@ static PyMethodDef TestMethods[] = {
(PyCFunction)test_pymem_setallocators, METH_NOARGS},
{"test_pyobject_setallocators",
(PyCFunction)test_pyobject_setallocators, METH_NOARGS},
+ {"no_docstring",
+ (PyCFunction)test_with_docstring, METH_NOARGS},
+ {"docstring_empty",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ docstring_empty},
+ {"docstring_no_signature",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ docstring_no_signature},
+ {"docstring_with_invalid_signature",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ docstring_with_invalid_signature},
+ {"docstring_with_signature",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ docstring_with_signature},
+ {"docstring_with_signature_and_extra_newlines",
+ (PyCFunction)test_with_docstring, METH_NOARGS,
+ docstring_with_signature_and_extra_newlines},
{NULL, NULL} /* sentinel */
};
diff --git a/Modules/_weakref.c b/Modules/_weakref.c
index af845ff..80de5da4 100644
--- a/Modules/_weakref.c
+++ b/Modules/_weakref.c
@@ -5,8 +5,11 @@
((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o))
/*[clinic]
-
module _weakref
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
+/*[clinic]
_weakref.getweakrefcount -> Py_ssize_t
@@ -17,9 +20,8 @@ Return the number of weak references to 'object'.
[clinic]*/
PyDoc_STRVAR(_weakref_getweakrefcount__doc__,
-"Return the number of weak references to \'object\'.\n"
-"\n"
-"_weakref.getweakrefcount(object)");
+"getweakrefcount(object)\n"
+"Return the number of weak references to \'object\'.");
#define _WEAKREF_GETWEAKREFCOUNT_METHODDEF \
{"getweakrefcount", (PyCFunction)_weakref_getweakrefcount, METH_O, _weakref_getweakrefcount__doc__},
@@ -43,7 +45,7 @@ exit:
static Py_ssize_t
_weakref_getweakrefcount_impl(PyModuleDef *module, PyObject *object)
-/*[clinic checksum: 015113be0c9a0a8672d35df10c63e3642cc23da4]*/
+/*[clinic checksum: 436e8fbe0297434375f039d8c2d9fc3a9bbe773c]*/
{
PyWeakReference **list;
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 9143fea..d0bd2d9 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -190,7 +190,10 @@ corresponding Unix manual entries for more information on calls.");
#endif /* ! __WATCOMC__ || __QNX__ */
-
+/*[clinic]
+module os
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
#ifndef _MSC_VER
@@ -2404,7 +2407,6 @@ class dir_fd_converter(CConverter):
/*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic]
-module os
os.stat -> object(doc_default='stat_result')
@@ -2435,9 +2437,9 @@ It's an error to use dir_fd or follow_symlinks when specifying path as
[clinic]*/
PyDoc_STRVAR(os_stat__doc__,
+"stat(path, *, dir_fd=None, follow_symlinks=True)\n"
"Perform a stat system call on the given path.\n"
"\n"
-"os.stat(path, *, dir_fd=None, follow_symlinks=True) -> stat_result\n"
" path\n"
" Path to be examined; can be string, bytes, or open-file-descriptor int.\n"
" dir_fd\n"
@@ -2486,7 +2488,7 @@ exit:
static PyObject *
os_stat_impl(PyModuleDef *module, path_t *path, int dir_fd, int follow_symlinks)
-/*[clinic checksum: b08112eff0ceab3ec2c72352da95ce73f245d104]*/
+/*[clinic checksum: 85a71ad602e89f8e280118da976f70cd2f9abdf1]*/
{
return posix_do_stat("stat", path, dir_fd, follow_symlinks);
}
@@ -2567,9 +2569,9 @@ Note that most operations will use the effective uid/gid, therefore this
[clinic]*/
PyDoc_STRVAR(os_access__doc__,
+"access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True)\n"
"Use the real uid/gid to test for access to a path.\n"
"\n"
-"os.access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True) -> True if granted, False otherwise\n"
" path\n"
" Path to be tested; can be string, bytes, or open-file-descriptor int.\n"
" mode\n"
@@ -2587,7 +2589,6 @@ PyDoc_STRVAR(os_access__doc__,
" access will examine the symbolic link itself instead of the file\n"
" the link points to.\n"
"\n"
-"{parameters}\n"
"dir_fd, effective_ids, and follow_symlinks may not be implemented\n"
" on your platform. If they are unavailable, using them will raise a\n"
" NotImplementedError.\n"
@@ -2628,7 +2629,7 @@ exit:
static PyObject *
os_access_impl(PyModuleDef *module, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks)
-/*[clinic checksum: b9f8ececb061d31b64220c29526bfee642d1b602]*/
+/*[clinic checksum: 636e835c36562a2fc11acab75314634127fdf769]*/
{
PyObject *return_value = NULL;
@@ -2724,9 +2725,9 @@ Return the name of the terminal device connected to 'fd'.
[clinic]*/
PyDoc_STRVAR(os_ttyname__doc__,
+"ttyname(fd)\n"
"Return the name of the terminal device connected to \'fd\'.\n"
"\n"
-"os.ttyname(fd)\n"
" fd\n"
" Integer file descriptor handle.");
@@ -2758,7 +2759,7 @@ exit:
static char *
os_ttyname_impl(PyModuleDef *module, int fd)
-/*[clinic checksum: 61e4e525984cb293f949ccae6ae393c0011dfe8e]*/
+/*[clinic checksum: 0f368134dc0a7f21f25185e2e6bacf7675fb473a]*/
{
char *ret;
diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c
index 62ab957..eca0054 100644
--- a/Modules/unicodedata.c
+++ b/Modules/unicodedata.c
@@ -17,6 +17,12 @@
#include "ucnhash.h"
#include "structmember.h"
+/*[clinic]
+module unicodedata
+class unicodedata.UCD
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
/* character properties */
typedef struct {
@@ -108,8 +114,7 @@ static Py_UCS4 getuchar(PyUnicodeObject *obj)
/* --- Module API --------------------------------------------------------- */
/*[clinic]
-module unicodedata
-class unicodedata.UCD
+
unicodedata.UCD.decimal
unichr: object(type='str')
@@ -124,10 +129,9 @@ not given, ValueError is raised.
[clinic]*/
PyDoc_STRVAR(unicodedata_UCD_decimal__doc__,
+"decimal(unichr, default=None)\n"
"Converts a Unicode character into its equivalent decimal value.\n"
"\n"
-"unicodedata.UCD.decimal(unichr, default=None)\n"
-"\n"
"Returns the decimal value assigned to the Unicode character unichr\n"
"as integer. If no such value is defined, default is returned, or, if\n"
"not given, ValueError is raised.");
@@ -157,7 +161,7 @@ exit:
static PyObject *
unicodedata_UCD_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value)
-/*[clinic checksum: a0980c387387287e2ac230c37d95b26f6903e0d2]*/
+/*[clinic checksum: 9576fa55f4ea0be82968af39dc9d0283e634beeb]*/
{
PyUnicodeObject *v = (PyUnicodeObject *)unichr;
int have_old = 0;
diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c
index b223aa7..425de98 100644
--- a/Modules/zlibmodule.c
+++ b/Modules/zlibmodule.c
@@ -165,6 +165,7 @@ PyZlib_Free(voidpf ctx, void *ptr)
}
/*[clinic]
+
zlib.compress
bytes: Py_buffer
Binary data to be compressed.
@@ -179,9 +180,9 @@ Returns compressed string.
[clinic]*/
PyDoc_STRVAR(zlib_compress__doc__,
+"compress(bytes, [level])\n"
"Returns compressed string.\n"
"\n"
-"zlib.compress(bytes, [level])\n"
" bytes\n"
" Binary data to be compressed.\n"
" level\n"
@@ -226,7 +227,7 @@ zlib_compress(PyModuleDef *module, PyObject *args)
static PyObject *
zlib_compress_impl(PyModuleDef *module, Py_buffer *bytes, int group_right_1, int level)
-/*[clinic checksum: 03e857836db25448d4d572da537eb7faf7695d71]*/
+/*[clinic checksum: f490708eff84be652b5ebe7fe622ab73ac12c888]*/
{
PyObject *ReturnVal = NULL;
Byte *input, *output = NULL;
@@ -742,6 +743,7 @@ save_unconsumed_input(compobject *self, int err)
}
/*[clinic]
+
zlib.Decompress.decompress
data: Py_buffer
@@ -760,9 +762,9 @@ Call the flush() method to clear these buffers.
[clinic]*/
PyDoc_STRVAR(zlib_Decompress_decompress__doc__,
+"decompress(data, max_length=0)\n"
"Return a string containing the decompressed version of the data.\n"
"\n"
-"zlib.Decompress.decompress(data, max_length=0)\n"
" data\n"
" The binary data to decompress.\n"
" max_length\n"
@@ -803,7 +805,7 @@ exit:
static PyObject *
zlib_Decompress_decompress_impl(PyObject *self, Py_buffer *data, unsigned int max_length)
-/*[clinic checksum: f83e91728d327462d7ccbee95299514f26b92253]*/
+/*[clinic checksum: 4683928665a1fa6987f5c57cada4a22807a78fbb]*/
{
compobject *zself = (compobject *)self;
int err;
@@ -1029,16 +1031,15 @@ Return a copy of the compression object.
[clinic]*/
PyDoc_STRVAR(zlib_Compress_copy__doc__,
-"Return a copy of the compression object.\n"
-"\n"
-"zlib.Compress.copy()");
+"copy()\n"
+"Return a copy of the compression object.");
#define ZLIB_COMPRESS_COPY_METHODDEF \
{"copy", (PyCFunction)zlib_Compress_copy, METH_NOARGS, zlib_Compress_copy__doc__},
static PyObject *
zlib_Compress_copy(PyObject *self)
-/*[clinic checksum: 2551952e72329f0f2beb48a1dde3780e485a220b]*/
+/*[clinic checksum: 8d30351f05defbc2b335c2a78d18f07aa367bb1d]*/
{
compobject *zself = (compobject *)self;
compobject *retval = NULL;
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 3c1b3bb..bfc730b 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -69,6 +69,11 @@ to the combined-table form.
#include "Python.h"
#include "stringlib/eq.h"
+/*[clinic]
+class dict
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
typedef struct {
/* Cached hash code of me_key. */
Py_hash_t me_hash;
@@ -2160,7 +2165,6 @@ dict_richcompare(PyObject *v, PyObject *w, int op)
}
/*[clinic]
-class dict
@coexist
dict.__contains__
@@ -2172,16 +2176,15 @@ True if D has a key k, else False"
[clinic]*/
PyDoc_STRVAR(dict___contains____doc__,
-"True if D has a key k, else False\"\n"
-"\n"
-"dict.__contains__(key)");
+"__contains__(key)\n"
+"True if D has a key k, else False\"");
#define DICT___CONTAINS___METHODDEF \
{"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__},
static PyObject *
dict___contains__(PyObject *self, PyObject *key)
-/*[clinic checksum: 61c5c802ea1d35699a1a754f1f3538ea9b259cf4]*/
+/*[clinic checksum: 3bbac5ce898ae630d9668fa1c8b3afb645ff22e8]*/
{
register PyDictObject *mp = (PyDictObject *)self;
Py_hash_t hash;
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 11c8b6e..ca21a68 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -159,15 +159,75 @@ meth_dealloc(PyCFunctionObject *m)
}
}
+/*
+ * finds the docstring's introspection signature.
+ * if present, returns a pointer pointing to the first '('.
+ * otherwise returns NULL.
+ */
+static const char *find_signature(PyCFunctionObject *m)
+{
+ const char *trace = m->m_ml->ml_doc;
+ const char *name = m->m_ml->ml_name;
+ size_t length;
+ if (!trace || !name)
+ return NULL;
+ length = strlen(name);
+ if (strncmp(trace, name, length))
+ return NULL;
+ trace += length;
+ if (*trace != '(')
+ return NULL;
+ return trace;
+}
+
+/*
+ * skips to the end of the docstring's instrospection signature.
+ */
+static const char *skip_signature(const char *trace)
+{
+ while (*trace && *trace != '\n')
+ trace++;
+ return trace;
+}
+
+static const char *skip_eols(const char *trace)
+{
+ while (*trace == '\n')
+ trace++;
+ return trace;
+}
+
+static PyObject *
+meth_get__text_signature__(PyCFunctionObject *m, void *closure)
+{
+ const char *start = find_signature(m);
+ const char *trace;
+
+ if (!start) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ trace = skip_signature(start);
+ return PyUnicode_FromStringAndSize(start, trace - start);
+}
+
static PyObject *
meth_get__doc__(PyCFunctionObject *m, void *closure)
{
- const char *doc = m->m_ml->ml_doc;
+ const char *doc = find_signature(m);
+
+ if (doc)
+ doc = skip_eols(skip_signature(doc));
+ else
+ doc = m->m_ml->ml_doc;
+
+ if (!doc) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
- if (doc != NULL)
- return PyUnicode_FromString(doc);
- Py_INCREF(Py_None);
- return Py_None;
+ return PyUnicode_FromString(doc);
}
static PyObject *
@@ -236,6 +296,7 @@ static PyGetSetDef meth_getsets [] = {
{"__name__", (getter)meth_get__name__, NULL, NULL},
{"__qualname__", (getter)meth_get__qualname__, NULL, NULL},
{"__self__", (getter)meth_get__self__, NULL, NULL},
+ {"__text_signature__", (getter)meth_get__text_signature__, NULL, NULL},
{0}
};
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index 1f3164c..34d51e4 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -47,6 +47,11 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <windows.h>
#endif
+/*[clinic]
+class str
+[clinic]*/
+/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
+
/* --- Globals ------------------------------------------------------------
NOTE: In the interpreter's initialization phase, some globals are currently
@@ -12883,7 +12888,6 @@ unicode_swapcase(PyObject *self)
}
/*[clinic]
-class str
@staticmethod
str.maketrans as unicode_maketrans
@@ -12908,10 +12912,9 @@ must be a string, whose characters will be mapped to None in the result.
[clinic]*/
PyDoc_STRVAR(unicode_maketrans__doc__,
+"maketrans(x, y=None, z=None)\n"
"Return a translation table usable for str.translate().\n"
"\n"
-"str.maketrans(x, y=None, z=None)\n"
-"\n"
"If there is only one argument, it must be a dictionary mapping Unicode\n"
"ordinals (integers) or characters to Unicode ordinals, strings or None.\n"
"Character keys will be then converted to ordinals.\n"
@@ -12946,7 +12949,7 @@ exit:
static PyObject *
unicode_maketrans_impl(void *null, PyObject *x, PyObject *y, PyObject *z)
-/*[clinic checksum: 6d522e3aea2f2e123da3c5d367132a99d803f9b9]*/
+/*[clinic checksum: 7f76f414a0dfd0c614e0d4717872eeb520516da7]*/
{
PyObject *new = NULL, *key, *value;
Py_ssize_t i = 0;
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index d34c3da..3c40e06 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -24,17 +24,6 @@ import tempfile
import textwrap
# TODO:
-# converters for
-#
-# es
-# es#
-# et
-# et#
-# s#
-# u#
-# y#
-# z#
-# Z#
#
# soon:
#
@@ -44,12 +33,6 @@ import textwrap
# * max and min use positional only with an optional group
# and keyword-only
#
-# * Generate forward slash for docstring first line
-# (if I get positional-only syntax pep accepted)
-#
-# * Add "version" directive, so we can complain if the file
-# is too new for us.
-#
version = '1'
@@ -2441,7 +2424,7 @@ class DSLParser:
## docstring first line
##
- add(f.full_name)
+ add(f.name)
add('(')
# populate "right_bracket_count" field for every parameter
@@ -2498,29 +2481,32 @@ class DSLParser:
add(fix_right_bracket_count(0))
add(')')
- if f.return_converter.doc_default:
- add(' -> ')
- add(f.return_converter.doc_default)
+ # if f.return_converter.doc_default:
+ # add(' -> ')
+ # add(f.return_converter.doc_default)
docstring_first_line = output()
# now fix up the places where the brackets look wrong
docstring_first_line = docstring_first_line.replace(', ]', ',] ')
- # okay. now we're officially building the
- # "prototype" section.
- add(docstring_first_line)
-
+ # okay. now we're officially building the "parameters" section.
# create substitution text for {parameters}
+ spacer_line = False
for p in parameters:
if not p.docstring.strip():
continue
- add('\n')
+ if spacer_line:
+ add('\n')
+ else:
+ spacer_line = True
add(" ")
add(p.name)
add('\n')
add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " "))
- prototype = output()
+ parameters = output()
+ if parameters:
+ parameters += '\n'
##
## docstring body
@@ -2549,21 +2535,26 @@ class DSLParser:
elif len(lines) == 1:
# the docstring is only one line right now--the summary line.
# add an empty line after the summary line so we have space
- # between it and the {prototype} we're about to add.
+ # between it and the {parameters} we're about to add.
lines.append('')
- prototype_marker_count = len(docstring.split('{prototype}')) - 1
- if prototype_marker_count:
- fail('You may not specify {prototype} in a docstring!')
- # insert *after* the summary line
- lines.insert(2, '{prototype}\n')
+ parameters_marker_count = len(docstring.split('{parameters}')) - 1
+ if parameters_marker_count > 1:
+ fail('You may not specify {parameters} more than once in a docstring!')
+
+ if not parameters_marker_count:
+ # insert after summary line
+ lines.insert(2, '{parameters}')
+
+ # insert at front of docstring
+ lines.insert(0, docstring_first_line)
docstring = "\n".join(lines)
add(docstring)
docstring = output()
- docstring = linear_format(docstring, prototype=prototype)
+ docstring = linear_format(docstring, parameters=parameters)
docstring = docstring.rstrip()
return docstring