[Python-checkins] cpython: Issue #20530: Argument Clinic's signature format has been revised again.

larry.hastings python-checkins at python.org
Sun Feb 9 07:16:12 CET 2014


http://hg.python.org/cpython/rev/29d9638bf449
changeset:   89083:29d9638bf449
user:        Larry Hastings <larry at hastings.org>
date:        Sat Feb 08 22:15:29 2014 -0800
summary:
  Issue #20530: Argument Clinic's signature format has been revised again.
The new syntax is highly human readable while still preventing false
positives.  The syntax also extends Python syntax to denote "self" and
positional-only parameters, allowing inspect.Signature objects to be
totally accurate for all supported builtins in Python 3.4.

files:
  Include/object.h               |    4 +-
  Lib/inspect.py                 |  117 +++++++++--
  Lib/test/test_capi.py          |   17 +-
  Lib/test/test_inspect.py       |   73 ++++++-
  Misc/NEWS                      |   16 +-
  Modules/_cryptmodule.c         |    6 +-
  Modules/_datetimemodule.c      |    6 +-
  Modules/_dbmmodule.c           |   12 +-
  Modules/_opcode.c              |    6 +-
  Modules/_sre.c                 |    6 +-
  Modules/_testcapimodule.c      |   27 ++-
  Modules/_weakref.c             |    6 +-
  Modules/clinic/_bz2module.c.h  |   22 +-
  Modules/clinic/_lzmamodule.c.h |   30 ++-
  Modules/clinic/_pickle.c.h     |   68 +++++-
  Modules/clinic/audioop.c.h     |  107 ++++++++--
  Modules/clinic/binascii.c.h    |   58 ++++-
  Modules/clinic/zlibmodule.c.h  |   52 +++-
  Modules/posixmodule.c          |   19 +-
  Modules/unicodedata.c          |    6 +-
  Objects/descrobject.c          |   12 +-
  Objects/dictobject.c           |   12 +-
  Objects/methodobject.c         |    4 +-
  Objects/typeobject.c           |  205 ++++++++++++--------
  Objects/unicodeobject.c        |    6 +-
  Python/import.c                |   72 ++++--
  Tools/clinic/clinic.py         |  162 +++++++++++----
  Tools/clinic/clinic_test.py    |   43 +++-
  28 files changed, 836 insertions(+), 338 deletions(-)


diff --git a/Include/object.h b/Include/object.h
--- a/Include/object.h
+++ b/Include/object.h
@@ -496,8 +496,8 @@
 PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);
 
 #ifndef Py_LIMITED_API
-PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *);
-PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *);
+PyAPI_FUNC(PyObject *) _PyType_GetDocFromInternalDoc(const char *, const char *);
+PyAPI_FUNC(PyObject *) _PyType_GetTextSignatureFromInternalDoc(const char *, const char *);
 #endif
 
 /* Generic operations on objects */
diff --git a/Lib/inspect.py b/Lib/inspect.py
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -39,6 +39,7 @@
 import re
 import sys
 import tokenize
+import token
 import types
 import warnings
 import functools
@@ -1648,25 +1649,88 @@
     return spec[2:pos]
 
 
+def _signature_strip_non_python_syntax(signature):
+    """
+    Takes a signature in Argument Clinic's extended signature format.
+    Returns a tuple of three things:
+      * that signature re-rendered in standard Python syntax,
+      * the index of the "self" parameter (generally 0), or None if
+        the function does not have a "self" parameter, and
+      * the index of the last "positional only" parameter,
+        or None if the signature has no positional-only parameters.
+    """
+
+    if not signature:
+        return signature, None, None
+
+    self_parameter = None
+    last_positional_only = None
+
+    lines = [l.encode('ascii') for l in signature.split('\n')]
+    generator = iter(lines).__next__
+    token_stream = tokenize.tokenize(generator)
+
+    delayed_comma = False
+    skip_next_comma = False
+    text = []
+    add = text.append
+
+    current_parameter = 0
+    OP = token.OP
+    ERRORTOKEN = token.ERRORTOKEN
+
+    # token stream always starts with ENCODING token, skip it
+    t = next(token_stream)
+    assert t.type == tokenize.ENCODING
+
+    for t in token_stream:
+        type, string = t.type, t.string
+
+        if type == OP:
+            if string == ',':
+                if skip_next_comma:
+                    skip_next_comma = False
+                else:
+                    assert not delayed_comma
+                    delayed_comma = True
+                    current_parameter += 1
+                continue
+
+            if string == '/':
+                assert not skip_next_comma
+                assert last_positional_only is None
+                skip_next_comma = True
+                last_positional_only = current_parameter - 1
+                continue
+
+        if (type == ERRORTOKEN) and (string == '$'):
+            assert self_parameter is None
+            self_parameter = current_parameter
+            continue
+
+        if delayed_comma:
+            delayed_comma = False
+            if not ((type == OP) and (string == ')')):
+                add(', ')
+        add(string)
+        if (string == ','):
+            add(' ')
+    clean_signature = ''.join(text)
+    return clean_signature, self_parameter, last_positional_only
+
+
 def _signature_fromstr(cls, obj, s):
     # Internal helper to parse content of '__text_signature__'
     # and return a Signature based on it
     Parameter = cls._parameter_cls
 
-    if s.endswith("/)"):
-        kind = Parameter.POSITIONAL_ONLY
-        s = s[:-2] + ')'
-    else:
-        kind = Parameter.POSITIONAL_OR_KEYWORD
-
-    first_parameter_is_self = s.startswith("($")
-    if first_parameter_is_self:
-        s = '(' + s[2:]
-
-    s = "def foo" + s + ": pass"
+    clean_signature, self_parameter, last_positional_only = \
+        _signature_strip_non_python_syntax(s)
+
+    program = "def foo" + clean_signature + ": pass"
 
     try:
-        module = ast.parse(s)
+        module = ast.parse(program)
     except SyntaxError:
         module = None
 
@@ -1750,8 +1814,14 @@
     args = reversed(f.args.args)
     defaults = reversed(f.args.defaults)
     iter = itertools.zip_longest(args, defaults, fillvalue=None)
-    for name, default in reversed(list(iter)):
+    if last_positional_only is not None:
+        kind = Parameter.POSITIONAL_ONLY
+    else:
+        kind = Parameter.POSITIONAL_OR_KEYWORD
+    for i, (name, default) in enumerate(reversed(list(iter))):
         p(name, default)
+        if i == last_positional_only:
+            kind = Parameter.POSITIONAL_OR_KEYWORD
 
     # *args
     if f.args.vararg:
@@ -1768,7 +1838,7 @@
         kind = Parameter.VAR_KEYWORD
         p(f.args.kwarg, empty)
 
-    if first_parameter_is_self:
+    if self_parameter is not None:
         assert parameters
         if getattr(obj, '__self__', None):
             # strip off self, it's already been bound
@@ -1861,12 +1931,13 @@
             # At this point we know, that `obj` is a class, with no user-
             # defined '__init__', '__new__', or class-level '__call__'
 
-            for base in obj.__mro__:
+            for base in obj.__mro__[:-1]:
                 # Since '__text_signature__' is implemented as a
                 # descriptor that extracts text signature from the
                 # class docstring, if 'obj' is derived from a builtin
                 # class, its own '__text_signature__' may be 'None'.
-                # Therefore, we go through the MRO to find the first
+                # Therefore, we go through the MRO (except the last
+                # class in there, which is 'object') to find the first
                 # class with non-empty text signature.
                 try:
                     text_sig = base.__text_signature__
@@ -1881,13 +1952,7 @@
             # No '__text_signature__' was found for the 'obj' class.
             # Last option is to check if its '__init__' is
             # object.__init__ or type.__init__.
-            if type in obj.__mro__:
-                # 'obj' is a metaclass without user-defined __init__
-                # or __new__.
-                if obj.__init__ is type.__init__:
-                    # Return a signature of 'type' builtin.
-                    return signature(type)
-            else:
+            if type not in obj.__mro__:
                 # We have a class (not metaclass), but no user-defined
                 # __init__ or __new__ for it
                 if obj.__init__ is object.__init__:
@@ -1901,7 +1966,11 @@
         # infinite recursion (and even potential segfault)
         call = _signature_get_user_defined_method(type(obj), '__call__')
         if call is not None:
-            sig = signature(call)
+            try:
+                sig = signature(call)
+            except ValueError as ex:
+                msg = 'no signature found for {!r}'.format(obj)
+                raise ValueError(msg) from ex
 
     if sig is not None:
         # For classes and objects we skip the first parameter of their
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -126,20 +126,29 @@
         self.assertEqual(_testcapi.docstring_no_signature.__text_signature__, None)
 
         self.assertEqual(_testcapi.docstring_with_invalid_signature.__doc__,
-            "sig= (module, boo)\n"
+            "docstring_with_invalid_signature($module, /, boo)\n"
             "\n"
             "This docstring has an invalid signature."
             )
         self.assertEqual(_testcapi.docstring_with_invalid_signature.__text_signature__, None)
 
+        self.assertEqual(_testcapi.docstring_with_invalid_signature2.__doc__,
+            "docstring_with_invalid_signature2($module, /, boo)\n"
+            "\n"
+            "--\n"
+            "\n"
+            "This docstring also has an invalid signature."
+            )
+        self.assertEqual(_testcapi.docstring_with_invalid_signature2.__text_signature__, None)
+
         self.assertEqual(_testcapi.docstring_with_signature.__doc__,
             "This docstring has a valid signature.")
-        self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "(module, sig)")
+        self.assertEqual(_testcapi.docstring_with_signature.__text_signature__, "($module, /, sig)")
 
         self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__doc__,
-            "This docstring has a valid signature and some extra newlines.")
+            "\nThis docstring has a valid signature and some extra newlines.")
         self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__,
-            "(module, parameter)")
+            "($module, /, parameter)")
 
 
 @unittest.skipUnless(threading, 'Threading required for this test.')
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
@@ -1684,7 +1684,6 @@
         self.assertEqual(p('sys'), sys.maxsize)
         self.assertEqual(p('exp'), sys.maxsize - 1)
 
-        test_callable(type)
         test_callable(object)
 
         # normal method
@@ -1710,9 +1709,12 @@
         # support for 'method-wrapper'
         test_callable(min.__call__)
 
