Issue #19674: inspect.signature() now produces a correct signature

for some builtins.
This commit is contained in:
Larry Hastings 2013-11-23 15:37:55 -08:00
parent 7fa6e1aeea
commit 44e2eaab54
18 changed files with 343 additions and 136 deletions

View File

@ -31,6 +31,7 @@ Here are some of the useful functions provided by this module:
__author__ = ('Ka-Ping Yee <ping@lfw.org>', __author__ = ('Ka-Ping Yee <ping@lfw.org>',
'Yury Selivanov <yselivanov@sprymix.com>') 'Yury Selivanov <yselivanov@sprymix.com>')
import ast
import importlib.machinery import importlib.machinery
import itertools import itertools
import linecache import linecache
@ -1461,6 +1462,9 @@ def signature(obj):
if isinstance(obj, types.FunctionType): if isinstance(obj, types.FunctionType):
return Signature.from_function(obj) return Signature.from_function(obj)
if isinstance(obj, types.BuiltinFunctionType):
return Signature.from_builtin(obj)
if isinstance(obj, functools.partial): if isinstance(obj, functools.partial):
sig = signature(obj.func) sig = signature(obj.func)
@ -1942,6 +1946,64 @@ class Signature:
return_annotation=annotations.get('return', _empty), return_annotation=annotations.get('return', _empty),
__validate_parameters__=False) __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 @property
def parameters(self): def parameters(self):
return self._parameters return self._parameters

View File

