diff --git a/Include/object.h b/Include/object.h --- a/Include/object.h +++ b/Include/object.h @@ -492,6 +492,9 @@ PyAPI_FUNC(unsigned int) PyType_ClearCache(void); PyAPI_FUNC(void) PyType_Modified(PyTypeObject *); +PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc); +PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc); + /* Generic operations on objects */ struct _Py_Identifier; #ifndef Py_LIMITED_API diff --git a/Lib/inspect.py b/Lib/inspect.py --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1419,9 +1419,11 @@ _WrapperDescriptor = type(type.__call__) _MethodWrapper = type(all.__call__) +_ClassMethodWrapper = type(int.__dict__['from_bytes']) _NonUserDefinedCallables = (_WrapperDescriptor, _MethodWrapper, + _ClassMethodWrapper, types.BuiltinFunctionType) @@ -1460,12 +1462,13 @@ if sig is not None: return sig - if isinstance(obj, types.FunctionType): return Signature.from_function(obj) - if isinstance(obj, types.BuiltinFunctionType): - return Signature.from_builtin(obj) + if isinstance(obj, _NonUserDefinedCallables) or ismethoddescriptor(obj): + sig = Signature.from_builtin(obj) + if sig: + return sig if isinstance(obj, functools.partial): sig = signature(obj.func) @@ -2066,6 +2069,15 @@ kind = Parameter.VAR_KEYWORD p(f.args.kwarg, empty) + # strip off self if it's already been bound + if parameters and parameters[0].name == 'self': + if isbuiltin(func) and getattr(func, '__self__', None): + parameters.pop(0) + else: + # for builtins, self parameter is always positional-only! + p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY) + parameters[0] = p + return cls(parameters, return_annotation=cls.empty) diff --git a/Lib/pydoc.py b/Lib/pydoc.py --- a/Lib/pydoc.py +++ b/Lib/pydoc.py @@ -925,7 +925,10 @@ anchor, name, reallink) argspec = None if inspect.isfunction(object) or inspect.isbuiltin(object): - signature = inspect.signature(object) + try: + signature = inspect.signature(object) + except ValueError: + signature = None if signature: argspec = str(signature) if realname == '': @@ -1320,7 +1323,10 @@ title = self.bold(name) + ' = ' + realname argspec = None if inspect.isfunction(object) or inspect.isbuiltin(object): - signature = inspect.signature(object) + try: + signature = inspect.signature(object) + except ValueError: + signature = None if signature: argspec = str(signature) if realname == '': diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -1595,7 +1595,8 @@ "Signature information for builtins requires docstrings") def test_signature_on_builtins(self): # min doesn't have a signature (yet) - self.assertEqual(inspect.signature(min), None) + with self.assertRaises(ValueError): + inspect.signature(min) signature = inspect.signature(_testcapi.docstring_with_signature_with_defaults) self.assertTrue(isinstance(signature, inspect.Signature)) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -584,7 +584,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(curses_window_addch__doc__, -"addch([x, y,] ch, [attr])\n" +"addch(self, [x, y,] ch, [attr])\n" "Paint character ch at (y, x) with attributes attr.\n" "\n" " x\n" @@ -650,7 +650,7 @@ 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 end generated code: checksum=b073327add8197b6ba7fb96c87062422c8312954]*/ +/*[clinic end generated code: checksum=d36b1e845f8c5b424db59df9f2b7ec8e1cbc40e0]*/ { PyCursesWindowObject *cwself = (PyCursesWindowObject *)self; int coordinates_group = group_left_1; diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -279,7 +279,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(dbm_dbm_get__doc__, -"get(key, [default])\n" +"get(self, key, [default])\n" "Return the value for key if present, otherwise default."); #define DBM_DBM_GET_METHODDEF \ @@ -289,7 +289,7 @@ dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, int group_right_1, PyObject *default_value); static PyObject * -dbm_dbm_get(PyObject *self, PyObject *args) +dbm_dbm_get(dbmobject *dp, PyObject *args) { PyObject *return_value = NULL; const char *key; @@ -311,14 +311,14 @@ PyErr_SetString(PyExc_TypeError, "dbm.dbm.get requires 1 to 2 arguments"); return NULL; } - return_value = dbm_dbm_get_impl((dbmobject *)self, key, key_length, group_right_1, default_value); + return_value = dbm_dbm_get_impl(dp, key, key_length, group_right_1, default_value); return return_value; } 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 end generated code: checksum=2c3209571267017f1b9abbd19e1b521849fd5d4a]*/ +/*[clinic end generated code: checksum=f19f39e772897721ab7f7525283adf046797faa9]*/ { datum dbm_key, val; diff --git a/Modules/_pickle.c b/Modules/_pickle.c --- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -3889,7 +3889,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Pickler_clear_memo__doc__, -"clear_memo()\n" +"clear_memo(self)\n" "Clears the pickler\'s \"memo\".\n" "\n" "The memo is the data structure that remembers which objects the\n" @@ -3904,14 +3904,14 @@ _pickle_Pickler_clear_memo_impl(PicklerObject *self); static PyObject * -_pickle_Pickler_clear_memo(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_Pickler_clear_memo_impl((PicklerObject *)self); +_pickle_Pickler_clear_memo(PicklerObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_Pickler_clear_memo_impl(self); } static PyObject * _pickle_Pickler_clear_memo_impl(PicklerObject *self) -/*[clinic end generated code: checksum=015cc3c5befea86cb08b9396938477bebbea4157]*/ +/*[clinic end generated code: checksum=17b1165d8dcae5a2e90b1703bf5cbbfc26114c5a]*/ { if (self->memo) PyMemoTable_Clear(self->memo); @@ -3931,7 +3931,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Pickler_dump__doc__, -"dump(obj)\n" +"dump(self, obj)\n" "Write a pickled representation of the given object to the open file."); #define _PICKLE_PICKLER_DUMP_METHODDEF \ @@ -3939,7 +3939,7 @@ static PyObject * _pickle_Pickler_dump(PicklerObject *self, PyObject *obj) -/*[clinic end generated code: checksum=b72a69ec98737fabf66dae7c5a3210178bdbd3e6]*/ +/*[clinic end generated code: checksum=36db7f67c8bc05ca6f17b8ab57c54d64bfd0539e]*/ { /* Check whether the Pickler was initialized correctly (issue3664). Developers often forget to call __init__() in their subclasses, which @@ -4044,7 +4044,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Pickler___init____doc__, -"__init__(file, protocol=None, fix_imports=True)\n" +"__init__(self, file, protocol=None, fix_imports=True)\n" "This takes a binary file for writing a pickle data stream.\n" "\n" "The optional *protocol* argument tells the pickler to use the given\n" @@ -4068,7 +4068,7 @@ _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports); static int -_pickle_Pickler___init__(PyObject *self, PyObject *args, PyObject *kwargs) +_pickle_Pickler___init__(PicklerObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; static char *_keywords[] = {"file", "protocol", "fix_imports", NULL}; @@ -4080,7 +4080,7 @@ "O|Op:__init__", _keywords, &file, &protocol, &fix_imports)) goto exit; - return_value = _pickle_Pickler___init___impl((PicklerObject *)self, file, protocol, fix_imports); + return_value = _pickle_Pickler___init___impl(self, file, protocol, fix_imports); exit: return return_value; @@ -4088,7 +4088,7 @@ static int _pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports) -/*[clinic end generated code: checksum=10c8ea05194d08108471163d8202cf5e12975544]*/ +/*[clinic end generated code: checksum=7363358132991695c72a7bff46f2670cffecdf3e]*/ { _Py_IDENTIFIER(persistent_id); _Py_IDENTIFIER(dispatch_table); @@ -4164,7 +4164,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_PicklerMemoProxy_clear__doc__, -"clear()\n" +"clear(self)\n" "Remove all items from memo."); #define _PICKLE_PICKLERMEMOPROXY_CLEAR_METHODDEF \ @@ -4174,14 +4174,14 @@ _pickle_PicklerMemoProxy_clear_impl(PicklerMemoProxyObject *self); static PyObject * -_pickle_PicklerMemoProxy_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_PicklerMemoProxy_clear_impl((PicklerMemoProxyObject *)self); +_pickle_PicklerMemoProxy_clear(PicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_PicklerMemoProxy_clear_impl(self); } static PyObject * _pickle_PicklerMemoProxy_clear_impl(PicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=bf8dd8c8688d0c0f7a2e59a804c47375b740f2f0]*/ +/*[clinic end generated code: checksum=fb4a5ba40918b3eccc9bc1e9d6875cb2737127a9]*/ { if (self->pickler->memo) PyMemoTable_Clear(self->pickler->memo); @@ -4197,7 +4197,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_PicklerMemoProxy_copy__doc__, -"copy()\n" +"copy(self)\n" "Copy the memo to a new object."); #define _PICKLE_PICKLERMEMOPROXY_COPY_METHODDEF \ @@ -4207,14 +4207,14 @@ _pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self); static PyObject * -_pickle_PicklerMemoProxy_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_PicklerMemoProxy_copy_impl((PicklerMemoProxyObject *)self); +_pickle_PicklerMemoProxy_copy(PicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_PicklerMemoProxy_copy_impl(self); } static PyObject * _pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=72d46879dc658adbd3d28b5c82dd8dcfa6b9b124]*/ +/*[clinic end generated code: checksum=3d27d3005725f1828c9a92a38197811c54c64abb]*/ { Py_ssize_t i; PyMemoTable *memo; @@ -4260,7 +4260,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_PicklerMemoProxy___reduce____doc__, -"__reduce__()\n" +"__reduce__(self)\n" "Implement pickle support."); #define _PICKLE_PICKLERMEMOPROXY___REDUCE___METHODDEF \ @@ -4270,14 +4270,14 @@ _pickle_PicklerMemoProxy___reduce___impl(PicklerMemoProxyObject *self); static PyObject * -_pickle_PicklerMemoProxy___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_PicklerMemoProxy___reduce___impl((PicklerMemoProxyObject *)self); +_pickle_PicklerMemoProxy___reduce__(PicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_PicklerMemoProxy___reduce___impl(self); } static PyObject * _pickle_PicklerMemoProxy___reduce___impl(PicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=aad71c4d81d1ed8bf0d32362dd80a29b9f3b0d03]*/ +/*[clinic end generated code: checksum=2682cf8a3a5027def6328419001b086b047d47c8]*/ { PyObject *reduce_value, *dict_args; PyObject *contents = _pickle_PicklerMemoProxy_copy_impl(self); @@ -6299,7 +6299,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Unpickler_load__doc__, -"load()\n" +"load(self)\n" "Load a pickle.\n" "\n" "Read a pickled object representation from the open file object given\n" @@ -6320,7 +6320,7 @@ static PyObject * _pickle_Unpickler_load_impl(PyObject *self) -/*[clinic end generated code: checksum=9477099fe6a90748c13ff1a6dd92ba7ab7a89602]*/ +/*[clinic end generated code: checksum=fb1119422c5e03045d690d1cd6c457f1ca4c585d]*/ { UnpicklerObject *unpickler = (UnpicklerObject*)self; @@ -6363,7 +6363,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Unpickler_find_class__doc__, -"find_class(module_name, global_name)\n" +"find_class(self, module_name, global_name)\n" "Return an object from a specified module.\n" "\n" "If necessary, the module will be imported. Subclasses may override\n" @@ -6380,7 +6380,7 @@ _pickle_Unpickler_find_class_impl(UnpicklerObject *self, PyObject *module_name, PyObject *global_name); static PyObject * -_pickle_Unpickler_find_class(PyObject *self, PyObject *args) +_pickle_Unpickler_find_class(UnpicklerObject *self, PyObject *args) { PyObject *return_value = NULL; PyObject *module_name; @@ -6390,7 +6390,7 @@ 2, 2, &module_name, &global_name)) goto exit; - return_value = _pickle_Unpickler_find_class_impl((UnpicklerObject *)self, module_name, global_name); + return_value = _pickle_Unpickler_find_class_impl(self, module_name, global_name); exit: return return_value; @@ -6398,7 +6398,7 @@ static PyObject * _pickle_Unpickler_find_class_impl(UnpicklerObject *self, PyObject *module_name, PyObject *global_name) -/*[clinic end generated code: checksum=15ed4836fd5860425fff9ea7855d4f1f4413c170]*/ +/*[clinic end generated code: checksum=2b8d5398787c8ac7ea5d45f644433169e441003b]*/ { PyObject *global; PyObject *modules_dict; @@ -6581,7 +6581,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_Unpickler___init____doc__, -"__init__(file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n" +"__init__(self, file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n" "This takes a binary file for reading a pickle data stream.\n" "\n" "The protocol version of the pickle is detected automatically, so no\n" @@ -6607,7 +6607,7 @@ _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors); static int -_pickle_Unpickler___init__(PyObject *self, PyObject *args, PyObject *kwargs) +_pickle_Unpickler___init__(UnpicklerObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; static char *_keywords[] = {"file", "fix_imports", "encoding", "errors", NULL}; @@ -6620,7 +6620,7 @@ "O|$pss:__init__", _keywords, &file, &fix_imports, &encoding, &errors)) goto exit; - return_value = _pickle_Unpickler___init___impl((UnpicklerObject *)self, file, fix_imports, encoding, errors); + return_value = _pickle_Unpickler___init___impl(self, file, fix_imports, encoding, errors); exit: return return_value; @@ -6628,7 +6628,7 @@ static int _pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors) -/*[clinic end generated code: checksum=6936e9188104e45b1b15e1c11fe77b3965409471]*/ +/*[clinic end generated code: checksum=7a465d1de4422dcdcee8b986d2c1b712a55b3b24]*/ { _Py_IDENTIFIER(persistent_load); @@ -6698,7 +6698,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_clear__doc__, -"clear()\n" +"clear(self)\n" "Remove all items from memo."); #define _PICKLE_UNPICKLERMEMOPROXY_CLEAR_METHODDEF \ @@ -6708,14 +6708,14 @@ _pickle_UnpicklerMemoProxy_clear_impl(UnpicklerMemoProxyObject *self); static PyObject * -_pickle_UnpicklerMemoProxy_clear(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_UnpicklerMemoProxy_clear_impl((UnpicklerMemoProxyObject *)self); +_pickle_UnpicklerMemoProxy_clear(UnpicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_UnpicklerMemoProxy_clear_impl(self); } static PyObject * _pickle_UnpicklerMemoProxy_clear_impl(UnpicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=07adecee2181e5e268b2ff184360b1d88ad947f2]*/ +/*[clinic end generated code: checksum=32f6ee47e44405dd587f768f3690d47947bb5a8e]*/ { _Unpickler_MemoCleanup(self->unpickler); self->unpickler->memo = _Unpickler_NewMemo(self->unpickler->memo_size); @@ -6733,7 +6733,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_copy__doc__, -"copy()\n" +"copy(self)\n" "Copy the memo to a new object."); #define _PICKLE_UNPICKLERMEMOPROXY_COPY_METHODDEF \ @@ -6743,14 +6743,14 @@ _pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self); static PyObject * -_pickle_UnpicklerMemoProxy_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_UnpicklerMemoProxy_copy_impl((UnpicklerMemoProxyObject *)self); +_pickle_UnpicklerMemoProxy_copy(UnpicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_UnpicklerMemoProxy_copy_impl(self); } static PyObject * _pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=47b9f0cc12c5a54004252e1b4916822cdfa8a881]*/ +/*[clinic end generated code: checksum=ac3da80efc3b2548aa8b5c5358d0e82e615fce1d]*/ { Py_ssize_t i; PyObject *new_memo = PyDict_New(); @@ -6789,7 +6789,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(_pickle_UnpicklerMemoProxy___reduce____doc__, -"__reduce__()\n" +"__reduce__(self)\n" "Implement pickling support."); #define _PICKLE_UNPICKLERMEMOPROXY___REDUCE___METHODDEF \ @@ -6799,14 +6799,14 @@ _pickle_UnpicklerMemoProxy___reduce___impl(UnpicklerMemoProxyObject *self); static PyObject * -_pickle_UnpicklerMemoProxy___reduce__(PyObject *self, PyObject *Py_UNUSED(ignored)) -{ - return _pickle_UnpicklerMemoProxy___reduce___impl((UnpicklerMemoProxyObject *)self); +_pickle_UnpicklerMemoProxy___reduce__(UnpicklerMemoProxyObject *self, PyObject *Py_UNUSED(ignored)) +{ + return _pickle_UnpicklerMemoProxy___reduce___impl(self); } static PyObject * _pickle_UnpicklerMemoProxy___reduce___impl(UnpicklerMemoProxyObject *self) -/*[clinic end generated code: checksum=2f061bb9ecd9ee8500184c135148a131c46a3b88]*/ +/*[clinic end generated code: checksum=2373102b7c87d99ba4c4a56b6813d2c84dd61865]*/ { PyObject *reduce_value; PyObject *constructor_args; diff --git a/Modules/_sre.c b/Modules/_sre.c --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -541,7 +541,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(pattern_match__doc__, -"match(pattern, pos=0, endpos=sys.maxsize)\n" +"match(self, pattern, pos=0, endpos=sys.maxsize)\n" "Matches zero or more characters at the beginning of the string."); #define PATTERN_MATCH_METHODDEF \ @@ -551,7 +551,7 @@ pattern_match_impl(PatternObject *self, PyObject *pattern, Py_ssize_t pos, Py_ssize_t endpos); static PyObject * -pattern_match(PyObject *self, PyObject *args, PyObject *kwargs) +pattern_match(PatternObject *self, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; static char *_keywords[] = {"pattern", "pos", "endpos", NULL}; @@ -563,7 +563,7 @@ "O|nn:match", _keywords, &pattern, &pos, &endpos)) goto exit; - return_value = pattern_match_impl((PatternObject *)self, pattern, pos, endpos); + return_value = pattern_match_impl(self, pattern, pos, endpos); exit: return return_value; @@ -571,7 +571,7 @@ static PyObject * pattern_match_impl(PatternObject *self, PyObject *pattern, Py_ssize_t pos, Py_ssize_t endpos) -/*[clinic end generated code: checksum=63e59c5f3019efe6c1f3acdec42b2d3595e14a09]*/ +/*[clinic end generated code: checksum=4a3865d13638cb7c13dcae1fe58c1a9c35071998]*/ { SRE_STATE state; Py_ssize_t status; diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c --- a/Modules/unicodedata.c +++ b/Modules/unicodedata.c @@ -129,7 +129,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(unicodedata_UCD_decimal__doc__, -"decimal(unichr, default=None)\n" +"decimal(self, unichr, default=None)\n" "Converts a Unicode character into its equivalent decimal value.\n" "\n" "Returns the decimal value assigned to the Unicode character unichr\n" @@ -161,7 +161,7 @@ static PyObject * unicodedata_UCD_decimal_impl(PyObject *self, PyUnicodeObject *unichr, PyObject *default_value) -/*[clinic end generated code: checksum=73edde0e9cd5913ea174c4fa81504369761b7426]*/ +/*[clinic end generated code: checksum=01826b179d497d8fd3842c56679ecbd4faddaa95]*/ { PyUnicodeObject *v = (PyUnicodeObject *)unichr; int have_old = 0; diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -765,7 +765,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(zlib_Decompress_decompress__doc__, -"decompress(data, max_length=0)\n" +"decompress(self, data, max_length=0)\n" "Return a string containing the decompressed version of the data.\n" "\n" " data\n" @@ -786,7 +786,7 @@ zlib_Decompress_decompress_impl(compobject *self, Py_buffer *data, unsigned int max_length); static PyObject * -zlib_Decompress_decompress(PyObject *self, PyObject *args) +zlib_Decompress_decompress(compobject *self, PyObject *args) { PyObject *return_value = NULL; Py_buffer data = {NULL, NULL}; @@ -796,7 +796,7 @@ "y*|O&:decompress", &data, uint_converter, &max_length)) goto exit; - return_value = zlib_Decompress_decompress_impl((compobject *)self, &data, max_length); + return_value = zlib_Decompress_decompress_impl(self, &data, max_length); exit: /* Cleanup for data */ @@ -808,7 +808,7 @@ static PyObject * zlib_Decompress_decompress_impl(compobject *self, Py_buffer *data, unsigned int max_length) -/*[clinic end generated code: checksum=e0058024c4a97b411d2e2197791b89fde175f76f]*/ +/*[clinic end generated code: checksum=b7fd2e3b23430f57f5a84817189575bc46464901]*/ { int err; unsigned int old_length, length = DEFAULTALLOC; @@ -1035,7 +1035,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(zlib_Compress_copy__doc__, -"copy()\n" +"copy(self)\n" "Return a copy of the compression object."); #define ZLIB_COMPRESS_COPY_METHODDEF \ @@ -1045,14 +1045,14 @@ zlib_Compress_copy_impl(compobject *self); static PyObject * -zlib_Compress_copy(PyObject *self, PyObject *Py_UNUSED(ignored)) +zlib_Compress_copy(compobject *self, PyObject *Py_UNUSED(ignored)) { - return zlib_Compress_copy_impl((compobject *)self); + return zlib_Compress_copy_impl(self); } static PyObject * zlib_Compress_copy_impl(compobject *self) -/*[clinic end generated code: checksum=d57a7911deb7940e85a8d7e65af20b6e2df69000]*/ +/*[clinic end generated code: checksum=7aa841ad51297eb83250f511a76872e88fdc737e]*/ { compobject *retval = NULL; int err; diff --git a/Objects/descrobject.c b/Objects/descrobject.c --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -350,14 +350,21 @@ return result; } + static PyObject * method_get_doc(PyMethodDescrObject *descr, void *closure) { - if (descr->d_method->ml_doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_method->ml_doc); + const char *name = descr->d_method->ml_name; + const char *doc = descr->d_method->ml_doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +method_get_text_signature(PyMethodDescrObject *descr, void *closure) +{ + const char *name = descr->d_method->ml_name; + const char *doc = descr->d_method->ml_doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyObject * @@ -425,22 +432,30 @@ static PyGetSetDef method_getset[] = { {"__doc__", (getter)method_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)method_get_text_signature}, {0} }; static PyObject * member_get_doc(PyMemberDescrObject *descr, void *closure) { - if (descr->d_member->doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_member->doc); + const char *name = descr->d_member->name; + const char *doc = descr->d_member->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +member_get_text_signature(PyMemberDescrObject *descr, void *closure) +{ + const char *name = descr->d_member->name; + const char *doc = descr->d_member->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyGetSetDef member_getset[] = { {"__doc__", (getter)member_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)member_get_text_signature}, {0} }; @@ -463,16 +478,23 @@ static PyObject * wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure) { - if (descr->d_base->doc == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - return PyUnicode_FromString(descr->d_base->doc); + const char *name = descr->d_base->name; + const char *doc = descr->d_base->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} + +static PyObject * +wrapperdescr_get_text_signature(PyWrapperDescrObject *descr, void *closure) +{ + const char *name = descr->d_base->name; + const char *doc = descr->d_base->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyGetSetDef wrapperdescr_getset[] = { {"__doc__", (getter)wrapperdescr_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__text_signature__", (getter)wrapperdescr_get_text_signature}, {0} }; @@ -1143,19 +1165,23 @@ } static PyObject * -wrapper_doc(wrapperobject *wp) +wrapper_doc(wrapperobject *wp, void *closure) { - const char *s = wp->descr->d_base->doc; + const char *name = wp->descr->d_base->name; + const char *doc = wp->descr->d_base->doc; + return _PyType_GetDocFromInternalDoc(name, doc); +} - if (s == NULL) { - Py_INCREF(Py_None); - return Py_None; - } - else { - return PyUnicode_FromString(s); - } +static PyObject * +wrapper_text_signature(wrapperobject *wp, void *closure) +{ + const char *name = wp->descr->d_base->name; + const char *doc = wp->descr->d_base->doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } + + static PyObject * wrapper_qualname(wrapperobject *wp) { @@ -1167,6 +1193,7 @@ {"__name__", (getter)wrapper_name}, {"__qualname__", (getter)wrapper_qualname}, {"__doc__", (getter)wrapper_doc}, + {"__text_signature__", (getter)wrapper_text_signature}, {0} }; diff --git a/Objects/dictobject.c b/Objects/dictobject.c --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2176,7 +2176,7 @@ [clinic start generated code]*/ PyDoc_STRVAR(dict___contains____doc__, -"__contains__(key)\n" +"__contains__(self, key)\n" "True if D has a key k, else False."); #define DICT___CONTAINS___METHODDEF \ @@ -2184,7 +2184,7 @@ static PyObject * dict___contains__(PyObject *self, PyObject *key) -/*[clinic end generated code: checksum=402ddb624ba1e4db764bfdfbbee6c1c59d1a11fa]*/ +/*[clinic end generated code: checksum=c4f85a39baac4776c4275ad5f072f7732c5f0806]*/ { register PyDictObject *mp = (PyDictObject *)self; Py_hash_t hash; diff --git a/Objects/methodobject.c b/Objects/methodobject.c --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -179,75 +179,20 @@ {NULL, NULL} }; -/* - * 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); + const char *name = m->m_ml->ml_name; + const char *doc = m->m_ml->ml_doc; + return _PyType_GetTextSignatureFromInternalDoc(name, doc); } static PyObject * meth_get__doc__(PyCFunctionObject *m, void *closure) { - 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; - } - - return PyUnicode_FromString(doc); + const char *name = m->m_ml->ml_name; + const char *doc = m->m_ml->ml_doc; + return _PyType_GetDocFromInternalDoc(name, doc); } static PyObject * diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -54,6 +54,80 @@ static PyObject * slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + +/* + * finds the docstring's introspection signature. + * if present, returns a pointer pointing to the first '('. + * otherwise returns NULL. + */ +static const char * +find_signature(const char *name, const char *doc) +{ + size_t length; + if (!doc || !name) + return NULL; + length = strlen(name); + if (strncmp(doc, name, length)) + return NULL; + doc += length; + if (*doc != '(') + return NULL; + return doc; +} + +/* + * skips to the end of the docstring's instrospection signature. + */ +static const char * +skip_signature(const char *doc) +{ + while (*doc && *doc != '\n') + doc++; + return doc; +} + +static const char * +skip_eols(const char *trace) +{ + while (*trace == '\n') + trace++; + return trace; +} + +PyObject * +_PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc) +{ + const char *signature = find_signature(name, internal_doc); + const char *doc; + + if (signature) + doc = skip_eols(skip_signature(signature)); + else + doc = internal_doc; + + if (!doc) { + Py_INCREF(Py_None); + return Py_None; + } + + return PyUnicode_FromString(doc); +} + +PyObject * +_PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc) +{ + const char *signature = find_signature(name, internal_doc); + const char *doc; + + if (!signature) { + Py_INCREF(Py_None); + return Py_None; + } + + doc = skip_signature(signature); + return PyUnicode_FromStringAndSize(signature, doc - signature); +} + unsigned int PyType_ClearCache(void) { @@ -3480,7 +3554,7 @@ { PyObject **dict; dict = _PyObject_GetDictPtr(obj); - /* It is possible that the object's dict is not initialized + /* It is possible that the object's dict is not initialized yet. In this case, we will return None for the state. We also return None if the dict is empty to make the behavior consistent regardless whether the dict was initialized or not. @@ -3788,7 +3862,7 @@ Py_DECREF(state); Py_DECREF(listitems); Py_DECREF(dictitems); - return result; + return result; } static PyObject * @@ -3813,7 +3887,7 @@ } else if (kwargs != NULL) { if (PyDict_Size(kwargs) > 0) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "must use protocol 4 or greater to copy this " "object; since __getnewargs_ex__ returned " "keyword arguments."); diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -139,9 +139,9 @@ # so if they're used Argument Clinic will add "_value" to the end # of the name in C. c_keywords = set(""" -asm auto break case char cls const continue default do double -else enum extern float for goto if inline int long module null -register return self short signed sizeof static struct switch +asm auto break case char const continue default do double +else enum extern float for goto if inline int long +register return short signed sizeof static struct switch typedef typeof union unsigned void volatile while """.strip().split()) @@ -366,7 +366,7 @@ checksum_line = "#/*[{dsl_name} end generated code: checksum={checksum}]*/" -def permute_left_option_groups(l): +def permute_left_optional_groups(l): """ Given [1, 2, 3], should yield: () @@ -381,7 +381,7 @@ yield tuple(accumulator) -def permute_right_option_groups(l): +def permute_right_optional_groups(l): """ Given [1, 2, 3], should yield: () @@ -414,8 +414,8 @@ accumulator = [] counts = set() - for r in permute_right_option_groups(right): - for l in permute_left_option_groups(left): + for r in permute_right_optional_groups(right): + for l in permute_left_optional_groups(left): t = l + required + r if len(t) in counts: continue @@ -559,8 +559,8 @@ goto exit; __________________________________________________ -parser_definition_option_groups - {option_group_parsing} +parser_definition_optional_groups + {optional_group_parsing} __________________________________________________ @@ -635,9 +635,12 @@ def output_templates(self, f): parameters = list(f.parameters.values()) + assert parameters + assert isinstance(parameters[0].converter, self_converter) + del parameters[0] converters = [p.converter for p in parameters] - has_option_groups = parameters and (parameters[0].group or parameters[-1].group) + has_optional_groups = parameters and (parameters[0].group or parameters[-1].group) default_return_converter = (not f.return_converter or f.return_converter.type == 'PyObject *') @@ -729,7 +732,7 @@ parser_prototype = templates['parser_prototype_meth_o'] parser_definition = parser_body(parser_prototype) - elif has_option_groups: + elif has_optional_groups: # positional parameters with option groups # (we have to generate lots of PyArg_ParseTuple calls # in a big switch statement) @@ -737,7 +740,7 @@ flags = "METH_VARARGS" parser_prototype = templates['parser_prototype_varargs'] - parser_definition = parser_body(parser_prototype, 'parser_definition_option_groups') + parser_definition = parser_body(parser_prototype, 'parser_definition_optional_groups') elif positional and all_boring_objects: # positional-only, but no option groups, @@ -837,7 +840,7 @@ adjective = "left_" if group < 0 else "right_" return "group_" + adjective + str(abs(group)) - def render_option_group_parsing(self, f, template_dict): + def render_optional_group_parsing(self, f, template_dict): # positional only, grouped, optional arguments! # can be optional on the left or right. # here's an example: @@ -858,6 +861,9 @@ add, output = text_accumulator() parameters = list(f.parameters.values()) + assert parameters + assert isinstance(parameters[0].converter, self_converter) + del parameters[0] groups = [] group = None @@ -926,7 +932,7 @@ add(s.format(f.full_name, count_min, count_max)) add(' return NULL;\n') add("}}") - template_dict['option_group_parsing'] = output() + template_dict['optional_group_parsing'] = output() def render_function(self, clinic, f): if not f: @@ -938,13 +944,8 @@ parameters = list(f.parameters.values()) converters = [p.converter for p in parameters] - template_dict = {} - full_name = f.full_name - template_dict['full_name'] = full_name - name = full_name.rpartition('.')[2] - template_dict['name'] = name if f.c_basename: c_basename = f.c_basename @@ -953,24 +954,19 @@ if fields[-1] == '__new__': fields.pop() c_basename = "_".join(fields) - template_dict['c_basename'] = c_basename methoddef_name = "{}_METHODDEF".format(c_basename.upper()) - template_dict['methoddef_name'] = methoddef_name - - template_dict['docstring'] = self.docstring_for_c_string(f) - - positional = has_option_groups = False - + + positional = has_optional_groups = False first_optional = len(parameters) - - if parameters: - last_group = 0 - - for i, p in enumerate(parameters): - c = p.converter - - if p.default is not unspecified: + last_group = 0 + + assert parameters + for i, p in enumerate(parameters): + c = p.converter + + if i: + if (p.default is not unspecified): first_optional = min(first_optional, i) # insert group variable @@ -982,12 +978,30 @@ data.impl_arguments.append(group_name) data.declarations.append("int " + group_name + " = 0;") data.impl_parameters.append("int " + group_name) - has_option_groups = True - c.render(p, data) - - positional = parameters[-1].kind == inspect.Parameter.POSITIONAL_ONLY - if has_option_groups and (not positional): - fail("You cannot use optional groups ('[' and ']')\nunless all parameters are positional-only ('/').") + has_optional_groups = True + + c.render(p, data) + + positional = parameters[-1].kind == inspect.Parameter.POSITIONAL_ONLY + if has_optional_groups and (not positional): + fail("You cannot use optional groups ('[' and ']')\nunless all parameters are positional-only ('/').") + + template_dict = {} + template_dict['full_name'] = full_name + template_dict['name'] = name + template_dict['c_basename'] = c_basename + template_dict['methoddef_name'] = methoddef_name + template_dict['docstring'] = self.docstring_for_c_string(f) + + values = list(f.parameters.values()) + p_self = values[0] + assert isinstance(p_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!" + template_dict['self_name'] = p_self.converter.name + template_dict['self_type'] = p_self.converter.type + + positional = parameters[-1].kind == inspect.Parameter.POSITIONAL_ONLY + if has_optional_groups and (not positional): + fail("You cannot use optional groups ('[' and ']')\nunless all parameters are positional-only ('/').") # HACK # when we're METH_O, but have a custom @@ -1001,27 +1015,15 @@ # have any problem finding it. default_return_converter = (not f.return_converter or f.return_converter.type == 'PyObject *') - if (len(parameters) == 1 and - parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY and - not converters[0].is_optional() and - isinstance(converters[0], object_converter) and - converters[0].format_unit == 'O' and + if (len(parameters) == 2 and + parameters[1].kind == inspect.Parameter.POSITIONAL_ONLY and + not converters[1].is_optional() and + isinstance(converters[1], object_converter) and + converters[1].format_unit == 'O' and not default_return_converter): data.declarations.pop(0) - # now insert our "self" (or whatever) parameters - # (we deliberately don't call render on self converters) - stock_self = self_converter('self', f) - template_dict['self_name'] = stock_self.name - template_dict['self_type'] = stock_self.type - data.impl_parameters.insert(0, f.self_converter.type + ("" if f.self_converter.type.endswith('*') else " ") + f.self_converter.name) - if f.self_converter.type != stock_self.type: - self_cast = '(' + f.self_converter.type + ')' - else: - self_cast = '' - data.impl_arguments.insert(0, self_cast + stock_self.name) - f.return_converter.render(f, data) template_dict['impl_return_type'] = f.return_converter.type @@ -1036,20 +1038,20 @@ template_dict['cleanup'] = "".join(data.cleanup) template_dict['return_value'] = data.return_value - # used by unpack tuple - template_dict['unpack_min'] = str(first_optional) - template_dict['unpack_max'] = str(len(parameters)) - - if has_option_groups: - self.render_option_group_parsing(f, template_dict) + # used by unpack tuple... -1 is to skip "self" + template_dict['unpack_min'] = str(first_optional - 1) + template_dict['unpack_max'] = str(len(parameters) - 1) + + if has_optional_groups: + self.render_optional_group_parsing(f, template_dict) templates = self.output_templates(f) for name, destination in clinic.field_destinations.items(): template = templates[name] - if has_option_groups: + if has_optional_groups: template = linear_format(template, - option_group_parsing=template_dict['option_group_parsing']) + optional_group_parsing=template_dict['optional_group_parsing']) template = linear_format(template, declarations=template_dict['declarations'], return_conversion=template_dict['return_conversion'], @@ -1077,6 +1079,7 @@ + @contextlib.contextmanager def OverrideStdioWith(stdout): saved_stdout = sys.stdout @@ -1969,6 +1972,10 @@ # Only used by format units ending with '#'. length = False + # Should we show this parameter in the generated + # __text_signature__? This is *almost* always True. + show_in_signature = True + def __init__(self, name, function, default=unspecified, *, c_default=None, py_default=None, annotation=unspecified, **kwargs): self.function = function self.name = name @@ -1998,30 +2005,36 @@ def is_optional(self): return (self.default is not unspecified) - def render(self, parameter, data): - """ - parameter is a clinic.Parameter instance. - data is a CRenderData instance. - """ + def _render_self(self, parameter, data): self.parameter = parameter original_name = self.name name = ensure_legal_c_identifier(original_name) - # declarations - d = self.declaration() - data.declarations.append(d) - - # initializers - initializers = self.initialize() - if initializers: - data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip()) - # impl_arguments s = ("&" if self.impl_by_reference else "") + name data.impl_arguments.append(s) if self.length: data.impl_arguments.append(self.length_name()) + # impl_parameters + data.impl_parameters.append(self.simple_declaration(by_reference=self.impl_by_reference)) + if self.length: + data.impl_parameters.append("Py_ssize_clean_t " + self.length_name()) + + def _render_non_self(self, parameter, data): + self.parameter = parameter + original_name = self.name + name = ensure_legal_c_identifier(original_name) + + # declarations + d = self.declaration() + data.declarations.append(d) + + # initializers + initializers = self.initialize() + if initializers: + data.initializers.append('/* initializers for ' + name + ' */\n' + initializers.rstrip()) + # keywords data.keywords.append(original_name) @@ -2035,16 +2048,20 @@ # parse_arguments self.parse_argument(data.parse_arguments) - # impl_parameters - data.impl_parameters.append(self.simple_declaration(by_reference=self.impl_by_reference)) - if self.length: - data.impl_parameters.append("Py_ssize_clean_t " + self.length_name()) - # cleanup cleanup = self.cleanup() if cleanup: data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n") + + def render(self, parameter, data): + """ + parameter is a clinic.Parameter instance. + data is a CRenderData instance. + """ + self._render_self(parameter, data) + self._render_non_self(parameter, data) + def length_name(self): """Computes the name of the associated "length" variable.""" if not self.length: @@ -2427,6 +2444,8 @@ this is the default converter used for "self". """ type = "PyObject *" + format_unit = '' + def converter_init(self, *, type=None): f = self.function if f.kind in (CALLABLE, METHOD_INIT): @@ -2435,12 +2454,15 @@ else: self.name = "module" self.type = "PyModuleDef *" + self.show_in_signature = False elif f.kind == STATIC_METHOD: self.name = "null" self.type = "void *" + self.show_in_signature = False elif f.kind == CLASS_METHOD: self.name = "cls" self.type = "PyTypeObject *" + self.show_in_signature = False elif f.kind == METHOD_NEW: self.name = "type" self.type = "PyTypeObject *" @@ -2448,10 +2470,15 @@ if type: self.type = type + # def render(self, parameter, data): + # fail("render() should never be called on self_converter instances") + def render(self, parameter, data): - fail("render() should never be called on self_converter instances") - - + """ + parameter is a clinic.Parameter instance. + data is a CRenderData instance. + """ + self._render_self(parameter, data) def add_c_return_converter(f, name=None): if not name: @@ -3007,6 +3034,12 @@ self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename, return_converter=return_converter, kind=self.kind, coexist=self.coexist) self.block.signatures.append(self.function) + + # insert a default "self" parameter (we'll remove it if the user provides one) + sc = self.function.self_converter = self_converter("self", self.function) + p_self = Parameter(sc.name, inspect.Parameter.POSITIONAL_ONLY, function=self.function, converter=sc) + self.function.parameters[sc.name] = p_self + (cls or module).functions.append(self.function) self.next(self.state_parameters_start) @@ -3263,18 +3296,22 @@ fail('{} is not a valid {}converter'.format(name, legacy_str)) converter = dict[name](parameter_name, self.function, value, **kwargs) - # special case: if it's the self converter, - # don't actually add it to the parameter list + kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD + if isinstance(converter, self_converter): - if self.function.parameters or (self.parameter_state != self.ps_required): + if len(self.function.parameters) == 1: + if (self.parameter_state != self.ps_required): + fail("The 'self' parameter cannot be marked optional.") + if value is not unspecified: + fail("The 'self' parameter cannot have a default value.") + if self.group: + fail("The 'self' parameter cannot be in an optional group.") + kind = inspect.Parameter.POSITIONAL_ONLY + self.parameter_state = self.ps_start + self.function.parameters.clear() + else: fail("The 'self' parameter, if specified, must be the very first thing in the parameter block.") - if self.function.self_converter: - fail("You can't specify the 'self' parameter more than once.") - self.function.self_converter = converter - self.parameter_state = self.ps_start - return - - kind = inspect.Parameter.KEYWORD_ONLY if self.keyword_only else inspect.Parameter.POSITIONAL_OR_KEYWORD + p = Parameter(parameter_name, kind, function=self.function, converter=converter, default=value, group=self.group) if parameter_name in self.function.parameters: @@ -3333,7 +3370,7 @@ self.parameter_state = self.ps_seen_slash # fixup preceeding parameters for p in self.function.parameters.values(): - if p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD: + if (p.kind != inspect.Parameter.POSITIONAL_OR_KEYWORD and not isinstance(p.converter, self_converter)): fail("Function " + self.function.name + " mixes keyword-only and positional-only parameters, which is unsupported.") p.kind = inspect.Parameter.POSITIONAL_ONLY @@ -3405,17 +3442,24 @@ add('(') # populate "right_bracket_count" field for every parameter - if parameters: + assert parameters, "We should always have a self parameter. " + repr(f) + assert isinstance(parameters[0].converter, self_converter) + parameters[0].right_bracket_count = 0 + parameters_after_self = parameters[1:] + if parameters_after_self: # for now, the only way Clinic supports positional-only parameters - # is if all of them are positional-only. - positional_only_parameters = [p.kind == inspect.Parameter.POSITIONAL_ONLY for p in parameters] - if parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY: + # is if all of them are positional-only... + # + # ... except for self! self is always positional-only. + + positional_only_parameters = [p.kind == inspect.Parameter.POSITIONAL_ONLY for p in parameters_after_self] + if parameters_after_self[0].kind == inspect.Parameter.POSITIONAL_ONLY: assert all(positional_only_parameters) for p in parameters: p.right_bracket_count = abs(p.group) else: # don't put any right brackets around non-positional-only parameters, ever. - for p in parameters: + for p in parameters_after_self: p.right_bracket_count = 0 right_bracket_count = 0 @@ -3435,6 +3479,9 @@ add_comma = False for p in parameters: + if not p.converter.show_in_signature: + continue + assert p.name if p.is_keyword_only() and not added_star: @@ -3442,6 +3489,7 @@ if add_comma: add(', ') add('*') + add_comma = True a = [p.name] if p.converter.is_optional(): @@ -3554,9 +3602,6 @@ if not self.function: return - if not self.function.self_converter: - self.function.self_converter = self_converter("self", self.function) - if self.keyword_only: values = self.function.parameters.values() if not values: @@ -3601,6 +3646,7 @@ cmdline = argparse.ArgumentParser() cmdline.add_argument("-f", "--force", action='store_true') cmdline.add_argument("-o", "--output", type=str) + cmdline.add_argument("-v", "--verbose", action='store_true') cmdline.add_argument("--converters", action='store_true') cmdline.add_argument("--make", action='store_true') cmdline.add_argument("filename", type=str, nargs="*") @@ -3674,13 +3720,15 @@ cmdline.print_usage() sys.exit(-1) for root, dirs, files in os.walk('.'): - for rcs_dir in ('.svn', '.git', '.hg'): + for rcs_dir in ('.svn', '.git', '.hg', 'build'): if rcs_dir in dirs: dirs.remove(rcs_dir) for filename in files: - if not filename.endswith('.c'): + if not (filename.endswith('.c') or filename.endswith('.h')): continue path = os.path.join(root, filename) + if ns.verbose: + print(path) parse_file(path, verify=not ns.force) return @@ -3695,6 +3743,8 @@ sys.exit(-1) for filename in ns.filename: + if ns.verbose: + print(filename) parse_file(filename, output=ns.output, verify=not ns.force)