-        class ThisWorksNow:
-            __call__ = type
-        test_callable(ThisWorksNow())
+        # This doesn't work now.
+        # (We don't have a valid signature for "type" in 3.4)
+        with self.assertRaisesRegex(ValueError, "no signature found"):
+            class ThisWorksNow:
+                __call__ = type
+            test_callable(ThisWorksNow())
 
     @cpython_only
     @unittest.skipIf(MISSING_C_DOCSTRINGS,
@@ -2213,11 +2215,11 @@
 
         # Test meta-classes without user-defined __init__ or __new__
         class C(type): pass
-        self.assertEqual(str(inspect.signature(C)),
-                         '(object_or_name, bases, dict)')
         class D(C): pass
-        self.assertEqual(str(inspect.signature(D)),
-                         '(object_or_name, bases, dict)')
+        with self.assertRaisesRegex(ValueError, "callable.*is not supported"):
+            self.assertEqual(inspect.signature(C), None)
+        with self.assertRaisesRegex(ValueError, "callable.*is not supported"):
+            self.assertEqual(inspect.signature(D), None)
 
     @unittest.skipIf(MISSING_C_DOCSTRINGS,
                      "Signature information for builtins requires docstrings")
@@ -2768,6 +2770,61 @@
         self.assertEqual(getter('($self, obj)'), 'self')
         self.assertEqual(getter('($cls, /, obj)'), 'cls')
 
+    def _strip_non_python_syntax(self, input,
+        clean_signature, self_parameter, last_positional_only):
+        computed_clean_signature, \
+            computed_self_parameter, \
+            computed_last_positional_only = \
+            inspect._signature_strip_non_python_syntax(input)
+        self.assertEqual(computed_clean_signature, clean_signature)
+        self.assertEqual(computed_self_parameter, self_parameter)
+        self.assertEqual(computed_last_positional_only, last_positional_only)
+
+    def test_signature_strip_non_python_syntax(self):
+        self._strip_non_python_syntax(
+            "($module, /, path, mode, *, dir_fd=None, " +
+                "effective_ids=False,\n       follow_symlinks=True)",
+            "(module, path, mode, *, dir_fd=None, " +
+                "effective_ids=False, follow_symlinks=True)",
+            0,
+            0)
+
+        self._strip_non_python_syntax(
+            "($module, word, salt, /)",
+            "(module, word, salt)",
+            0,
+            2)
+
+        self._strip_non_python_syntax(
+            "(x, y=None, z=None, /)",
+            "(x, y=None, z=None)",
+            None,
+            2)
+
+        self._strip_non_python_syntax(
+            "(x, y=None, z=None)",
+            "(x, y=None, z=None)",
+            None,
+            None)
+
+        self._strip_non_python_syntax(
+            "(x,\n    y=None,\n      z = None  )",
+            "(x, y=None, z=None)",
+            None,
+            None)
+
+        self._strip_non_python_syntax(
+            "",
+            "",
+            None,
+            None)
+
+        self._strip_non_python_syntax(
+            None,
+            None,
+            None,
+            None)
+
 
 class TestUnwrap(unittest.TestCase):
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -200,18 +200,24 @@
 Tools/Demos
 -----------
 
-- #Issue 20456: Argument Clinic now observes the C preprocessor conditional
+- Issue #20530: Argument Clinic's signature format has been revised again.
+  The new syntax is highly human readable while still preventing false
+  positives.  The syntax also extends Python syntax to denote "self" and
+  positional-only parameters, allowing inspect.Signature objects to be
+  totally accurate for all supported builtins in Python 3.4.
+
+- Issue #20456: Argument Clinic now observes the C preprocessor conditional
   compilation statements of the C files it parses.  When a Clinic block is
   inside a conditional code, it adjusts its output to match, including
   automatically generating an empty methoddef macro.
 
-- #Issue 20456: Cloned functions in Argument Clinic now use the correct
+- Issue #20456: Cloned functions in Argument Clinic now use the correct
   name, not the name of the function they were cloned from, for text
   strings inside generated code.
 
-- #Issue 20456: Fixed Argument Clinic's test suite and "--converters" feature.
-
-- #Issue 20456: Argument Clinic now allows specifying different names
+- Issue #20456: Fixed Argument Clinic's test suite and "--converters" feature.
+
+- Issue #20456: Argument Clinic now allows specifying different names
   for a parameter in Python and C, using "as" on the parameter line.
 
 - Issue #20326: Argument Clinic now uses a simple, unique signature to
diff --git a/Modules/_cryptmodule.c b/Modules/_cryptmodule.c
--- a/Modules/_cryptmodule.c
+++ b/Modules/_cryptmodule.c
@@ -30,7 +30,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(crypt_crypt__doc__,
-"sig=($module, word, salt)\n"
+"crypt($module, word, salt, /)\n"
+"--\n"
+"\n"
 "Hash a *word* with the given *salt* and return the hashed password.\n"
 "\n"
 "*word* will usually be a user\'s password.  *salt* (either a random 2 or 16\n"
@@ -63,7 +65,7 @@
 
 static PyObject *
 crypt_crypt_impl(PyModuleDef *module, const char *word, const char *salt)
-/*[clinic end generated code: output=c7443257e03fca92 input=4d93b6d0f41fbf58]*/
+/*[clinic end generated code: output=3eaacdf994a6ff23 input=4d93b6d0f41fbf58]*/
 {
     /* On some platforms (AtheOS) crypt returns NULL for an invalid
        salt. Return None in that case. XXX Maybe raise an exception?  */
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -4159,7 +4159,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(datetime_datetime_now__doc__,
-"sig=($type, tz=None)\n"
+"now($type, /, tz=None)\n"
+"--\n"
+"\n"
 "Returns new datetime object representing current time local to tz.\n"
 "\n"
 "  tz\n"
@@ -4192,7 +4194,7 @@
 
 static PyObject *
 datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
-/*[clinic end generated code: output=c8a47308483e579a input=80d09869c5267d00]*/
+/*[clinic end generated code: output=583c5637e3c843fa input=80d09869c5267d00]*/
 {
     PyObject *self;
 
diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c
--- a/Modules/_dbmmodule.c
+++ b/Modules/_dbmmodule.c
@@ -278,7 +278,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(dbm_dbm_get__doc__,
-"sig=($self, key, default=None)\n"
+"get($self, key, default=None, /)\n"
+"--\n"
+"\n"
 "Return the value for key if present, otherwise default.");
 
 #define DBM_DBM_GET_METHODDEF    \
@@ -307,7 +309,7 @@
 
 static PyObject *
 dbm_dbm_get_impl(dbmobject *dp, const char *key, Py_ssize_clean_t key_length, PyObject *default_value)
-/*[clinic end generated code: output=2bbaf9a187f9b6bf input=aecf5efd2f2b1a3b]*/
+/*[clinic end generated code: output=452ea11394e7e92d input=aecf5efd2f2b1a3b]*/
 {
     datum dbm_key, val;
 
@@ -448,7 +450,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(dbmopen__doc__,
-"sig=($module, filename, flags=\'r\', mode=0o666)\n"
+"open($module, filename, flags=\'r\', mode=0o666, /)\n"
+"--\n"
+"\n"
 "Return a database object.\n"
 "\n"
 "  filename\n"
@@ -485,7 +489,7 @@
 
 static PyObject *
 dbmopen_impl(PyModuleDef *module, const char *filename, const char *flags, int mode)
-/*[clinic end generated code: output=a1da6a481d9d332b input=6499ab0fab1333ac]*/
+/*[clinic end generated code: output=9a7b725f9c4dcec2 input=6499ab0fab1333ac]*/
 {
     int iflags;
 
diff --git a/Modules/_opcode.c b/Modules/_opcode.c
--- a/Modules/_opcode.c
+++ b/Modules/_opcode.c
@@ -18,7 +18,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_opcode_stack_effect__doc__,
-"sig=($module, opcode, oparg=None)\n"
+"stack_effect($module, opcode, oparg=None, /)\n"
+"--\n"
+"\n"
 "Compute the stack effect of the opcode.");
 
 #define _OPCODE_STACK_EFFECT_METHODDEF    \
@@ -50,7 +52,7 @@
 
 static int
 _opcode_stack_effect_impl(PyModuleDef *module, int opcode, PyObject *oparg)
-/*[clinic end generated code: output=4fe636f5db87c0a9 input=2d0a9ee53c0418f5]*/
+/*[clinic end generated code: output=9e1133f8d587bc67 input=2d0a9ee53c0418f5]*/
 {
     int effect;
     int oparg_int = 0;
diff --git a/Modules/_sre.c b/Modules/_sre.c
--- a/Modules/_sre.c
+++ b/Modules/_sre.c
@@ -540,7 +540,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(pattern_match__doc__,
-"sig=($self, pattern, pos=0, endpos=sys.maxsize)\n"
+"match($self, /, pattern, pos=0, endpos=sys.maxsize)\n"
+"--\n"
+"\n"
 "Matches zero or more characters at the beginning of the string.");
 
 #define PATTERN_MATCH_METHODDEF    \
@@ -570,7 +572,7 @@
 
 static PyObject *
 pattern_match_impl(PatternObject *self, PyObject *pattern, Py_ssize_t pos, Py_ssize_t endpos)
-/*[clinic end generated code: output=9f5b785661677848 input=26f9fd31befe46b9]*/
+/*[clinic end generated code: output=1528eafdb8b025ad input=26f9fd31befe46b9]*/
 {
     SRE_STATE state;
     Py_ssize_t status;
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2851,26 +2851,40 @@
 );
 
 PyDoc_STRVAR(docstring_with_invalid_signature,
-"sig= (module, boo)\n"
+"docstring_with_invalid_signature($module, /, boo)\n"
 "\n"
 "This docstring has an invalid signature."
 );
 
+PyDoc_STRVAR(docstring_with_invalid_signature2,
+"docstring_with_invalid_signature2($module, /, boo)\n"
+"\n"
+"--\n"
+"\n"
+"This docstring also has an invalid signature."
+);
+
 PyDoc_STRVAR(docstring_with_signature,
-"sig=(module, sig)\n"
+"docstring_with_signature($module, /, sig)\n"
+"--\n"
+"\n"
 "This docstring has a valid signature."
 );
 
 PyDoc_STRVAR(docstring_with_signature_and_extra_newlines,
-"sig=(module, parameter)\n"
-"\n"
+"docstring_with_signature_and_extra_newlines($module, /, parameter)\n"
+"--\n"
 "\n"
 "\n"
 "This docstring has a valid signature and some extra newlines."
 );
 
 PyDoc_STRVAR(docstring_with_signature_with_defaults,
-"sig=(module, s='avocado', b=b'bytes', d=3.14, i=35, n=None, t=True, f=False, local=the_number_three, sys=sys.maxsize, exp=sys.maxsize - 1)\n"
+"docstring_with_signature_with_defaults(module, s='avocado',\n"
+"        b=b'bytes', d=3.14, i=35, n=None, t=True, f=False,\n"
+"        local=the_number_three, sys=sys.maxsize,\n"
+"        exp=sys.maxsize - 1)\n"
+"--\n"
 "\n"
 "\n"
 "\n"
@@ -3090,6 +3104,9 @@
     {"docstring_with_invalid_signature",
         (PyCFunction)test_with_docstring, METH_NOARGS,
         docstring_with_invalid_signature},
+    {"docstring_with_invalid_signature2",
+        (PyCFunction)test_with_docstring, METH_NOARGS,
+        docstring_with_invalid_signature2},
     {"docstring_with_signature",
         (PyCFunction)test_with_docstring, METH_NOARGS,
         docstring_with_signature},
diff --git a/Modules/_weakref.c b/Modules/_weakref.c
--- a/Modules/_weakref.c
+++ b/Modules/_weakref.c
@@ -20,7 +20,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_weakref_getweakrefcount__doc__,
-"sig=($module, object)\n"
+"getweakrefcount($module, object, /)\n"
+"--\n"
+"\n"
 "Return the number of weak references to \'object\'.");
 
 #define _WEAKREF_GETWEAKREFCOUNT_METHODDEF    \
@@ -46,7 +48,7 @@
 
 static Py_ssize_t
 _weakref_getweakrefcount_impl(PyModuleDef *module, PyObject *object)
-/*[clinic end generated code: output=ef51baac56180816 input=cedb69711b6a2507]*/
+/*[clinic end generated code: output=032eedbfd7d69e10 input=cedb69711b6a2507]*/
 {
     PyWeakReference **list;
 
diff --git a/Modules/clinic/_bz2module.c.h b/Modules/clinic/_bz2module.c.h
--- a/Modules/clinic/_bz2module.c.h
+++ b/Modules/clinic/_bz2module.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_bz2_BZ2Compressor_compress__doc__,
-"sig=($self, data)\n"
+"compress($self, data, /)\n"
+"--\n"
+"\n"
 "Provide data to the compressor object.\n"
 "\n"
 "Returns a chunk of compressed data if possible, or b\'\' otherwise.\n"
@@ -38,7 +40,9 @@
 }
 
 PyDoc_STRVAR(_bz2_BZ2Compressor_flush__doc__,
-"sig=($self)\n"
+"flush($self, /)\n"
+"--\n"
+"\n"
 "Finish the compression process.\n"
 "\n"
 "Returns the compressed data left in internal buffers.\n"
@@ -58,7 +62,9 @@
 }
 
 PyDoc_STRVAR(_bz2_BZ2Compressor___init____doc__,
-"sig=(compresslevel=9)\n"
+"BZ2Compressor(compresslevel=9, /)\n"
+"--\n"
+"\n"
 "Create a compressor object for compressing data incrementally.\n"
 "\n"
 "  compresslevel\n"
@@ -89,7 +95,9 @@
 }
 
 PyDoc_STRVAR(_bz2_BZ2Decompressor_decompress__doc__,
-"sig=($self, data)\n"
+"decompress($self, data, /)\n"
+"--\n"
+"\n"
 "Provide data to the decompressor object.\n"
 "\n"
 "Returns a chunk of decompressed data if possible, or b\'\' otherwise.\n"
@@ -125,7 +133,9 @@
 }
 
 PyDoc_STRVAR(_bz2_BZ2Decompressor___init____doc__,
-"sig=()\n"
+"BZ2Decompressor()\n"
+"--\n"
+"\n"
 "Create a decompressor object for decompressing data incrementally.\n"
 "\n"
 "For one-shot decompression, use the decompress() function instead.");
@@ -149,4 +159,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=aca4f6329c1c773a input=a9049054013a1b77]*/
+/*[clinic end generated code: output=21ca4405519a0931 input=a9049054013a1b77]*/
diff --git a/Modules/clinic/_lzmamodule.c.h b/Modules/clinic/_lzmamodule.c.h
--- a/Modules/clinic/_lzmamodule.c.h
+++ b/Modules/clinic/_lzmamodule.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_lzma_LZMACompressor_compress__doc__,
-"sig=($self, data)\n"
+"compress($self, data, /)\n"
+"--\n"
+"\n"
 "Provide data to the compressor object.\n"
 "\n"
 "Returns a chunk of compressed data if possible, or b\'\' otherwise.\n"
@@ -38,7 +40,9 @@
 }
 
 PyDoc_STRVAR(_lzma_LZMACompressor_flush__doc__,
-"sig=($self)\n"
+"flush($self, /)\n"
+"--\n"
+"\n"
 "Finish the compression process.\n"
 "\n"
 "Returns the compressed data left in internal buffers.\n"
@@ -58,7 +62,9 @@
 }
 
 PyDoc_STRVAR(_lzma_LZMADecompressor_decompress__doc__,
-"sig=($self, data)\n"
+"decompress($self, data, /)\n"
+"--\n"
+"\n"
 "Provide data to the decompressor object.\n"
 "\n"
 "Returns a chunk of decompressed data if possible, or b\'\' otherwise.\n"
@@ -94,7 +100,9 @@
 }
 
 PyDoc_STRVAR(_lzma_LZMADecompressor___init____doc__,
-"sig=(format=FORMAT_AUTO, memlimit=None, filters=None)\n"
+"LZMADecompressor(format=FORMAT_AUTO, memlimit=None, filters=None)\n"
+"--\n"
+"\n"
 "Create a decompressor object for decompressing data incrementally.\n"
 "\n"
 "  format\n"
@@ -137,7 +145,9 @@
 }
 
 PyDoc_STRVAR(_lzma_is_check_supported__doc__,
-"sig=($module, check_id)\n"
+"is_check_supported($module, check_id, /)\n"
+"--\n"
+"\n"
 "Test whether the given integrity check is supported.\n"
 "\n"
 "Always returns True for CHECK_NONE and CHECK_CRC32.");
@@ -165,7 +175,9 @@
 }
 
 PyDoc_STRVAR(_lzma__encode_filter_properties__doc__,
-"sig=($module, filter)\n"
+"_encode_filter_properties($module, filter, /)\n"
+"--\n"
+"\n"
 "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n"
 "\n"
 "The result does not include the filter ID itself, only the options.");
@@ -197,7 +209,9 @@
 }
 
 PyDoc_STRVAR(_lzma__decode_filter_properties__doc__,
-"sig=($module, filter_id, encoded_props)\n"
+"_decode_filter_properties($module, filter_id, encoded_props, /)\n"
+"--\n"
+"\n"
 "Return a bytes object encoding the options (properties) of the filter specified by *filter* (a dict).\n"
 "\n"
 "The result does not include the filter ID itself, only the options.");
@@ -228,4 +242,4 @@
 
     return return_value;
 }
-/*[clinic end generated code: output=fe63bc798a5c5c55 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=808fec8216ac712b input=a9049054013a1b77]*/
diff --git a/Modules/clinic/_pickle.c.h b/Modules/clinic/_pickle.c.h
--- a/Modules/clinic/_pickle.c.h
+++ b/Modules/clinic/_pickle.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_pickle_Pickler_clear_memo__doc__,
-"sig=($self)\n"
+"clear_memo($self, /)\n"
+"--\n"
+"\n"
 "Clears the pickler\'s \"memo\".\n"
 "\n"
 "The memo is the data structure that remembers which objects the\n"
@@ -24,14 +26,18 @@
 }
 
 PyDoc_STRVAR(_pickle_Pickler_dump__doc__,
-"sig=($self, obj)\n"
+"dump($self, obj, /)\n"
+"--\n"
+"\n"
 "Write a pickled representation of the given object to the open file.");
 
 #define _PICKLE_PICKLER_DUMP_METHODDEF    \
     {"dump", (PyCFunction)_pickle_Pickler_dump, METH_O, _pickle_Pickler_dump__doc__},
 
 PyDoc_STRVAR(_pickle_Pickler___init____doc__,
-"sig=(file, protocol=None, fix_imports=True)\n"
+"Pickler(file, protocol=None, fix_imports=True)\n"
+"--\n"
+"\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"
@@ -74,7 +80,9 @@
 }
 
 PyDoc_STRVAR(_pickle_PicklerMemoProxy_clear__doc__,
-"sig=($self)\n"
+"clear($self, /)\n"
+"--\n"
+"\n"
 "Remove all items from memo.");
 
 #define _PICKLE_PICKLERMEMOPROXY_CLEAR_METHODDEF    \
@@ -90,7 +98,9 @@
 }
 
 PyDoc_STRVAR(_pickle_PicklerMemoProxy_copy__doc__,
-"sig=($self)\n"
+"copy($self, /)\n"
+"--\n"
+"\n"
 "Copy the memo to a new object.");
 
 #define _PICKLE_PICKLERMEMOPROXY_COPY_METHODDEF    \
@@ -106,7 +116,9 @@
 }
 
 PyDoc_STRVAR(_pickle_PicklerMemoProxy___reduce____doc__,
-"sig=($self)\n"
+"__reduce__($self, /)\n"
+"--\n"
+"\n"
 "Implement pickle support.");
 
 #define _PICKLE_PICKLERMEMOPROXY___REDUCE___METHODDEF    \