@ -916,20 +916,18 @@ class HTMLDoc(Doc):
reallink = realname reallink = realname
title = '<a name="%s"><strong>%s</strong></a> = %s' % ( title = '<a name="%s"><strong>%s</strong></a> = %s' % (
anchor, name, reallink) anchor, name, reallink)
if inspect.isfunction(object): argspec = None
args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann = \ if inspect.isfunction(object) or inspect.isbuiltin(object):
inspect.getfullargspec(object) signature = inspect.signature(object)
argspec = inspect.formatargspec( if signature:
args, varargs, kwonlyargs, kwdefaults, varkw, defaults, ann, argspec = str(signature)
formatvalue=self.formatvalue,
formatannotation=inspect.formatannotationrelativeto(object))
if realname == '<lambda>': if realname == '<lambda>':
title = '<strong>%s</strong> <em>lambda</em> ' % name title = '<strong>%s</strong> <em>lambda</em> ' % name
# XXX lambda's won't usually have func_annotations['return'] # XXX lambda's won't usually have func_annotations['return']
# since the syntax doesn't support but it is possible. # since the syntax doesn't support but it is possible.
# So removing parentheses isn't truly safe. # So removing parentheses isn't truly safe.
argspec = argspec[1:-1] # remove parentheses argspec = argspec[1:-1] # remove parentheses
else: if not argspec:
argspec = '(...)' argspec = '(...)'
decl = title + argspec + (note and self.grey( decl = title + argspec + (note and self.grey(
@ -1313,20 +1311,18 @@ location listed above.
cl.__dict__[realname] is object): cl.__dict__[realname] is object):
skipdocs = 1 skipdocs = 1
title = self.bold(name) + ' = ' + realname title = self.bold(name) + ' = ' + realname
if inspect.isfunction(object): argspec = None
args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann = \ if inspect.isfunction(object) or inspect.isbuiltin(object):
inspect.getfullargspec(object) signature = inspect.signature(object)
argspec = inspect.formatargspec( if signature:
args, varargs, varkw, defaults, kwonlyargs, kwdefaults, ann, argspec = str(signature)
formatvalue=self.formatvalue,
formatannotation=inspect.formatannotationrelativeto(object))
if realname == '<lambda>': if realname == '<lambda>':
title = self.bold(name) + ' lambda ' title = self.bold(name) + ' lambda '
# XXX lambda's won't usually have func_annotations['return'] # XXX lambda's won't usually have func_annotations['return']
# since the syntax doesn't support but it is possible. # since the syntax doesn't support but it is possible.
# So removing parentheses isn't truly safe. # So removing parentheses isn't truly safe.
argspec = argspec[1:-1] # remove parentheses argspec = argspec[1:-1] # remove parentheses
else: if not argspec:
argspec = '(...)' argspec = '(...)'
decl = title + argspec + note decl = title + argspec + note

View File

@ -109,6 +109,35 @@ class CAPITest(unittest.TestCase):
self.assertRaises(TypeError, _posixsubprocess.fork_exec, 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) 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.') @unittest.skipUnless(threading, 'Threading required for this test.')
class TestPendingCalls(unittest.TestCase): class TestPendingCalls(unittest.TestCase):

View File

@ -1588,10 +1588,9 @@ class TestSignatureObject(unittest.TestCase):
with self.assertRaisesRegex(ValueError, 'not supported by signature'): with self.assertRaisesRegex(ValueError, 'not supported by signature'):
# support for 'method-wrapper' # support for 'method-wrapper'
inspect.signature(min.__call__) inspect.signature(min.__call__)
with self.assertRaisesRegex(ValueError, self.assertEqual(inspect.signature(min), None)
'no signature found for builtin function'): signature = inspect.signature(os.stat)
# support for 'method-wrapper' self.assertTrue(isinstance(signature, inspect.Signature))
inspect.signature(min)
def test_signature_on_non_function(self): def test_signature_on_non_function(self):
with self.assertRaisesRegex(TypeError, 'is not a callable object'): with self.assertRaisesRegex(TypeError, 'is not a callable object'):

View File

@ -68,6 +68,9 @@ Core and Builtins
Library Library
------- -------
- Issue #19674: inspect.signature() now produces a correct signature
for some builtins.
- Issue #19722: Added opcode.stack_effect(), which - Issue #19722: Added opcode.stack_effect(), which
computes the stack effect of bytecode instructions. computes the stack effect of bytecode instructions.

View File

@ -134,6 +134,12 @@ typedef chtype attr_t; /* No attr_t type is available */
#define STRICT_SYSV_CURSES #define STRICT_SYSV_CURSES
#endif #endif
/*[clinic]
module curses
class curses.window
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/* Definition of exception curses.error */ /* Definition of exception curses.error */
static PyObject *PyCursesError; static PyObject *PyCursesError;
@ -550,8 +556,6 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
/* Addch, Addstr, Addnstr */ /* Addch, Addstr, Addnstr */
/*[clinic] /*[clinic]
module curses
class curses.window
curses.window.addch curses.window.addch
@ -580,9 +584,9 @@ current settings for the window object.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(curses_window_addch__doc__, PyDoc_STRVAR(curses_window_addch__doc__,
"addch([x, y,] ch, [attr])\n"
"Paint character ch at (y, x) with attributes attr.\n" "Paint character ch at (y, x) with attributes attr.\n"
"\n" "\n"
"curses.window.addch([x, y,] ch, [attr])\n"
" x\n" " x\n"
" X-coordinate.\n" " X-coordinate.\n"
" y\n" " y\n"
@ -646,7 +650,7 @@ curses_window_addch(PyObject *self, PyObject *args)
static PyObject * static PyObject *
curses_window_addch_impl(PyObject *self, int group_left_1, int x, int y, PyObject *ch, int group_right_1, long attr) 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; PyCursesWindowObject *cwself = (PyCursesWindowObject *)self;
int coordinates_group = group_left_1; int coordinates_group = group_left_1;

View File

@ -16,6 +16,12 @@
#include "datetime.h" #include "datetime.h"
#undef Py_BUILD_CORE #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 /* 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 * 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 * 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] /*[clinic]
module datetime
class datetime.datetime
@classmethod @classmethod
datetime.datetime.now datetime.datetime.now
@ -4155,9 +4159,9 @@ If no tz is specified, uses local timezone.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(datetime_datetime_now__doc__, PyDoc_STRVAR(datetime_datetime_now__doc__,
"now(tz=None)\n"
"Returns new datetime object representing current time local to tz.\n" "Returns new datetime object representing current time local to tz.\n"
"\n" "\n"
"datetime.datetime.now(tz=None)\n"
" tz\n" " tz\n"
" Timezone object.\n" " Timezone object.\n"
"\n" "\n"
@ -4188,7 +4192,7 @@ exit:
static PyObject * static PyObject *
datetime_datetime_now_impl(PyTypeObject *cls, PyObject *tz) datetime_datetime_now_impl(PyTypeObject *cls, PyObject *tz)
/*[clinic checksum: 5e61647d5d1feaf1ab096c5406ccea17bb7b061c]*/ /*[clinic checksum: ca3d26a423b3f633b260c7622e303f0915a96f7c]*/
{ {
PyObject *self; PyObject *self;

View File

@ -28,6 +28,12 @@ static char *which_dbm = "Berkeley DB";
#error "No ndbm.h available!" #error "No ndbm.h available!"
#endif #endif
/*[clinic]
module dbm
class dbm.dbm
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
int di_size; /* -1 means recompute */ int di_size; /* -1 means recompute */
@ -43,12 +49,6 @@ static PyTypeObject Dbmtype;
static PyObject *DbmError; static PyObject *DbmError;
/*[clinic]
module dbm
class dbm.dbm
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[python] /*[python]
class dbmobject_converter(self_converter): class dbmobject_converter(self_converter):
type = "dbmobject *" type = "dbmobject *"
@ -278,9 +278,8 @@ Return the value for key if present, otherwise default.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(dbm_dbm_get__doc__, PyDoc_STRVAR(dbm_dbm_get__doc__,
"Return the value for key if present, otherwise default.\n" "get(key, [default])\n"
"\n" "Return the value for key if present, otherwise default.");
"dbm.dbm.get(key, [default])");
#define DBM_DBM_GET_METHODDEF \ #define DBM_DBM_GET_METHODDEF \
{"get", (PyCFunction)dbm_dbm_get, METH_VARARGS, dbm_dbm_get__doc__}, {"get", (PyCFunction)dbm_dbm_get, METH_VARARGS, dbm_dbm_get__doc__},
@ -318,7 +317,7 @@ dbm_dbm_get(PyObject *self, PyObject *args)
static PyObject * static PyObject *
dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, int group_right_1, PyObject *default_value) 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; datum dbm_key, val;
@ -461,9 +460,9 @@ Return a database object.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(dbmopen__doc__, PyDoc_STRVAR(dbmopen__doc__,
"open(filename, flags=\'r\', mode=0o666)\n"
"Return a database object.\n" "Return a database object.\n"
"\n" "\n"
"dbm.open(filename, flags=\'r\', mode=0o666)\n"
" filename\n" " filename\n"
" The filename to open.\n" " The filename to open.\n"
" flags\n" " flags\n"
@ -498,7 +497,7 @@ exit:
static PyObject * static PyObject *
dbmopen_impl(PyModuleDef *module, const char *filename, const char *flags, int mode) dbmopen_impl(PyModuleDef *module, const char *filename, const char *flags, int mode)
/*[clinic checksum: c1f2036017ec36a43ac6f59893732751e67c19d5]*/ /*[clinic checksum: fb265f75641553ccd963f84c143b35c11f9121fc]*/
{ {
int iflags; int iflags;

View File

@ -1,11 +1,13 @@
#include "Python.h" #include "Python.h"
#include "opcode.h" #include "opcode.h"
/*[clinic]
module _opcode
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic] /*[clinic]
module _opcode
_opcode.stack_effect -> int _opcode.stack_effect -> int
opcode: int opcode: int
@ -19,18 +21,17 @@ Compute the stack effect of the opcode.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(_opcode_stack_effect__doc__, PyDoc_STRVAR(_opcode_stack_effect__doc__,
"Compute the stack effect of the opcode.\n" "stack_effect(opcode, [oparg])\n"
"\n" "Compute the stack effect of the opcode.");
"_opcode.stack_effect(opcode, [oparg])");
#define _OPCODE_STACK_EFFECT_METHODDEF \ #define _OPCODE_STACK_EFFECT_METHODDEF \
{"stack_effect", (PyCFunction)_opcode_stack_effect, METH_VARARGS, _opcode_stack_effect__doc__}, {"stack_effect", (PyCFunction)_opcode_stack_effect, METH_VARARGS, _opcode_stack_effect__doc__},
static int 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 * static PyObject *
_opcode_stack_effect(PyObject *module, PyObject *args) _opcode_stack_effect(PyModuleDef *module, PyObject *args)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
int opcode; int opcode;
@ -62,8 +63,8 @@ exit:
} }
static int 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)
/*[clinic checksum: 2312ded40abc9bcbce718942de21f53e61a2dfd3]*/ /*[clinic checksum: e880e62dc7b0de73403026eaf4f8074aa106358b]*/
{ {
int effect; int effect;
if (HAS_ARG(opcode)) { if (HAS_ARG(opcode)) {

View File

@ -2842,6 +2842,33 @@ test_pyobject_setallocators(PyObject *self)
return test_setallocators(PYMEM_DOMAIN_OBJ); 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[] = { static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS},
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS}, {"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@ -2953,6 +2980,23 @@ static PyMethodDef TestMethods[] = {
(PyCFunction)test_pymem_setallocators, METH_NOARGS}, (PyCFunction)test_pymem_setallocators, METH_NOARGS},
{"test_pyobject_setallocators", {"test_pyobject_setallocators",
(PyCFunction)test_pyobject_setallocators, METH_NOARGS}, (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 */ {NULL, NULL} /* sentinel */
}; };

View File

@ -5,8 +5,11 @@
((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o)) ((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o))
/*[clinic] /*[clinic]
module _weakref module _weakref
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic]
_weakref.getweakrefcount -> Py_ssize_t _weakref.getweakrefcount -> Py_ssize_t
@ -17,9 +20,8 @@ Return the number of weak references to 'object'.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(_weakref_getweakrefcount__doc__, PyDoc_STRVAR(_weakref_getweakrefcount__doc__,
"Return the number of weak references to \'object\'.\n" "getweakrefcount(object)\n"
"\n" "Return the number of weak references to \'object\'.");
"_weakref.getweakrefcount(object)");
#define _WEAKREF_GETWEAKREFCOUNT_METHODDEF \ #define _WEAKREF_GETWEAKREFCOUNT_METHODDEF \
{"getweakrefcount", (PyCFunction)_weakref_getweakrefcount, METH_O, _weakref_getweakrefcount__doc__}, {"getweakrefcount", (PyCFunction)_weakref_getweakrefcount, METH_O, _weakref_getweakrefcount__doc__},
@ -43,7 +45,7 @@ exit:
static Py_ssize_t static Py_ssize_t
_weakref_getweakrefcount_impl(PyModuleDef *module, PyObject *object) _weakref_getweakrefcount_impl(PyModuleDef *module, PyObject *object)
/*[clinic checksum: 015113be0c9a0a8672d35df10c63e3642cc23da4]*/ /*[clinic checksum: 436e8fbe0297434375f039d8c2d9fc3a9bbe773c]*/
{ {
PyWeakReference **list; PyWeakReference **list;

View File

@ -190,7 +190,10 @@ corresponding Unix manual entries for more information on calls.");
#endif /* ! __WATCOMC__ || __QNX__ */ #endif /* ! __WATCOMC__ || __QNX__ */
/*[clinic]
module os
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
#ifndef _MSC_VER #ifndef _MSC_VER
@ -2404,7 +2407,6 @@ class dir_fd_converter(CConverter):
/*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/ /*[python checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/*[clinic] /*[clinic]
module os
os.stat -> object(doc_default='stat_result') 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]*/ [clinic]*/
PyDoc_STRVAR(os_stat__doc__, PyDoc_STRVAR(os_stat__doc__,
"stat(path, *, dir_fd=None, follow_symlinks=True)\n"
"Perform a stat system call on the given path.\n" "Perform a stat system call on the given path.\n"
"\n" "\n"
"os.stat(path, *, dir_fd=None, follow_symlinks=True) -> stat_result\n"
" path\n" " path\n"
" Path to be examined; can be string, bytes, or open-file-descriptor int.\n" " Path to be examined; can be string, bytes, or open-file-descriptor int.\n"
" dir_fd\n" " dir_fd\n"
@ -2486,7 +2488,7 @@ exit:
static PyObject * static PyObject *
os_stat_impl(PyModuleDef *module, path_t *path, int dir_fd, int follow_symlinks) 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); 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]*/ [clinic]*/
PyDoc_STRVAR(os_access__doc__, 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" "Use the real uid/gid to test for access to a path.\n"
"\n" "\n"
"os.access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True) -> True if granted, False otherwise\n"
" path\n" " path\n"
" Path to be tested; can be string, bytes, or open-file-descriptor int.\n" " Path to be tested; can be string, bytes, or open-file-descriptor int.\n"
" mode\n" " mode\n"
@ -2587,7 +2589,6 @@ PyDoc_STRVAR(os_access__doc__,
" access will examine the symbolic link itself instead of the file\n" " access will examine the symbolic link itself instead of the file\n"
" the link points to.\n" " the link points to.\n"
"\n" "\n"
"{parameters}\n"
"dir_fd, effective_ids, and follow_symlinks may not be implemented\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" " on your platform. If they are unavailable, using them will raise a\n"
" NotImplementedError.\n" " NotImplementedError.\n"
@ -2628,7 +2629,7 @@ exit:
static PyObject * static PyObject *
os_access_impl(PyModuleDef *module, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks) 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; PyObject *return_value = NULL;
@ -2724,9 +2725,9 @@ Return the name of the terminal device connected to 'fd'.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(os_ttyname__doc__, PyDoc_STRVAR(os_ttyname__doc__,
"ttyname(fd)\n"
"Return the name of the terminal device connected to \'fd\'.\n" "Return the name of the terminal device connected to \'fd\'.\n"
"\n" "\n"
"os.ttyname(fd)\n"
" fd\n" " fd\n"
" Integer file descriptor handle."); " Integer file descriptor handle.");
@ -2758,7 +2759,7 @@ exit:
static char * static char *
os_ttyname_impl(PyModuleDef *module, int fd) os_ttyname_impl(PyModuleDef *module, int fd)
/*[clinic checksum: 61e4e525984cb293f949ccae6ae393c0011dfe8e]*/ /*[clinic checksum: 0f368134dc0a7f21f25185e2e6bacf7675fb473a]*/
{ {
char *ret; char *ret;

View File

@ -17,6 +17,12 @@
#include "ucnhash.h" #include "ucnhash.h"
#include "structmember.h" #include "structmember.h"
/*[clinic]
module unicodedata
class unicodedata.UCD
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/* character properties */ /* character properties */
typedef struct { typedef struct {
@ -108,8 +114,7 @@ static Py_UCS4 getuchar(PyUnicodeObject *obj)
/* --- Module API --------------------------------------------------------- */ /* --- Module API --------------------------------------------------------- */
/*[clinic] /*[clinic]
module unicodedata
class unicodedata.UCD
unicodedata.UCD.decimal unicodedata.UCD.decimal
unichr: object(type='str') unichr: object(type='str')
@ -124,10 +129,9 @@ not given, ValueError is raised.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(unicodedata_UCD_decimal__doc__, PyDoc_STRVAR(unicodedata_UCD_decimal__doc__,
"decimal(unichr, default=None)\n"
"Converts a Unicode character into its equivalent decimal value.\n" "Converts a Unicode character into its equivalent decimal value.\n"
"\n" "\n"
"unicodedata.UCD.decimal(unichr, default=None)\n"
"\n"
"Returns the decimal value assigned to the Unicode character unichr\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" "as integer. If no such value is defined, default is returned, or, if\n"
"not given, ValueError is raised."); "not given, ValueError is raised.");
@ -157,7 +161,7 @@ exit:
static PyObject * static PyObject *
unicodedata_UCD_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value) unicodedata_UCD_decimal_impl(PyObject *self, PyObject *unichr, PyObject *default_value)
/*[clinic checksum: a0980c387387287e2ac230c37d95b26f6903e0d2]*/ /*[clinic checksum: 9576fa55f4ea0be82968af39dc9d0283e634beeb]*/
{ {
PyUnicodeObject *v = (PyUnicodeObject *)unichr; PyUnicodeObject *v = (PyUnicodeObject *)unichr;
int have_old = 0; int have_old = 0;

View File

@ -165,6 +165,7 @@ PyZlib_Free(voidpf ctx, void *ptr)
} }
/*[clinic] /*[clinic]
zlib.compress zlib.compress
bytes: Py_buffer bytes: Py_buffer
Binary data to be compressed. Binary data to be compressed.
@ -179,9 +180,9 @@ Returns compressed string.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(zlib_compress__doc__, PyDoc_STRVAR(zlib_compress__doc__,
"compress(bytes, [level])\n"
"Returns compressed string.\n" "Returns compressed string.\n"
"\n" "\n"
"zlib.compress(bytes, [level])\n"
" bytes\n" " bytes\n"
" Binary data to be compressed.\n" " Binary data to be compressed.\n"
" level\n" " level\n"
@ -226,7 +227,7 @@ zlib_compress(PyModuleDef *module, PyObject *args)
static PyObject * static PyObject *
zlib_compress_impl(PyModuleDef *module, Py_buffer *bytes, int group_right_1, int level) zlib_compress_impl(PyModuleDef *module, Py_buffer *bytes, int group_right_1, int level)
/*[clinic checksum: 03e857836db25448d4d572da537eb7faf7695d71]*/ /*[clinic checksum: f490708eff84be652b5ebe7fe622ab73ac12c888]*/
{ {
PyObject *ReturnVal = NULL; PyObject *ReturnVal = NULL;
Byte *input, *output = NULL; Byte *input, *output = NULL;
@ -742,6 +743,7 @@ save_unconsumed_input(compobject *self, int err)
} }
/*[clinic] /*[clinic]
zlib.Decompress.decompress zlib.Decompress.decompress
data: Py_buffer data: Py_buffer
@ -760,9 +762,9 @@ Call the flush() method to clear these buffers.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(zlib_Decompress_decompress__doc__, PyDoc_STRVAR(zlib_Decompress_decompress__doc__,
"decompress(data, max_length=0)\n"
"Return a string containing the decompressed version of the data.\n" "Return a string containing the decompressed version of the data.\n"
"\n" "\n"
"zlib.Decompress.decompress(data, max_length=0)\n"
" data\n" " data\n"
" The binary data to decompress.\n" " The binary data to decompress.\n"
" max_length\n" " max_length\n"
@ -803,7 +805,7 @@ exit:
static PyObject * static PyObject *
zlib_Decompress_decompress_impl(PyObject *self, Py_buffer *data, unsigned int max_length) zlib_Decompress_decompress_impl(PyObject *self, Py_buffer *data, unsigned int max_length)
/*[clinic checksum: f83e91728d327462d7ccbee95299514f26b92253]*/ /*[clinic checksum: 4683928665a1fa6987f5c57cada4a22807a78fbb]*/
{ {
compobject *zself = (compobject *)self; compobject *zself = (compobject *)self;
int err; int err;
@ -1029,16 +1031,15 @@ Return a copy of the compression object.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(zlib_Compress_copy__doc__, PyDoc_STRVAR(zlib_Compress_copy__doc__,
"Return a copy of the compression object.\n" "copy()\n"
"\n" "Return a copy of the compression object.");
"zlib.Compress.copy()");
#define ZLIB_COMPRESS_COPY_METHODDEF \ #define ZLIB_COMPRESS_COPY_METHODDEF \
{"copy", (PyCFunction)zlib_Compress_copy, METH_NOARGS, zlib_Compress_copy__doc__}, {"copy", (PyCFunction)zlib_Compress_copy, METH_NOARGS, zlib_Compress_copy__doc__},
static PyObject * static PyObject *
zlib_Compress_copy(PyObject *self) zlib_Compress_copy(PyObject *self)
/*[clinic checksum: 2551952e72329f0f2beb48a1dde3780e485a220b]*/ /*[clinic checksum: 8d30351f05defbc2b335c2a78d18f07aa367bb1d]*/
{ {
compobject *zself = (compobject *)self; compobject *zself = (compobject *)self;
compobject *retval = NULL; compobject *retval = NULL;

View File

@ -69,6 +69,11 @@ to the combined-table form.
#include "Python.h" #include "Python.h"
#include "stringlib/eq.h" #include "stringlib/eq.h"
/*[clinic]
class dict
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
typedef struct { typedef struct {
/* Cached hash code of me_key. */ /* Cached hash code of me_key. */
Py_hash_t me_hash; Py_hash_t me_hash;
@ -2160,7 +2165,6 @@ dict_richcompare(PyObject *v, PyObject *w, int op)
} }
/*[clinic] /*[clinic]
class dict
@coexist @coexist
dict.__contains__ dict.__contains__
@ -2172,16 +2176,15 @@ True if D has a key k, else False"
[clinic]*/ [clinic]*/
PyDoc_STRVAR(dict___contains____doc__, PyDoc_STRVAR(dict___contains____doc__,
"True if D has a key k, else False\"\n" "__contains__(key)\n"
"\n" "True if D has a key k, else False\"");
"dict.__contains__(key)");
#define DICT___CONTAINS___METHODDEF \ #define DICT___CONTAINS___METHODDEF \
{"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__},
static PyObject * static PyObject *
dict___contains__(PyObject *self, PyObject *key) dict___contains__(PyObject *self, PyObject *key)
/*[clinic checksum: 61c5c802ea1d35699a1a754f1f3538ea9b259cf4]*/ /*[clinic checksum: 3bbac5ce898ae630d9668fa1c8b3afb645ff22e8]*/
{ {
register PyDictObject *mp = (PyDictObject *)self; register PyDictObject *mp = (PyDictObject *)self;
Py_hash_t hash; Py_hash_t hash;

View File

@ -159,17 +159,77 @@ 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 * static PyObject *
meth_get__doc__(PyCFunctionObject *m, void *closure) meth_get__doc__(PyCFunctionObject *m, void *closure)
{ {
const char *doc = m->m_ml->ml_doc; const char *doc = find_signature(m);
if (doc != NULL) if (doc)
return PyUnicode_FromString(doc); doc = skip_eols(skip_signature(doc));
else
doc = m->m_ml->ml_doc;
if (!doc) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
return PyUnicode_FromString(doc);
}
static PyObject * static PyObject *
meth_get__name__(PyCFunctionObject *m, void *closure) meth_get__name__(PyCFunctionObject *m, void *closure)
{ {
@ -236,6 +296,7 @@ static PyGetSetDef meth_getsets [] = {
{"__name__", (getter)meth_get__name__, NULL, NULL}, {"__name__", (getter)meth_get__name__, NULL, NULL},
{"__qualname__", (getter)meth_get__qualname__, NULL, NULL}, {"__qualname__", (getter)meth_get__qualname__, NULL, NULL},
{"__self__", (getter)meth_get__self__, NULL, NULL}, {"__self__", (getter)meth_get__self__, NULL, NULL},
{"__text_signature__", (getter)meth_get__text_signature__, NULL, NULL},
{0} {0}
}; };

View File

@ -47,6 +47,11 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <windows.h> #include <windows.h>
#endif #endif
/*[clinic]
class str
[clinic]*/
/*[clinic checksum: da39a3ee5e6b4b0d3255bfef95601890afd80709]*/
/* --- Globals ------------------------------------------------------------ /* --- Globals ------------------------------------------------------------
NOTE: In the interpreter's initialization phase, some globals are currently NOTE: In the interpreter's initialization phase, some globals are currently
@ -12883,7 +12888,6 @@ unicode_swapcase(PyObject *self)
} }
/*[clinic] /*[clinic]
class str
@staticmethod @staticmethod
str.maketrans as unicode_maketrans str.maketrans as unicode_maketrans
@ -12908,10 +12912,9 @@ must be a string, whose characters will be mapped to None in the result.
[clinic]*/ [clinic]*/
PyDoc_STRVAR(unicode_maketrans__doc__, PyDoc_STRVAR(unicode_maketrans__doc__,
"maketrans(x, y=None, z=None)\n"
"Return a translation table usable for str.translate().\n" "Return a translation table usable for str.translate().\n"
"\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" "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" "ordinals (integers) or characters to Unicode ordinals, strings or None.\n"
"Character keys will be then converted to ordinals.\n" "Character keys will be then converted to ordinals.\n"
@ -12946,7 +12949,7 @@ exit:
static PyObject * static PyObject *
unicode_maketrans_impl(void *null, PyObject *x, PyObject *y, PyObject *z) unicode_maketrans_impl(void *null, PyObject *x, PyObject *y, PyObject *z)
/*[clinic checksum: 6d522e3aea2f2e123da3c5d367132a99d803f9b9]*/ /*[clinic checksum: 7f76f414a0dfd0c614e0d4717872eeb520516da7]*/
{ {
PyObject *new = NULL, *key, *value; PyObject *new = NULL, *key, *value;
Py_ssize_t i = 0; Py_ssize_t i = 0;

View File

@ -24,17 +24,6 @@ import tempfile
import textwrap import textwrap
# TODO: # TODO:
# converters for
#
# es
# es#
# et
# et#
# s#
# u#
# y#
# z#
# Z#
# #
# soon: # soon:
# #
@ -44,12 +33,6 @@ import textwrap
# * max and min use positional only with an optional group # * max and min use positional only with an optional group
# and keyword-only # 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' version = '1'
@ -2441,7 +2424,7 @@ class DSLParser:
## docstring first line ## docstring first line
## ##
add(f.full_name) add(f.name)
add('(') add('(')
# populate "right_bracket_count" field for every parameter # populate "right_bracket_count" field for every parameter
@ -2498,29 +2481,32 @@ class DSLParser:
add(fix_right_bracket_count(0)) add(fix_right_bracket_count(0))
add(')') add(')')
if f.return_converter.doc_default: # if f.return_converter.doc_default:
add(' -> ') # add(' -> ')
add(f.return_converter.doc_default) # add(f.return_converter.doc_default)
docstring_first_line = output() docstring_first_line = output()
# now fix up the places where the brackets look wrong # now fix up the places where the brackets look wrong
docstring_first_line = docstring_first_line.replace(', ]', ',] ') docstring_first_line = docstring_first_line.replace(', ]', ',] ')
# okay. now we're officially building the # okay. now we're officially building the "parameters" section.
# "prototype" section.
add(docstring_first_line)
# create substitution text for {parameters} # create substitution text for {parameters}
spacer_line = False
for p in parameters: for p in parameters:
if not p.docstring.strip(): if not p.docstring.strip():
continue continue
if spacer_line:
add('\n') add('\n')
else:
spacer_line = True
add(" ") add(" ")
add(p.name) add(p.name)
add('\n') add('\n')
add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " ")) add(textwrap.indent(rstrip_lines(p.docstring.rstrip()), " "))
prototype = output() parameters = output()
if parameters:
parameters += '\n'
## ##
## docstring body ## docstring body
@ -2549,21 +2535,26 @@ class DSLParser:
elif len(lines) == 1: elif len(lines) == 1:
# the docstring is only one line right now--the summary line. # the docstring is only one line right now--the summary line.
# add an empty line after the summary line so we have space # 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('') lines.append('')
prototype_marker_count = len(docstring.split('{prototype}')) - 1 parameters_marker_count = len(docstring.split('{parameters}')) - 1
if prototype_marker_count: if parameters_marker_count > 1:
fail('You may not specify {prototype} in a docstring!') fail('You may not specify {parameters} more than once in a docstring!')
# insert *after* the summary line
lines.insert(2, '{prototype}\n') 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) docstring = "\n".join(lines)
add(docstring) add(docstring)
docstring = output() docstring = output()
docstring = linear_format(docstring, prototype=prototype) docstring = linear_format(docstring, parameters=parameters)
docstring = docstring.rstrip() docstring = docstring.rstrip()
return docstring return docstring