@@ -122,7 +134,9 @@
 }
 
 PyDoc_STRVAR(_pickle_Unpickler_load__doc__,
-"sig=($self)\n"
+"load($self, /)\n"
+"--\n"
+"\n"
 "Load a pickle.\n"
 "\n"
 "Read a pickled object representation from the open file object given\n"
@@ -142,7 +156,9 @@
 }
 
 PyDoc_STRVAR(_pickle_Unpickler_find_class__doc__,
-"sig=($self, module_name, global_name)\n"
+"find_class($self, module_name, global_name, /)\n"
+"--\n"
+"\n"
 "Return an object from a specified module.\n"
 "\n"
 "If necessary, the module will be imported. Subclasses may override\n"
@@ -176,7 +192,9 @@
 }
 
 PyDoc_STRVAR(_pickle_Unpickler___init____doc__,
-"sig=(file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n"
+"Unpickler(file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n"
+"--\n"
+"\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"
@@ -222,7 +240,9 @@
 }
 
 PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_clear__doc__,
-"sig=($self)\n"
+"clear($self, /)\n"
+"--\n"
+"\n"
 "Remove all items from memo.");
 
 #define _PICKLE_UNPICKLERMEMOPROXY_CLEAR_METHODDEF    \
@@ -238,7 +258,9 @@
 }
 
 PyDoc_STRVAR(_pickle_UnpicklerMemoProxy_copy__doc__,
-"sig=($self)\n"
+"copy($self, /)\n"
+"--\n"
+"\n"
 "Copy the memo to a new object.");
 
 #define _PICKLE_UNPICKLERMEMOPROXY_COPY_METHODDEF    \
@@ -254,7 +276,9 @@
 }
 
 PyDoc_STRVAR(_pickle_UnpicklerMemoProxy___reduce____doc__,
-"sig=($self)\n"
+"__reduce__($self, /)\n"
+"--\n"
+"\n"
 "Implement pickling support.");
 
 #define _PICKLE_UNPICKLERMEMOPROXY___REDUCE___METHODDEF    \
@@ -270,7 +294,9 @@
 }
 
 PyDoc_STRVAR(_pickle_dump__doc__,
-"sig=($module, obj, file, protocol=None, *, fix_imports=True)\n"
+"dump($module, /, obj, file, protocol=None, *, fix_imports=True)\n"
+"--\n"
+"\n"
 "Write a pickled representation of obj to the open file object file.\n"
 "\n"
 "This is equivalent to ``Pickler(file, protocol).dump(obj)``, but may\n"
@@ -320,7 +346,9 @@
 }
 
 PyDoc_STRVAR(_pickle_dumps__doc__,
-"sig=($module, obj, protocol=None, *, fix_imports=True)\n"
+"dumps($module, /, obj, protocol=None, *, fix_imports=True)\n"
+"--\n"
+"\n"
 "Return the pickled representation of the object as a bytes object.\n"
 "\n"
 "The optional *protocol* argument tells the pickler to use the given\n"
@@ -361,7 +389,10 @@
 }
 
 PyDoc_STRVAR(_pickle_load__doc__,
-"sig=($module, file, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n"
+"load($module, /, file, *, fix_imports=True, encoding=\'ASCII\',\n"
+"     errors=\'strict\')\n"
+"--\n"
+"\n"
 "Read and return an object from the pickle data stored in a file.\n"
 "\n"
 "This is equivalent to ``Unpickler(file).load()``, but may be more\n"
@@ -413,7 +444,10 @@
 }
 
 PyDoc_STRVAR(_pickle_loads__doc__,
-"sig=($module, data, *, fix_imports=True, encoding=\'ASCII\', errors=\'strict\')\n"
+"loads($module, /, data, *, fix_imports=True, encoding=\'ASCII\',\n"
+"      errors=\'strict\')\n"
+"--\n"
+"\n"
 "Read and return an object from the given pickle data.\n"
 "\n"
 "The protocol version of the pickle is detected automatically, so no\n"
@@ -454,4 +488,4 @@
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=c59d4dafc2646f11 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=f965b6c7018c898d input=a9049054013a1b77]*/
diff --git a/Modules/clinic/audioop.c.h b/Modules/clinic/audioop.c.h
--- a/Modules/clinic/audioop.c.h
+++ b/Modules/clinic/audioop.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(audioop_getsample__doc__,
-"sig=($module, fragment, width, index)\n"
+"getsample($module, fragment, width, index, /)\n"
+"--\n"
+"\n"
 "Return the value of sample index from the fragment.");
 
 #define AUDIOOP_GETSAMPLE_METHODDEF    \
@@ -35,7 +37,9 @@
 }
 
 PyDoc_STRVAR(audioop_max__doc__,
-"sig=($module, fragment, width)\n"
+"max($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the maximum of the absolute value of all samples in a fragment.");
 
 #define AUDIOOP_MAX_METHODDEF    \
@@ -66,7 +70,9 @@
 }
 
 PyDoc_STRVAR(audioop_minmax__doc__,
-"sig=($module, fragment, width)\n"
+"minmax($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the minimum and maximum values of all samples in the sound fragment.");
 
 #define AUDIOOP_MINMAX_METHODDEF    \
@@ -97,7 +103,9 @@
 }
 
 PyDoc_STRVAR(audioop_avg__doc__,
-"sig=($module, fragment, width)\n"
+"avg($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the average over all samples in the fragment.");
 
 #define AUDIOOP_AVG_METHODDEF    \
@@ -128,7 +136,9 @@
 }
 
 PyDoc_STRVAR(audioop_rms__doc__,
-"sig=($module, fragment, width)\n"
+"rms($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the root-mean-square of the fragment, i.e. sqrt(sum(S_i^2)/n).");
 
 #define AUDIOOP_RMS_METHODDEF    \
@@ -159,7 +169,9 @@
 }
 
 PyDoc_STRVAR(audioop_findfit__doc__,
-"sig=($module, fragment, reference)\n"
+"findfit($module, fragment, reference, /)\n"
+"--\n"
+"\n"
 "Try to match reference as well as possible to a portion of fragment.");
 
 #define AUDIOOP_FINDFIT_METHODDEF    \
@@ -193,7 +205,9 @@
 }
 
 PyDoc_STRVAR(audioop_findfactor__doc__,
-"sig=($module, fragment, reference)\n"
+"findfactor($module, fragment, reference, /)\n"
+"--\n"
+"\n"
 "Return a factor F such that rms(add(fragment, mul(reference, -F))) is minimal.");
 
 #define AUDIOOP_FINDFACTOR_METHODDEF    \
@@ -227,7 +241,9 @@
 }
 
 PyDoc_STRVAR(audioop_findmax__doc__,
-"sig=($module, fragment, length)\n"
+"findmax($module, fragment, length, /)\n"
+"--\n"
+"\n"
 "Search fragment for a slice of specified number of samples with maximum energy.");
 
 #define AUDIOOP_FINDMAX_METHODDEF    \
@@ -258,7 +274,9 @@
 }
 
 PyDoc_STRVAR(audioop_avgpp__doc__,
-"sig=($module, fragment, width)\n"
+"avgpp($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the average peak-peak value over all samples in the fragment.");
 
 #define AUDIOOP_AVGPP_METHODDEF    \
@@ -289,7 +307,9 @@
 }
 
 PyDoc_STRVAR(audioop_maxpp__doc__,
-"sig=($module, fragment, width)\n"
+"maxpp($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the maximum peak-peak value in the sound fragment.");
 
 #define AUDIOOP_MAXPP_METHODDEF    \
@@ -320,7 +340,9 @@
 }
 
 PyDoc_STRVAR(audioop_cross__doc__,
-"sig=($module, fragment, width)\n"
+"cross($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Return the number of zero crossings in the fragment passed as an argument.");
 
 #define AUDIOOP_CROSS_METHODDEF    \
@@ -351,7 +373,9 @@
 }
 
 PyDoc_STRVAR(audioop_mul__doc__,
-"sig=($module, fragment, width, factor)\n"
+"mul($module, fragment, width, factor, /)\n"
+"--\n"
+"\n"
 "Return a fragment that has all samples in the original fragment multiplied by the floating-point value factor.");
 
 #define AUDIOOP_MUL_METHODDEF    \
@@ -383,7 +407,9 @@
 }
 
 PyDoc_STRVAR(audioop_tomono__doc__,
-"sig=($module, fragment, width, lfactor, rfactor)\n"
+"tomono($module, fragment, width, lfactor, rfactor, /)\n"
+"--\n"
+"\n"
 "Convert a stereo fragment to a mono fragment.");
 
 #define AUDIOOP_TOMONO_METHODDEF    \
@@ -416,7 +442,9 @@
 }
 
 PyDoc_STRVAR(audioop_tostereo__doc__,
-"sig=($module, fragment, width, lfactor, rfactor)\n"
+"tostereo($module, fragment, width, lfactor, rfactor, /)\n"
+"--\n"
+"\n"
 "Generate a stereo fragment from a mono fragment.");
 
 #define AUDIOOP_TOSTEREO_METHODDEF    \
@@ -449,7 +477,9 @@
 }
 
 PyDoc_STRVAR(audioop_add__doc__,
-"sig=($module, fragment1, fragment2, width)\n"
+"add($module, fragment1, fragment2, width, /)\n"
+"--\n"
+"\n"
 "Return a fragment which is the addition of the two samples passed as parameters.");
 
 #define AUDIOOP_ADD_METHODDEF    \
@@ -484,7 +514,9 @@
 }
 
 PyDoc_STRVAR(audioop_bias__doc__,
-"sig=($module, fragment, width, bias)\n"
+"bias($module, fragment, width, bias, /)\n"
+"--\n"
+"\n"
 "Return a fragment that is the original fragment with a bias added to each sample.");
 
 #define AUDIOOP_BIAS_METHODDEF    \
@@ -516,7 +548,9 @@
 }
 
 PyDoc_STRVAR(audioop_reverse__doc__,
-"sig=($module, fragment, width)\n"
+"reverse($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Reverse the samples in a fragment and returns the modified fragment.");
 
 #define AUDIOOP_REVERSE_METHODDEF    \
@@ -547,7 +581,9 @@
 }
 
 PyDoc_STRVAR(audioop_byteswap__doc__,
-"sig=($module, fragment, width)\n"
+"byteswap($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Convert big-endian samples to little-endian and vice versa.");
 
 #define AUDIOOP_BYTESWAP_METHODDEF    \
@@ -578,7 +614,9 @@
 }
 
 PyDoc_STRVAR(audioop_lin2lin__doc__,
-"sig=($module, fragment, width, newwidth)\n"
+"lin2lin($module, fragment, width, newwidth, /)\n"
+"--\n"
+"\n"
 "Convert samples between 1-, 2-, 3- and 4-byte formats.");
 
 #define AUDIOOP_LIN2LIN_METHODDEF    \
@@ -610,7 +648,10 @@
 }
 
 PyDoc_STRVAR(audioop_ratecv__doc__,
-"sig=($module, fragment, width, nchannels, inrate, outrate, state, weightA=1, weightB=0)\n"
+"ratecv($module, fragment, width, nchannels, inrate, outrate, state,\n"
+"       weightA=1, weightB=0, /)\n"
+"--\n"
+"\n"
 "Convert the frame rate of the input fragment.");
 
 #define AUDIOOP_RATECV_METHODDEF    \
@@ -647,7 +688,9 @@
 }
 
 PyDoc_STRVAR(audioop_lin2ulaw__doc__,
-"sig=($module, fragment, width)\n"
+"lin2ulaw($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Convert samples in the audio fragment to u-LAW encoding.");
 
 #define AUDIOOP_LIN2ULAW_METHODDEF    \
@@ -678,7 +721,9 @@
 }
 
 PyDoc_STRVAR(audioop_ulaw2lin__doc__,
-"sig=($module, fragment, width)\n"
+"ulaw2lin($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Convert sound fragments in u-LAW encoding to linearly encoded sound fragments.");
 
 #define AUDIOOP_ULAW2LIN_METHODDEF    \
@@ -709,7 +754,9 @@
 }
 
 PyDoc_STRVAR(audioop_lin2alaw__doc__,
-"sig=($module, fragment, width)\n"
+"lin2alaw($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Convert samples in the audio fragment to a-LAW encoding.");
 
 #define AUDIOOP_LIN2ALAW_METHODDEF    \
@@ -740,7 +787,9 @@
 }
 
 PyDoc_STRVAR(audioop_alaw2lin__doc__,
-"sig=($module, fragment, width)\n"
+"alaw2lin($module, fragment, width, /)\n"
+"--\n"
+"\n"
 "Convert sound fragments in a-LAW encoding to linearly encoded sound fragments.");
 
 #define AUDIOOP_ALAW2LIN_METHODDEF    \
@@ -771,7 +820,9 @@
 }
 
 PyDoc_STRVAR(audioop_lin2adpcm__doc__,
-"sig=($module, fragment, width, state)\n"
+"lin2adpcm($module, fragment, width, state, /)\n"
+"--\n"
+"\n"
 "Convert samples to 4 bit Intel/DVI ADPCM encoding.");
 
 #define AUDIOOP_LIN2ADPCM_METHODDEF    \
@@ -803,7 +854,9 @@
 }
 
 PyDoc_STRVAR(audioop_adpcm2lin__doc__,
-"sig=($module, fragment, width, state)\n"
+"adpcm2lin($module, fragment, width, state, /)\n"
+"--\n"
+"\n"
 "Decode an Intel/DVI ADPCM coded fragment to a linear fragment.");
 
 #define AUDIOOP_ADPCM2LIN_METHODDEF    \
@@ -833,4 +886,4 @@
 
     return return_value;
 }
-/*[clinic end generated code: output=ee7e58cfd3d0d5a6 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=be840bba5d40c2ce input=a9049054013a1b77]*/
diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h
--- a/Modules/clinic/binascii.c.h
+++ b/Modules/clinic/binascii.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(binascii_a2b_uu__doc__,
-"sig=($module, data)\n"
+"a2b_uu($module, data, /)\n"
+"--\n"
+"\n"
 "Decode a line of uuencoded data.");
 
 #define BINASCII_A2B_UU_METHODDEF    \
@@ -33,7 +35,9 @@
 }
 
 PyDoc_STRVAR(binascii_b2a_uu__doc__,
-"sig=($module, data)\n"
+"b2a_uu($module, data, /)\n"
+"--\n"
+"\n"
 "Uuencode line of data.");
 
 #define BINASCII_B2A_UU_METHODDEF    \
@@ -63,7 +67,9 @@
 }
 
 PyDoc_STRVAR(binascii_a2b_base64__doc__,
-"sig=($module, data)\n"
+"a2b_base64($module, data, /)\n"
+"--\n"
+"\n"
 "Decode a line of base64 data.");
 
 #define BINASCII_A2B_BASE64_METHODDEF    \
@@ -93,7 +99,9 @@
 }
 
 PyDoc_STRVAR(binascii_b2a_base64__doc__,
-"sig=($module, data)\n"
+"b2a_base64($module, data, /)\n"
+"--\n"
+"\n"
 "Base64-code line of data.");
 
 #define BINASCII_B2A_BASE64_METHODDEF    \
@@ -123,7 +131,9 @@
 }
 
 PyDoc_STRVAR(binascii_a2b_hqx__doc__,
-"sig=($module, data)\n"
+"a2b_hqx($module, data, /)\n"
+"--\n"
+"\n"
 "Decode .hqx coding.");
 
 #define BINASCII_A2B_HQX_METHODDEF    \
@@ -153,7 +163,9 @@
 }
 
 PyDoc_STRVAR(binascii_rlecode_hqx__doc__,
-"sig=($module, data)\n"
+"rlecode_hqx($module, data, /)\n"
+"--\n"
+"\n"
 "Binhex RLE-code binary data.");
 
 #define BINASCII_RLECODE_HQX_METHODDEF    \
@@ -183,7 +195,9 @@
 }
 
 PyDoc_STRVAR(binascii_b2a_hqx__doc__,
-"sig=($module, data)\n"
+"b2a_hqx($module, data, /)\n"
+"--\n"
+"\n"
 "Encode .hqx data.");
 
 #define BINASCII_B2A_HQX_METHODDEF    \
@@ -213,7 +227,9 @@
 }
 
 PyDoc_STRVAR(binascii_rledecode_hqx__doc__,
-"sig=($module, data)\n"
+"rledecode_hqx($module, data, /)\n"
+"--\n"
+"\n"
 "Decode hexbin RLE-coded string.");
 
 #define BINASCII_RLEDECODE_HQX_METHODDEF    \
@@ -243,7 +259,9 @@
 }
 
 PyDoc_STRVAR(binascii_crc_hqx__doc__,
-"sig=($module, data, crc)\n"
+"crc_hqx($module, data, crc, /)\n"
+"--\n"
+"\n"
 "Compute hqx CRC incrementally.");
 
 #define BINASCII_CRC_HQX_METHODDEF    \
@@ -278,7 +296,9 @@
 }
 
 PyDoc_STRVAR(binascii_crc32__doc__,
-"sig=($module, data, crc=0)\n"
+"crc32($module, data, crc=0, /)\n"
+"--\n"
+"\n"
 "Compute CRC-32 incrementally.");
 
 #define BINASCII_CRC32_METHODDEF    \
@@ -313,7 +333,9 @@
 }
 
 PyDoc_STRVAR(binascii_b2a_hex__doc__,
-"sig=($module, data)\n"
+"b2a_hex($module, data, /)\n"
+"--\n"
+"\n"
 "Hexadecimal representation of binary data.\n"
 "\n"
 "The return value is a bytes object.  This function is also\n"
@@ -346,7 +368,9 @@
 }
 
 PyDoc_STRVAR(binascii_a2b_hex__doc__,
-"sig=($module, hexstr)\n"
+"a2b_hex($module, hexstr, /)\n"
+"--\n"
+"\n"
 "Binary data of hexadecimal representation.\n"
 "\n"
 "hexstr must contain an even number of hex digits (upper or lower case).\n"
@@ -379,7 +403,9 @@
 }
 
 PyDoc_STRVAR(binascii_a2b_qp__doc__,
-"sig=($module, data, header=False)\n"
+"a2b_qp($module, /, data, header=False)\n"
+"--\n"
+"\n"
 "Decode a string of qp-encoded data.");
 
 #define BINASCII_A2B_QP_METHODDEF    \
@@ -411,7 +437,9 @@
 }
 
 PyDoc_STRVAR(binascii_b2a_qp__doc__,
-"sig=($module, data, quotetabs=False, istext=True, header=False)\n"
+"b2a_qp($module, /, data, quotetabs=False, istext=True, header=False)\n"
+"--\n"
+"\n"
 "Encode a string using quoted-printable encoding.\n"
 "\n"
 "On encoding, when istext is set, newlines are not encoded, and white\n"
@@ -447,4 +475,4 @@
 
     return return_value;
 }
-/*[clinic end generated code: output=831a8ccc9f984001 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=68e2bcc6956b6213 input=a9049054013a1b77]*/
diff --git a/Modules/clinic/zlibmodule.c.h b/Modules/clinic/zlibmodule.c.h
--- a/Modules/clinic/zlibmodule.c.h
+++ b/Modules/clinic/zlibmodule.c.h
@@ -3,7 +3,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(zlib_compress__doc__,
-"sig=($module, bytes, level=Z_DEFAULT_COMPRESSION)\n"
+"compress($module, bytes, level=Z_DEFAULT_COMPRESSION, /)\n"
+"--\n"
+"\n"
 "Returns a bytes object containing compressed data.\n"
 "\n"
 "  bytes\n"
@@ -39,7 +41,9 @@
 }
 
 PyDoc_STRVAR(zlib_decompress__doc__,
-"sig=($module, data, wbits=MAX_WBITS, bufsize=DEF_BUF_SIZE)\n"
+"decompress($module, data, wbits=MAX_WBITS, bufsize=DEF_BUF_SIZE, /)\n"
+"--\n"
+"\n"
 "Returns a bytes object containing the uncompressed data.\n"
 "\n"
 "  data\n"
@@ -78,7 +82,11 @@
 }
 
 PyDoc_STRVAR(zlib_compressobj__doc__,
-"sig=($module, level=Z_DEFAULT_COMPRESSION, method=DEFLATED, wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL, strategy=Z_DEFAULT_STRATEGY, zdict=None)\n"
+"compressobj($module, /, level=Z_DEFAULT_COMPRESSION, method=DEFLATED,\n"
+"            wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL,\n"
+"            strategy=Z_DEFAULT_STRATEGY, zdict=None)\n"
+"--\n"
+"\n"
 "Return a compressor object.\n"
 "\n"
 "  level\n"
@@ -132,7 +140,9 @@
 }
 
 PyDoc_STRVAR(zlib_decompressobj__doc__,
-"sig=($module, wbits=MAX_WBITS, zdict=b\'\')\n"
+"decompressobj($module, /, wbits=MAX_WBITS, zdict=b\'\')\n"
+"--\n"
+"\n"
 "Return a decompressor object.\n"
 "\n"
 "  wbits\n"
@@ -166,7 +176,9 @@
 }
 
 PyDoc_STRVAR(zlib_Compress_compress__doc__,
-"sig=($self, data)\n"
+"compress($self, data, /)\n"
+"--\n"
+"\n"
 "Returns a bytes object containing compressed data.\n"
 "\n"
 "  data\n"
@@ -203,7 +215,9 @@
 }
 
 PyDoc_STRVAR(zlib_Decompress_decompress__doc__,
-"sig=($self, data, max_length=0)\n"
+"decompress($self, data, max_length=0, /)\n"
+"--\n"
+"\n"
 "Return a bytes object containing the decompressed version of the data.\n"
 "\n"
 "  data\n"
@@ -245,7 +259,9 @@
 }
 
 PyDoc_STRVAR(zlib_Compress_flush__doc__,
-"sig=($self, mode=zlib.Z_FINISH)\n"
+"flush($self, mode=zlib.Z_FINISH, /)\n"
+"--\n"
+"\n"
 "Return a bytes object containing any remaining compressed data.\n"
 "\n"
 "  mode\n"
@@ -279,7 +295,9 @@
 #if defined(HAVE_ZLIB_COPY)
 
 PyDoc_STRVAR(zlib_Compress_copy__doc__,
-"sig=($self)\n"
+"copy($self, /)\n"
+"--\n"
+"\n"
 "Return a copy of the compression object.");
 
 #define ZLIB_COMPRESS_COPY_METHODDEF    \
@@ -303,7 +321,9 @@
 #if defined(HAVE_ZLIB_COPY)
 
 PyDoc_STRVAR(zlib_Decompress_copy__doc__,
-"sig=($self)\n"
+"copy($self, /)\n"
+"--\n"
+"\n"
 "Return a copy of the decompression object.");
 
 #define ZLIB_DECOMPRESS_COPY_METHODDEF    \
@@ -325,7 +345,9 @@
 #endif /* !defined(ZLIB_DECOMPRESS_COPY_METHODDEF) */
 
 PyDoc_STRVAR(zlib_Decompress_flush__doc__,
-"sig=($self, length=zlib.DEF_BUF_SIZE)\n"
+"flush($self, length=zlib.DEF_BUF_SIZE, /)\n"
+"--\n"
+"\n"
 "Return a bytes object containing any remaining decompressed data.\n"
 "\n"
 "  length\n"
@@ -354,7 +376,9 @@
 }
 
 PyDoc_STRVAR(zlib_adler32__doc__,
-"sig=($module, data, value=1)\n"
+"adler32($module, data, value=1, /)\n"
+"--\n"
+"\n"
 "Compute an Adler-32 checksum of data.\n"
 "\n"
 "  value\n"
@@ -390,7 +414,9 @@
 }
 
 PyDoc_STRVAR(zlib_crc32__doc__,
-"sig=($module, data, value=0)\n"
+"crc32($module, data, value=0, /)\n"
+"--\n"
+"\n"
 "Compute a CRC-32 checksum of data.\n"
 "\n"
 "  value\n"
@@ -424,4 +450,4 @@
 
     return return_value;
 }
-/*[clinic end generated code: output=67d3e81eafcfb982 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=bc9473721ca7c962 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -2435,7 +2435,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(os_stat__doc__,
-"sig=($module, path, *, dir_fd=None, follow_symlinks=True)\n"
+"stat($module, /, path, *, dir_fd=None, follow_symlinks=True)\n"
+"--\n"
+"\n"
 "Perform a stat system call on the given path.\n"
 "\n"
 "  path\n"
@@ -2486,7 +2488,7 @@
 
 static PyObject *
 os_stat_impl(PyModuleDef *module, path_t *path, int dir_fd, int follow_symlinks)
-/*[clinic end generated code: output=33b6ee92cd1b98de input=5ae155bd475fd20a]*/
+/*[clinic end generated code: output=f1dcaa5e24db9882 input=5ae155bd475fd20a]*/
 {
     return posix_do_stat("stat", path, dir_fd, follow_symlinks);
 }
@@ -2567,7 +2569,10 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(os_access__doc__,
-"sig=($module, path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True)\n"
+"access($module, /, path, mode, *, dir_fd=None, effective_ids=False,\n"
+"       follow_symlinks=True)\n"
+"--\n"
+"\n"
 "Use the real uid/gid to test for access to a path.\n"
 "\n"
 "  path\n"
@@ -2627,7 +2632,7 @@
 
 static PyObject *
 os_access_impl(PyModuleDef *module, path_t *path, int mode, int dir_fd, int effective_ids, int follow_symlinks)
-/*[clinic end generated code: output=33b3fafc61e778e1 input=2e2e7594371f5b7e]*/
+/*[clinic end generated code: output=a6ed4f151be9df0f input=2e2e7594371f5b7e]*/
 {
     PyObject *return_value = NULL;
 
@@ -2723,7 +2728,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(os_ttyname__doc__,
-"sig=($module, fd)\n"
+"ttyname($module, fd, /)\n"
+"--\n"
+"\n"
 "Return the name of the terminal device connected to \'fd\'.\n"
 "\n"
 "  fd\n"
@@ -2757,7 +2764,7 @@
 
 static char *
 os_ttyname_impl(PyModuleDef *module, int fd)
-/*[clinic end generated code: output=c3083e665d4d11b9 input=5f72ca83e76b3b45]*/
+/*[clinic end generated code: output=cee7bc4cffec01a2 input=5f72ca83e76b3b45]*/
 {
     char *ret;
 
diff --git a/Modules/unicodedata.c b/Modules/unicodedata.c
--- a/Modules/unicodedata.c
+++ b/Modules/unicodedata.c
@@ -129,7 +129,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(unicodedata_UCD_decimal__doc__,
-"sig=($self, unichr, default=None)\n"
+"decimal($self, unichr, default=None, /)\n"
+"--\n"
+"\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 +163,7 @@
 
 static PyObject *
 unicodedata_UCD_decimal_impl(PreviousDBVersion *self, PyUnicodeObject *unichr, PyObject *default_value)
-/*[clinic end generated code: output=a3ad5de9393acb2f input=c25c9d2b4de076b1]*/
+/*[clinic end generated code: output=8689669896d293df input=c25c9d2b4de076b1]*/
 {
     int have_old = 0;
     long rc;
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -353,13 +353,13 @@
 static PyObject *
 method_get_doc(PyMethodDescrObject *descr, void *closure)
 {
-    return _PyType_GetDocFromInternalDoc(descr->d_method->ml_doc);
+    return _PyType_GetDocFromInternalDoc(descr->d_method->ml_name, descr->d_method->ml_doc);
 }
 
 static PyObject *
 method_get_text_signature(PyMethodDescrObject *descr, void *closure)
 {
-    return _PyType_GetTextSignatureFromInternalDoc(descr->d_method->ml_doc);
+    return _PyType_GetTextSignatureFromInternalDoc(descr->d_method->ml_name, descr->d_method->ml_doc);
 }
 
 static PyObject *
@@ -466,13 +466,13 @@
 static PyObject *
 wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure)
 {
-    return _PyType_GetDocFromInternalDoc(descr->d_base->doc);
+    return _PyType_GetDocFromInternalDoc(descr->d_base->name, descr->d_base->doc);
 }
 
 static PyObject *
 wrapperdescr_get_text_signature(PyWrapperDescrObject *descr, void *closure)
 {
-    return _PyType_GetTextSignatureFromInternalDoc(descr->d_base->doc);
+    return _PyType_GetTextSignatureFromInternalDoc(descr->d_base->name, descr->d_base->doc);
 }
 
 static PyGetSetDef wrapperdescr_getset[] = {
@@ -1151,13 +1151,13 @@
 static PyObject *
 wrapper_doc(wrapperobject *wp, void *closure)
 {
-    return _PyType_GetDocFromInternalDoc(wp->descr->d_base->doc);
+    return _PyType_GetDocFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
 }
 
 static PyObject *
 wrapper_text_signature(wrapperobject *wp, void *closure)
 {
-    return _PyType_GetTextSignatureFromInternalDoc(wp->descr->d_base->doc);
+    return _PyType_GetTextSignatureFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
 }
 
 static PyObject *
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -1702,7 +1702,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(dict_fromkeys__doc__,
-"sig=($type, iterable, value=None)\n"
+"fromkeys($type, iterable, value=None, /)\n"
+"--\n"
+"\n"
 "Returns a new dict with keys from iterable and values equal to value.");
 
 #define DICT_FROMKEYS_METHODDEF    \
@@ -1730,7 +1732,7 @@
 
 static PyObject *
 dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value)
-/*[clinic end generated code: output=aff6e583703dbeba input=b85a667f9bf4669d]*/
+/*[clinic end generated code: output=55f8dc0ffa87406f input=b85a667f9bf4669d]*/
 {
     PyObject *it;       /* iter(seq) */
     PyObject *key;
@@ -2209,7 +2211,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(dict___contains____doc__,
-"sig=($self, key)\n"
+"__contains__($self, key, /)\n"
+"--\n"
+"\n"
 "True if D has a key k, else False.");
 
 #define DICT___CONTAINS___METHODDEF    \
@@ -2217,7 +2221,7 @@
 
 static PyObject *
 dict___contains__(PyDictObject *self, PyObject *key)
-/*[clinic end generated code: output=c654684a6d880281 input=b852b2a19b51ab24]*/
+/*[clinic end generated code: output=3cf3f8aaf2cc5cc3 input=b852b2a19b51ab24]*/
 {
     register PyDictObject *mp = self;
     Py_hash_t hash;
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -182,13 +182,13 @@
 static PyObject *
 meth_get__text_signature__(PyCFunctionObject *m, void *closure)
 {
-    return _PyType_GetTextSignatureFromInternalDoc(m->m_ml->ml_doc);
+    return _PyType_GetTextSignatureFromInternalDoc(m->m_ml->ml_name, m->m_ml->ml_doc);
 }
 
 static PyObject *
 meth_get__doc__(PyCFunctionObject *m, void *closure)
 {
-    return _PyType_GetDocFromInternalDoc(m->m_ml->ml_doc);
+    return _PyType_GetDocFromInternalDoc(m->m_ml->ml_name, m->m_ml->ml_doc);
 }
 
 static PyObject *
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -55,51 +55,75 @@
 slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
 
 /*
- * finds the docstring's introspection signature.
+ * finds the beginning of the docstring's introspection signature.
  * if present, returns a pointer pointing to the first '('.
  * otherwise returns NULL.
+ *
+ * doesn't guarantee that the signature is valid, only that it
+ * has a valid prefix.  (the signature must also pass skip_signature.)
  */
 static const char *
-find_signature(const char *doc)
-{
-    if (doc && !strncmp(doc, "sig=(", 5))
-        return doc + 4;
-    return NULL;
-}
-
+find_signature(const char *name, const char *doc)
+{
+    const char *dot;
+    size_t length;
+
+    if (!doc)
+        return NULL;
+
+    assert(name != NULL);
+
+    /* for dotted names like classes, only use the last component */
+    dot = strrchr(name, '.');
+    if (dot)
+        name = dot + 1;
+
+    length = strlen(name);
+    if (strncmp(doc, name, length))
+        return NULL;
+    doc += length;
+    if (*doc != '(')
+        return NULL;
+    return doc;
+}
+
+#define SIGNATURE_END_MARKER         ")\n--\n\n"
+#define SIGNATURE_END_MARKER_LENGTH  6
 /*
- * skips to the end of the docstring's instrospection signature.
+ * skips past the end of the docstring's instrospection signature.
+ * (assumes doc starts with a valid signature prefix.)
  */
 static const char *
 skip_signature(const char *doc)
 {
-    while (*doc && *doc != '\n')
+    while (*doc) {
+        if ((*doc == *SIGNATURE_END_MARKER) &&
+            !strncmp(doc, SIGNATURE_END_MARKER, SIGNATURE_END_MARKER_LENGTH))
+            return doc + SIGNATURE_END_MARKER_LENGTH;
+        if ((*doc == '\n') && (doc[1] == '\n'))
+            return NULL;
         doc++;
-    return doc;
+    }
+    return NULL;
 }
 
 static const char *
-skip_eols(const char *trace)
-{
-    while (*trace == '\n')
-        trace++;
-    return trace;
-}
-
-static const char *
-_PyType_DocWithoutSignature(const char *internal_doc)
-{
-    const char *signature = find_signature(internal_doc);
-
-    if (signature)
-        return skip_eols(skip_signature(signature));
+_PyType_DocWithoutSignature(const char *name, const char *internal_doc)
+{
+    const char *doc = find_signature(name, internal_doc);
+
+    if (doc) {
+        doc = skip_signature(doc);
+        if (doc)
+            return doc;
+        }
     return internal_doc;
 }
 
 PyObject *
-_PyType_GetDocFromInternalDoc(const char *internal_doc)
-{
-    const char *doc = _PyType_DocWithoutSignature(internal_doc);
+_PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc)
+{
+    const char *doc = _PyType_DocWithoutSignature(name, internal_doc);
 
     if (!doc) {
         Py_INCREF(Py_None);
@@ -110,18 +134,26 @@
 }
 
 PyObject *
-_PyType_GetTextSignatureFromInternalDoc(const char *internal_doc)
-{
-    const char *signature = find_signature(internal_doc);
-    const char *doc;
-
-    if (!signature) {
+_PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc)
+{
+    const char *start = find_signature(name, internal_doc);
+    const char *end;
+
+    if (start)
+        end = skip_signature(start);
+    else
+        end = NULL;
+    if (!end) {
         Py_INCREF(Py_None);
         return Py_None;
     }
 
-    doc = skip_signature(signature);
-    return PyUnicode_FromStringAndSize(signature, doc - signature);
+    /* back "end" up until it points just past the final ')' */
+    end -= SIGNATURE_END_MARKER_LENGTH - 1;
+    assert((end - start) >= 2); /* should be "()" at least */
+    assert(end[-1] == ')');
+    assert(end[0] == '\n');
+    return PyUnicode_FromStringAndSize(start, end - start);
 }
 
 unsigned int
@@ -699,7 +731,7 @@
 {
     PyObject *result;
     if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE) && type->tp_doc != NULL) {
-        return _PyType_GetDocFromInternalDoc(type->tp_doc);
+        return _PyType_GetDocFromInternalDoc(type->tp_name, type->tp_doc);
     }
     result = _PyDict_GetItemId(type->tp_dict, &PyId___doc__);
     if (result == NULL) {
@@ -719,7 +751,7 @@
 static PyObject *
 type_get_text_signature(PyTypeObject *type, void *context)
 {
-    return _PyType_GetTextSignatureFromInternalDoc(type->tp_doc);
+    return _PyType_GetTextSignatureFromInternalDoc(type->tp_name, type->tp_doc);
 }
 
 static int
@@ -2597,7 +2629,7 @@
         /* need to make a copy of the docstring slot, which usually
            points to a static string literal */
         if (slot->slot == Py_tp_doc) {
-            const char *old_doc = _PyType_DocWithoutSignature(slot->pfunc);
+            const char *old_doc = _PyType_DocWithoutSignature(type->tp_name, slot->pfunc);
             size_t len = strlen(old_doc)+1;
             char *tp_doc = PyObject_MALLOC(len);
             if (tp_doc == NULL) {
@@ -3002,7 +3034,7 @@
 
 PyDoc_STRVAR(type_doc,
 /* this text signature cannot be accurate yet.  will fix.  --larry */
-"sig=(object_or_name, bases, dict)\n"
+"type(object_or_name, bases, dict)\n"
 "type(object) -> the object's type\n"
 "type(name, bases, dict) -> a new type");
 
@@ -4198,7 +4230,7 @@
     PyObject_GenericSetAttr,                    /* tp_setattro */
     0,                                          /* tp_as_buffer */
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
-    PyDoc_STR("sig=()\nThe most base type"),  /* tp_doc */
+    PyDoc_STR("object()\n--\n\nThe most base type"),  /* tp_doc */
     0,                                          /* tp_traverse */
     0,                                          /* tp_clear */
     object_richcompare,                         /* tp_richcompare */
@@ -4665,7 +4697,8 @@
      */
     if (_PyDict_GetItemId(type->tp_dict, &PyId___doc__) == NULL) {
         if (type->tp_doc != NULL) {
-            const char *old_doc = _PyType_DocWithoutSignature(type->tp_doc);
+            const char *old_doc = _PyType_DocWithoutSignature(type->tp_name,
+                type->tp_doc);
             PyObject *doc = PyUnicode_FromString(old_doc);
             if (doc == NULL)
                 goto error;
@@ -5327,7 +5360,7 @@
 
 static struct PyMethodDef tp_new_methoddef[] = {
     {"__new__", (PyCFunction)tp_new_wrapper, METH_VARARGS|METH_KEYWORDS,
-     PyDoc_STR("sig=($type, *args, **kwargs)\n"
+     PyDoc_STR("__new__($type, *args, **kwargs)\n--\n\n"
                "Create and return a new object.  "
                "See help(type) for accurate signature.")},
     {0}
@@ -6101,22 +6134,22 @@
     ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, DOC)
 #define UNSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \
-           "sig=($self)\n" DOC)
+           NAME "($self)\n--\n\n" DOC)
 #define IBSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, WRAPPER, \
-           "sig=($self, value)\nReturn self" DOC "value.")
+           NAME "($self, value)\n--\n\nReturn self" DOC "value.")
 #define BINSLOT(NAME, SLOT, FUNCTION, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \
-           "sig=($self, value)\nReturn self" DOC "value.")
+           NAME "($self, value)\n--\n\nReturn self" DOC "value.")
 #define RBINSLOT(NAME, SLOT, FUNCTION, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, \
-           "sig=($self, value)\nReturn value" DOC "self.")
+           NAME "($self, value)\n--\n\nReturn value" DOC "self.")
 #define BINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_l, \
-           "sig=($self, value)\n" DOC)
+           NAME "($self, value)\n--\n\n" DOC)
 #define RBINSLOTNOTINFIX(NAME, SLOT, FUNCTION, DOC) \
     ETSLOT(NAME, as_number.SLOT, FUNCTION, wrap_binaryfunc_r, \
-           "sig=($self, value)\n" DOC)
+           NAME "($self, value)\n--\n\n" DOC)
 
 static slotdef slotdefs[] = {
     TPSLOT("__getattribute__", tp_getattr, NULL, NULL, ""),
@@ -6124,51 +6157,51 @@
     TPSLOT("__setattr__", tp_setattr, NULL, NULL, ""),
     TPSLOT("__delattr__", tp_setattr, NULL, NULL, ""),
     TPSLOT("__repr__", tp_repr, slot_tp_repr, wrap_unaryfunc,
-           "sig=($self)\nReturn repr(self)."),
+           "__repr__($self)\n--\n\nReturn repr(self)."),
     TPSLOT("__hash__", tp_hash, slot_tp_hash, wrap_hashfunc,
-           "sig=($self)\nReturn hash(self)."),
+           "__hash__($self)\n--\n\nReturn hash(self)."),
     FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call,
-           "sig=($self, *args, **kwargs)\nCall self as a function.",
+           "__call__($self, *args, **kwargs)\n--\n\nCall self as a function.",
            PyWrapperFlag_KEYWORDS),
     TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
-           "sig=($self)\nReturn str(self)."),
+           "__str__($self)\n--\n\nReturn str(self)."),
     TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
            wrap_binaryfunc,
-           "sig=($self, name)\nReturn getattr(self, name)."),
+           "__getattribute__($self, name)\n--\n\nReturn getattr(self, name)."),
     TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
     TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
-           "sig=($self, name, value)\nImplement setattr(self, name, value)."),
+           "__setattr__($self, name, value)\n--\n\nImplement setattr(self, name, value)."),
     TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr,
-           "sig=($self, name)\nImplement delattr(self, name)."),
+           "__delattr__($self, name)\n--\n\nImplement delattr(self, name)."),
     TPSLOT("__lt__", tp_richcompare, slot_tp_richcompare, richcmp_lt,
-           "sig=($self, value)\nReturn self<value."),
+           "__lt__($self, value)\n--\n\nReturn self<value."),
     TPSLOT("__le__", tp_richcompare, slot_tp_richcompare, richcmp_le,
-           "sig=($self, value)\nReturn self<=value."),
+           "__le__($self, value)\n--\n\nReturn self<=value."),
     TPSLOT("__eq__", tp_richcompare, slot_tp_richcompare, richcmp_eq,
-           "sig=($self, value)\nReturn self==value."),
+           "__eq__($self, value)\n--\n\nReturn self==value."),
     TPSLOT("__ne__", tp_richcompare, slot_tp_richcompare, richcmp_ne,
-           "sig=($self, value)\nReturn self!=value."),
+           "__ne__($self, value)\n--\n\nReturn self!=value."),
     TPSLOT("__gt__", tp_richcompare, slot_tp_richcompare, richcmp_gt,
-           "sig=($self, value)\nReturn self>value."),
+           "__gt__($self, value)\n--\n\nReturn self>value."),
     TPSLOT("__ge__", tp_richcompare, slot_tp_richcompare, richcmp_ge,
-           "sig=($self, value)\nReturn self>=value."),
+           "__ge__=($self, value)\n--\n\nReturn self>=value."),
     TPSLOT("__iter__", tp_iter, slot_tp_iter, wrap_unaryfunc,
-           "sig=($self)\nImplement iter(self)."),
+           "__iter__($self)\n--\n\nImplement iter(self)."),
     TPSLOT("__next__", tp_iternext, slot_tp_iternext, wrap_next,
-           "sig=($self)\nImplement next(self)."),
+           "__next__($self)\n--\n\nImplement next(self)."),
     TPSLOT("__get__", tp_descr_get, slot_tp_descr_get, wrap_descr_get,
-           "sig=($self, instance, owner)\nReturn an attribute of instance, which is of type owner."),
+           "__get__($self, instance, owner)\n--\n\nReturn an attribute of instance, which is of type owner."),
     TPSLOT("__set__", tp_descr_set, slot_tp_descr_set, wrap_descr_set,
-           "sig=($self, instance, value)\nSet an attribute of instance to value."),
+           "__set__($self, instance, value)\n--\n\nSet an attribute of instance to value."),
     TPSLOT("__delete__", tp_descr_set, slot_tp_descr_set,
            wrap_descr_delete,
-           "sig=(instance)\nDelete an attribute of instance."),
+           "__delete__(instance)\n--\n\nDelete an attribute of instance."),
     FLSLOT("__init__", tp_init, slot_tp_init, (wrapperfunc)wrap_init,
-           "sig=($self, *args, **kwargs)\n"
+           "__init__($self, *args, **kwargs)\n--\n\n"
            "Initialize self.  See help(type(self)) for accurate signature.",
            PyWrapperFlag_KEYWORDS),
     TPSLOT("__new__", tp_new, slot_tp_new, NULL,
-           "sig=(type, *args, **kwargs)\n"
+           "__new__(type, *args, **kwargs)\n--\n\n"
            "Create and return new object.  See help(type) for accurate signature."),
     TPSLOT("__del__", tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
 
@@ -6193,9 +6226,9 @@
     RBINSLOTNOTINFIX("__rdivmod__", nb_divmod, slot_nb_divmod,
            "Return divmod(value, self)."),
     NBSLOT("__pow__", nb_power, slot_nb_power, wrap_ternaryfunc,
-           "sig=($self, value, mod=None)\nReturn pow(self, value, mod)."),
+           "__pow__($self, value, mod=None)\n--\n\nReturn pow(self, value, mod)."),
     NBSLOT("__rpow__", nb_power, slot_nb_power, wrap_ternaryfunc_r,
-           "sig=($self, value, mod=None)\nReturn pow(value, self, mod)."),
+           "__rpow__($self, value, mod=None)\n--\n\nReturn pow(value, self, mod)."),
     UNSLOT("__neg__", nb_negative, slot_nb_negative, wrap_unaryfunc, "-self"),
     UNSLOT("__pos__", nb_positive, slot_nb_positive, wrap_unaryfunc, "+self"),
     UNSLOT("__abs__", nb_absolute, slot_nb_absolute, wrap_unaryfunc,
@@ -6246,48 +6279,48 @@
     IBSLOT("__itruediv__", nb_inplace_true_divide,
            slot_nb_inplace_true_divide, wrap_binaryfunc, "/"),
     NBSLOT("__index__", nb_index, slot_nb_index, wrap_unaryfunc,
-           "sig=($self)\n"
+           "__index__($self)\n--\n\n"
            "Return self converted to an integer, if self is suitable"
            "for use as an index into a list."),
     MPSLOT("__len__", mp_length, slot_mp_length, wrap_lenfunc,
-           "sig=($self)\nReturn len(self)."),
+           "__len__($self)\n--\n\nReturn len(self)."),
     MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,
            wrap_binaryfunc,
-           "sig=($self, key)\nReturn self[key]."),
+           "__getitem__($self, key)\n--\n\nReturn self[key]."),
     MPSLOT("__setitem__", mp_ass_subscript, slot_mp_ass_subscript,
            wrap_objobjargproc,
-           "sig=($self, key, value)\nSet self[key] to value."),
+           "__setitem__($self, key, value)\n--\n\nSet self[key] to value."),
     MPSLOT("__delitem__", mp_ass_subscript, slot_mp_ass_subscript,
            wrap_delitem,
-           "sig=(key)\nDelete self[key]."),
+           "__delitem__(key)\n--\n\nDelete self[key]."),
 
     SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
-           "sig=($self)\nReturn len(self)."),
+           "__len__($self)\n--\n\nReturn len(self)."),
     /* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
        The logic in abstract.c always falls back to nb_add/nb_multiply in
        this case.  Defining both the nb_* and the sq_* slots to call the
        user-defined methods has unexpected side-effects, as shown by
        test_descr.notimplemented() */
     SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
-           "sig=($self, value)\nReturn self+value."),
+           "__add__($self, value)\n--\n\nReturn self+value."),
     SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
-           "sig=($self, value)\nReturn self*value.n"),
+           "__mul__($self, value)\n--\n\nReturn self*value.n"),
     SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,
-           "sig=($self, value)\nReturn self*value."),
+           "__rmul__($self, value)\n--\n\nReturn self*value."),
     SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,
-           "sig=($self, key)\nReturn self[key]."),
+           "__getitem__($self, key)\n--\n\nReturn self[key]."),
     SQSLOT("__setitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_setitem,
-           "sig=($self, key, value)\nSet self[key] to value."),
+           "__setitem__($self, key, value)\n--\n\nSet self[key] to value."),
     SQSLOT("__delitem__", sq_ass_item, slot_sq_ass_item, wrap_sq_delitem,
-           "sig=($self, key)\nDelete self[key]."),
+           "__delitem__($self, key)\n--\n\nDelete self[key]."),
     SQSLOT("__contains__", sq_contains, slot_sq_contains, wrap_objobjproc,
-           "sig=($self, key)\nReturn key in self."),
+           "__contains__($self, key)\n--\n\nReturn key in self."),
     SQSLOT("__iadd__", sq_inplace_concat, NULL,
            wrap_binaryfunc,
-           "sig=($self, value)\nImplement self+=value."),
+           "__iadd__($self, value)\n--\n\nImplement self+=value."),
     SQSLOT("__imul__", sq_inplace_repeat, NULL,
            wrap_indexargfunc,
-           "sig=($self, value)\nImplement self*=value."),
+           "__imul__($self, value)\n--\n\nImplement self*=value."),
 
     {NULL}
 };
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -12893,7 +12893,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(unicode_maketrans__doc__,
-"sig=(x, y=None, z=None)\n"
+"maketrans(x, y=None, z=None, /)\n"
+"--\n"
+"\n"
 "Return a translation table usable for str.translate().\n"
 "\n"
 "If there is only one argument, it must be a dictionary mapping Unicode\n"
@@ -12930,7 +12932,7 @@
 
 static PyObject *
 unicode_maketrans_impl(PyObject *x, PyObject *y, PyObject *z)
-/*[clinic end generated code: output=ca001ac83ed32269 input=7bfbf529a293c6c5]*/
+/*[clinic end generated code: output=566edf630f77436a input=7bfbf529a293c6c5]*/
 {
     PyObject *new = NULL, *key, *value;
     Py_ssize_t i = 0;
diff --git a/Python/import.c b/Python/import.c
--- a/Python/import.c
+++ b/Python/import.c
@@ -232,7 +232,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_lock_held__doc__,
-"sig=($module)\n"
+"lock_held($module, /)\n"
+"--\n"
+"\n"
 "Return True if the import lock is currently held, else False.\n"
 "\n"
 "On platforms without threads, return False.");
@@ -251,7 +253,7 @@
 
 static PyObject *
 _imp_lock_held_impl(PyModuleDef *module)
-/*[clinic end generated code: output=5ce46d12a8e4c469 input=9b088f9b217d9bdf]*/
+/*[clinic end generated code: output=dae65674966baa65 input=9b088f9b217d9bdf]*/
 {
 #ifdef WITH_THREAD
     return PyBool_FromLong(import_lock_thread != -1);
@@ -270,7 +272,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_acquire_lock__doc__,
-"sig=($module)\n"
+"acquire_lock($module, /)\n"
+"--\n"
+"\n"
 "Acquires the interpreter\'s import lock for the current thread.\n"
 "\n"
 "This lock should be used by import hooks to ensure thread-safety when importing\n"
@@ -290,7 +294,7 @@
 
 static PyObject *
 _imp_acquire_lock_impl(PyModuleDef *module)
-/*[clinic end generated code: output=b0dd6a132ad25961 input=4a2d4381866d5fdc]*/
+/*[clinic end generated code: output=478f1fa089fdb9a4 input=4a2d4381866d5fdc]*/
 {
 #ifdef WITH_THREAD
     _PyImport_AcquireLock();
@@ -308,7 +312,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_release_lock__doc__,
-"sig=($module)\n"
+"release_lock($module, /)\n"
+"--\n"
+"\n"
 "Release the interpreter\'s import lock.\n"
 "\n"
 "On platforms without threads, this function does nothing.");
@@ -327,7 +333,7 @@
 
 static PyObject *
 _imp_release_lock_impl(PyModuleDef *module)
-/*[clinic end generated code: output=b1e6e9d723cf5f89 input=934fb11516dd778b]*/
+/*[clinic end generated code: output=36c77a6832fdafd4 input=934fb11516dd778b]*/
 {
 #ifdef WITH_THREAD
     if (_PyImport_ReleaseLock() < 0) {
@@ -927,7 +933,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp__fix_co_filename__doc__,
-"sig=($module, code, path)\n"
+"_fix_co_filename($module, code, path, /)\n"
+"--\n"
+"\n"
 "Changes code.co_filename to specify the passed-in file path.\n"
 "\n"
 "  code\n"
@@ -960,7 +968,7 @@
 
 static PyObject *
 _imp__fix_co_filename_impl(PyModuleDef *module, PyCodeObject *code, PyObject *path)
-/*[clinic end generated code: output=3fe5b5a1b0d497df input=895ba50e78b82f05]*/
+/*[clinic end generated code: output=6b4b1edeb0d55c5d input=895ba50e78b82f05]*/
 
 {
     update_compiled_module(code, path);
@@ -1823,7 +1831,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_extension_suffixes__doc__,
-"sig=($module)\n"
+"extension_suffixes($module, /)\n"
+"--\n"
+"\n"
 "Returns the list of file suffixes used to identify extension modules.");
 
 #define _IMP_EXTENSION_SUFFIXES_METHODDEF    \
@@ -1840,7 +1850,7 @@
 
 static PyObject *
 _imp_extension_suffixes_impl(PyModuleDef *module)
-/*[clinic end generated code: output=c1bcfbddabefa00a input=ecdeeecfcb6f839e]*/
+/*[clinic end generated code: output=bb30a2438167798c input=ecdeeecfcb6f839e]*/
 {
     PyObject *list;
     const char *suffix;
@@ -1878,7 +1888,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_init_builtin__doc__,
-"sig=($module, name)\n"
+"init_builtin($module, name, /)\n"
+"--\n"
+"\n"
 "Initializes a built-in module.");
 
 #define _IMP_INIT_BUILTIN_METHODDEF    \
@@ -1905,7 +1917,7 @@
 
 static PyObject *
 _imp_init_builtin_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=02437efd4668f53e input=f934d2231ec52a2e]*/
+/*[clinic end generated code: output=a0244948a43f8e26 input=f934d2231ec52a2e]*/
 {
     int ret;
     PyObject *m;
@@ -1932,7 +1944,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_init_frozen__doc__,
-"sig=($module, name)\n"
+"init_frozen($module, name, /)\n"
+"--\n"
+"\n"
 "Initializes a frozen module.");
 
 #define _IMP_INIT_FROZEN_METHODDEF    \
@@ -1959,7 +1973,7 @@
 
 static PyObject *
 _imp_init_frozen_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=20cea421af513afe input=13019adfc04f3fb3]*/
+/*[clinic end generated code: output=e4bc2bff296f8f22 input=13019adfc04f3fb3]*/
 {
     int ret;
     PyObject *m;
@@ -1986,7 +2000,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_get_frozen_object__doc__,
-"sig=($module, name)\n"
+"get_frozen_object($module, name, /)\n"
+"--\n"
+"\n"
 "Create a code object for a frozen module.");
 
 #define _IMP_GET_FROZEN_OBJECT_METHODDEF    \
@@ -2013,7 +2029,7 @@
 
 static PyObject *
 _imp_get_frozen_object_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=f00d01ae30ec842f input=ed689bc05358fdbd]*/
+/*[clinic end generated code: output=4089ec702a9d70c5 input=ed689bc05358fdbd]*/
 {
     return get_frozen_object(name);
 }
@@ -2028,7 +2044,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_is_frozen_package__doc__,
-"sig=($module, name)\n"
+"is_frozen_package($module, name, /)\n"
+"--\n"
+"\n"
 "Returns True if the module name is of a frozen package.");
 
 #define _IMP_IS_FROZEN_PACKAGE_METHODDEF    \
@@ -2055,7 +2073,7 @@
 
 static PyObject *
 _imp_is_frozen_package_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=35c78f2448c6fcff input=81b6cdecd080fbb8]*/
+/*[clinic end generated code: output=86aab14dcd4b959b input=81b6cdecd080fbb8]*/
 {
     return is_frozen_package(name);
 }
@@ -2070,7 +2088,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_is_builtin__doc__,
-"sig=($module, name)\n"
+"is_builtin($module, name, /)\n"
+"--\n"
+"\n"
 "Returns True if the module name corresponds to a built-in module.");
 
 #define _IMP_IS_BUILTIN_METHODDEF    \
@@ -2097,7 +2117,7 @@
 
 static PyObject *
 _imp_is_builtin_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=641689f833347f66 input=86befdac021dd1c7]*/
+/*[clinic end generated code: output=d5847f8cac50946e input=86befdac021dd1c7]*/
 {
     return PyLong_FromLong(is_builtin(name));
 }
@@ -2112,7 +2132,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_is_frozen__doc__,
-"sig=($module, name)\n"
+"is_frozen($module, name, /)\n"
+"--\n"
+"\n"
 "Returns True if the module name corresponds to a frozen module.");
 
 #define _IMP_IS_FROZEN_METHODDEF    \
@@ -2139,7 +2161,7 @@
 
 static PyObject *
 _imp_is_frozen_impl(PyModuleDef *module, PyObject *name)
-/*[clinic end generated code: output=0f80c7a3f283a686 input=7301dbca1897d66b]*/
+/*[clinic end generated code: output=6691af884ba4987d input=7301dbca1897d66b]*/
 {
     const struct _frozen *p;
 
@@ -2161,7 +2183,9 @@
 [clinic start generated code]*/
 
 PyDoc_STRVAR(_imp_load_dynamic__doc__,
-"sig=($module, name, path, file=None)\n"
+"load_dynamic($module, name, path, file=None, /)\n"
+"--\n"
+"\n"
 "Loads an extension module.");
 
 #define _IMP_LOAD_DYNAMIC_METHODDEF    \
@@ -2190,7 +2214,7 @@
 
 static PyObject *
 _imp_load_dynamic_impl(PyModuleDef *module, PyObject *name, PyObject *path, PyObject *file)
-/*[clinic end generated code: output=8f33f48dc6252948 input=af64f06e4bad3526]*/
+/*[clinic end generated code: output=81d11a1fbd1ea0a8 input=af64f06e4bad3526]*/
 {
     PyObject *mod;
     FILE *fp;
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -1123,10 +1123,12 @@
         sys.stdout = saved_stdout
 
 
-def create_regex(before, after, word=True):
+def create_regex(before, after, word=True, whole_line=True):
     """Create an re object for matching marker lines."""
     group_re = "\w+" if word else ".+"
-    pattern = r'^{}({}){}$'
+    pattern = r'{}({}){}'
+    if whole_line:
+        pattern = '^' + pattern + '$'
     pattern = pattern.format(re.escape(before), group_re, re.escape(after))
     return re.compile(pattern)
 
@@ -1218,6 +1220,7 @@
         self.language = language
         before, _, after = language.start_line.partition('{dsl_name}')
         assert _ == '{dsl_name}'
+        self.find_start_re = create_regex(before, after, whole_line=False)
         self.start_re = create_regex(before, after)
         self.verify = verify
         self.last_checksum_re = None
@@ -1735,11 +1738,15 @@
     except KeyError:
         fail("Can't identify file type for file " + repr(filename))
 
-    clinic = Clinic(language, force=force, verify=verify, filename=filename)
-
     with open(filename, 'r', encoding=encoding) as f:
         raw = f.read()
 
+    # exit quickly if there are no clinic markers in the file
+    find_start_re = BlockParser("", language).find_start_re
+    if not find_start_re.search(raw):
+        return
+
+    clinic = Clinic(language, force=force, verify=verify, filename=filename)
     cooked = clinic.parse(raw)
     if (cooked == raw) and not force:
         return
@@ -1897,7 +1904,7 @@
                  full_name=None,
                  return_converter, return_annotation=_empty,
                  docstring=None, kind=CALLABLE, coexist=False,
-                 suppress_signature=False):
+                 docstring_only=False):
         self.parameters = parameters or collections.OrderedDict()
         self.return_annotation = return_annotation
         self.name = name
@@ -1911,7 +1918,11 @@
         self.kind = kind
         self.coexist = coexist
         self.self_converter = None
-        self.suppress_signature = suppress_signature
+        # docstring_only means "don't generate a machine-readable
+        # signature, just a normal docstring".  it's True for
+        # functions with optional groups because we can't represent
+        # those accurately with inspect.Signature in 3.4.
+        self.docstring_only = docstring_only
 
         self.rendered_parameters = None
 
@@ -1951,7 +1962,7 @@
             'full_name': self.full_name,
             'return_converter': self.return_converter, 'return_annotation': self.return_annotation,
             'docstring': self.docstring, 'kind': self.kind, 'coexist': self.coexist,
-            'suppress_signature': self.suppress_signature,
+            'docstring_only': self.docstring_only,
             }
         kwargs.update(overrides)
         f = Function(**kwargs)
@@ -1987,6 +1998,9 @@
     def is_keyword_only(self):
         return self.kind == inspect.Parameter.KEYWORD_ONLY
 
+    def is_positional_only(self):
+        return self.kind == inspect.Parameter.POSITIONAL_ONLY
+
     def copy(self, **overrides):
         kwargs = {
             'name': self.name, 'kind': self.kind, 'default':self.default,
@@ -2929,7 +2943,7 @@
         Returns the length of the line's margin.
         """
         if '\t' in line:
-            fail('Tab characters are illegal in the Clinic DSL.')
+            fail('Tab characters are illegal in the Argument Clinic DSL.')
         stripped = line.lstrip()
         if not len(stripped):
             # we can't tell anything from an empty line
@@ -3694,7 +3708,7 @@
             else:
                 fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".b)")
             self.group += 1
-            self.function.suppress_signature = True
+            self.function.docstring_only = True
         elif symbol == ']':
             if not self.group:
                 fail("Function " + self.function.name + " has a ] without a matching [.")
@@ -3783,21 +3797,20 @@
             # don't render a docstring at all, no signature, nothing.
             return f.docstring
 
-        add, output = text_accumulator()
+        text, add, output = _text_accumulator()
         parameters = f.render_parameters
 
         ##
         ## docstring first line
         ##
 
-        if not f.suppress_signature:
-            add('sig=')
+        if new_or_init:
+            # classes get *just* the name of the class
+            # not __new__, not __init__, and not module.classname
+            assert f.cls
+            add(f.cls.name)
         else:
-            if new_or_init:
-                assert f.cls
-                add(f.cls.name)
-            else:
-                add(f.name)
+            add(f.name)
         add('(')
 
         # populate "right_bracket_count" field for every parameter
@@ -3834,53 +3847,105 @@
                 right_bracket_count -= 1
             return s
 
+        need_slash = False
+        added_slash = False
+        need_a_trailing_slash = False
+
+        # we only need a trailing slash:
+        #   * if this is not a "docstring_only" signature
+        #   * and if the last *shown* parameter is
+        #     positional only
+        if not f.docstring_only:
+            for p in reversed(parameters):
+                if not p.converter.show_in_signature:
+                    continue
+                if p.is_positional_only():
+                    need_a_trailing_slash = True
+                break
+
+
         added_star = False
-        add_comma = False
+
+        first_parameter = True
+        last_p = parameters[-1]
+        line_length = len(''.join(text))
+        indent = " " * line_length
+        def add_parameter(text):
+            nonlocal line_length
+            nonlocal first_parameter
+            if first_parameter:
+                s = text
+                first_parameter = False
+            else:
+                s = ' ' + text
+                if line_length + len(s) >= 72:
+                    add('\n')
+                    add(indent)
+                    line_length = len(indent)
+                    s = text
+            line_length += len(s)
+            add(s)
 
         for p in parameters:
             if not p.converter.show_in_signature:
                 continue
-
             assert p.name
 
+            is_self = isinstance(p.converter, self_converter)
+            if is_self and f.docstring_only:
+                # this isn't a real machine-parsable signature,
+                # so let's not print the "self" parameter
+                continue
+
+            if p.is_positional_only():
+                need_slash = not f.docstring_only
+            elif need_slash and not (added_slash or p.is_positional_only()):
+                added_slash = True
+                add_parameter('/,')
+
             if p.is_keyword_only() and not added_star:
                 added_star = True
-                if add_comma:
-                    add(', ')
-                add('*')
-                add_comma = True
+                add_parameter('*,')
+
+            p_add, p_output = text_accumulator()
+            p_add(fix_right_bracket_count(p.right_bracket_count))
+
+            if isinstance(p.converter, self_converter):
+                # annotate first parameter as being a "self".
+                #
+                # if inspect.Signature gets this function,
+                # and it's already bound, the self parameter
+                # will be stripped off.
+                #
+                # if it's not bound, it should be marked
+                # as positional-only.
+                #
+                # note: we don't print "self" for __init__,
+                # because this isn't actually the signature
+                # for __init__.  (it can't be, __init__ doesn't
+                # have a docstring.)  if this is an __init__
+                # (or __new__), then this signature is for
+                # calling the class to contruct a new instance.
+                p_add('$')
 
             name = p.converter.signature_name or p.name
-
-            a = []
-            if isinstance(p.converter, self_converter):
-                if f.suppress_signature:
-                    continue
-                else:
-                    # annotate first parameter as being a "self".
-                    #
-                    # if inspect.Signature gets this function, and it's already bound,
-                    # the self parameter will be stripped off.
-                    #
-                    # if it's not bound, it should be marked as positional-only.
-                    a.append('$')
-                    a.append(name)
-            else:
-                a.append(name)
+            p_add(name)
+
             if p.converter.is_optional():
-                a.append('=')
+                p_add('=')
                 value = p.converter.py_default
                 if not value:
                     value = repr(p.converter.default)
-                a.append(value)
-            s = fix_right_bracket_count(p.right_bracket_count)
-            s += "".join(a)
-            if add_comma:
-                add(', ')
-            add(s)
-            add_comma = True
+                p_add(value)
+
+            if (p != last_p) or need_a_trailing_slash:
+                p_add(',')
+
+            add_parameter(p_output())
 
         add(fix_right_bracket_count(0))
+        if need_a_trailing_slash:
+            add_parameter('/')
         add(')')
 
         # PEP 8 says:
@@ -3896,6 +3961,9 @@
         #     add(' -> ')
         #     add(f.return_converter.py_default)
 
+        if not f.docstring_only:
+            add("\n--\n")
+
         docstring_first_line = output()
 
         # now fix up the places where the brackets look wrong
diff --git a/Tools/clinic/clinic_test.py b/Tools/clinic/clinic_test.py
--- a/Tools/clinic/clinic_test.py
+++ b/Tools/clinic/clinic_test.py
@@ -359,7 +359,9 @@
 
 Perform a stat system call on the given path.""")
         self.assertEqual("""
-sig=($module, path)
+stat($module, /, path)
+--
+
 Perform a stat system call on the given path.
 
   path
@@ -379,7 +381,9 @@
 Okay, we're done here.
 """)
         self.assertEqual("""
-sig=($module, x, y)
+bar($module, /, x, y)
+--
+
 This is the documentation for foo.
 
   x
@@ -395,7 +399,7 @@
     path: str
 This/used to break Clinic!
 """)
-        self.assertEqual("sig=($module, path)\n\nThis/used to break Clinic!", function.docstring)
+        self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
 
     def test_c_name(self):
         function = self.parse_function("module os\nos.stat as os_stat_fn")
@@ -504,7 +508,8 @@
             self.assertEqual(p.kind, inspect.Parameter.POSITIONAL_ONLY)
 
         self.assertEqual(function.docstring.strip(), """
-imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5, attr6]])
+imaginary([[y1, y2,] x1, x2,] ch, [attr1, attr2, attr3, [attr4, attr5,
+          attr6]])
 
 
   y1
@@ -624,9 +629,23 @@
 Docstring
 
 """)
-        self.assertEqual("sig=($module)\nDocstring", function.docstring)
+        self.assertEqual("bar($module, /)\n--\n\nDocstring", function.docstring)
         self.assertEqual(1, len(function.parameters)) # self!
 
+    def test_init_with_no_parameters(self):
+        function = self.parse_function("""
+module foo
+class foo.Bar "unused" "notneeded"
+foo.Bar.__init__
+
+Docstring
+
+""", signatures_in_block=3, function_index=2)
+        # self is not in the signature
+        self.assertEqual("Bar()\n--\n\nDocstring", function.docstring)
+        # but it *is* a parameter
+        self.assertEqual(1, len(function.parameters))
+
     def test_illegal_module_line(self):
         self.parse_function_should_fail("""
 module foo
@@ -719,7 +738,9 @@
   Not at column 0!
 """)
         self.assertEqual("""
-sig=($module, x, *, y)
+bar($module, /, x, *, y)
+--
+
 Not at column 0!
 
   x
@@ -733,7 +754,7 @@
     path: str
 This/used to break Clinic!
 """)
-        self.assertEqual("sig=($module, path)\nThis/used to break Clinic!", function.docstring)
+        self.assertEqual("stat($module, /, path)\n--\n\nThis/used to break Clinic!", function.docstring)
 
     def test_directive(self):
         c = FakeClinic()
@@ -756,13 +777,13 @@
         parser.parse(block)
         return block
 
-    def parse_function(self, text):
+    def parse_function(self, text, signatures_in_block=2, function_index=1):
         block = self.parse(text)
         s = block.signatures
-        self.assertEqual(len(s), 2)
+        self.assertEqual(len(s), signatures_in_block)
         assert isinstance(s[0], clinic.Module)
-        assert isinstance(s[1], clinic.Function)
-        return s[1]
+        assert isinstance(s[function_index], clinic.Function)
+        return s[function_index]
 
     def test_scaffolding(self):
         # test repr on special values

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list