https://github.com/python/cpython/commit/0d42ac9474f857633d00b414c0715f4efa73f1ca
commit: 0d42ac9474f857633d00b414c0715f4efa73f1ca
branch: main
author: Erlend E. Aasland <[email protected]>
committer: erlend-aasland <[email protected]>
date: 2024-04-10T10:12:05+02:00
summary:

gh-117431: Argument Clinic: copy forced text signature when cloning (#117591)

files:
M Lib/test/test_clinic.py
M Objects/clinic/unicodeobject.c.h
M Tools/clinic/libclinic/dsl_parser.py
M Tools/clinic/libclinic/function.py

diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index a5887cdb56e3ca..e3ba3d943216de 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -5,7 +5,7 @@
 from functools import partial
 from test import support, test_tools
 from test.support import os_helper
-from test.support.os_helper import TESTFN, unlink
+from test.support.os_helper import TESTFN, unlink, rmtree
 from textwrap import dedent
 from unittest import TestCase
 import inspect
@@ -662,6 +662,61 @@ class C "void *" ""
         err = "Illegal C basename: '.illegal.'"
         self.expect_failure(block, err, lineno=7)
 
+    def test_cloned_forced_text_signature(self):
+        block = dedent("""
+            /*[clinic input]
+            @text_signature "($module, a[, b])"
+            src
+                a: object
+                    param a
+                b: object = NULL
+                /
+
+            docstring
+            [clinic start generated code]*/
+
+            /*[clinic input]
+            dst = src
+            [clinic start generated code]*/
+        """)
+        self.clinic.parse(block)
+        self.addCleanup(rmtree, "clinic")
+        funcs = self.clinic.functions
+        self.assertEqual(len(funcs), 2)
+
+        src_docstring_lines = funcs[0].docstring.split("\n")
+        dst_docstring_lines = funcs[1].docstring.split("\n")
+
+        # Signatures are copied.
+        self.assertEqual(src_docstring_lines[0], "src($module, a[, b])")
+        self.assertEqual(dst_docstring_lines[0], "dst($module, a[, b])")
+
+        # Param docstrings are copied.
+        self.assertIn("    param a", src_docstring_lines)
+        self.assertIn("    param a", dst_docstring_lines)
+
+        # Docstrings are not copied.
+        self.assertIn("docstring", src_docstring_lines)
+        self.assertNotIn("docstring", dst_docstring_lines)
+
+    def test_cloned_forced_text_signature_illegal(self):
+        block = """
+            /*[clinic input]
+            @text_signature "($module, a[, b])"
+            src
+                a: object
+                b: object = NULL
+                /
+            [clinic start generated code]*/
+
+            /*[clinic input]
+            @text_signature "($module, a_override[, b])"
+            dst = src
+            [clinic start generated code]*/
+        """
+        err = "Cannot use @text_signature when cloning a function"
+        self.expect_failure(block, err, lineno=11)
+
 
 class ParseFileUnitTest(TestCase):
     def expect_parsing_failure(
diff --git a/Objects/clinic/unicodeobject.c.h b/Objects/clinic/unicodeobject.c.h
index 01c40b90d9b4b8..78e14b0021d006 100644
--- a/Objects/clinic/unicodeobject.c.h
+++ b/Objects/clinic/unicodeobject.c.h
@@ -357,7 +357,7 @@ unicode_expandtabs(PyObject *self, PyObject *const *args, 
Py_ssize_t nargs, PyOb
 }
 
 PyDoc_STRVAR(unicode_find__doc__,
-"find($self, sub, start=None, end=None, /)\n"
+"find($self, sub[, start[, end]], /)\n"
 "--\n"
 "\n"
 "Return the lowest index in S where substring sub is found, such that sub is 
contained within S[start:end].\n"
@@ -413,7 +413,7 @@ unicode_find(PyObject *str, PyObject *const *args, 
Py_ssize_t nargs)
 }
 
 PyDoc_STRVAR(unicode_index__doc__,
-"index($self, sub, start=None, end=None, /)\n"
+"index($self, sub[, start[, end]], /)\n"
 "--\n"
 "\n"
 "Return the lowest index in S where substring sub is found, such that sub is 
contained within S[start:end].\n"
@@ -1060,7 +1060,7 @@ unicode_removesuffix(PyObject *self, PyObject *arg)
 }
 
 PyDoc_STRVAR(unicode_rfind__doc__,
-"rfind($self, sub, start=None, end=None, /)\n"
+"rfind($self, sub[, start[, end]], /)\n"
 "--\n"
 "\n"
 "Return the highest index in S where substring sub is found, such that sub is 
contained within S[start:end].\n"
@@ -1116,7 +1116,7 @@ unicode_rfind(PyObject *str, PyObject *const *args, 
Py_ssize_t nargs)
 }
 
 PyDoc_STRVAR(unicode_rindex__doc__,
-"rindex($self, sub, start=None, end=None, /)\n"
+"rindex($self, sub[, start[, end]], /)\n"
 "--\n"
 "\n"
 "Return the highest index in S where substring sub is found, such that sub is 
contained within S[start:end].\n"
@@ -1888,4 +1888,4 @@ unicode_new(PyTypeObject *type, PyObject *args, PyObject 
*kwargs)
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=3aa49013ffa3fa93 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=9fee62bd337f809b input=a9049054013a1b77]*/
diff --git a/Tools/clinic/libclinic/dsl_parser.py 
b/Tools/clinic/libclinic/dsl_parser.py
index 4c739efe1066e4..9e22d847c4dc90 100644
--- a/Tools/clinic/libclinic/dsl_parser.py
+++ b/Tools/clinic/libclinic/dsl_parser.py
@@ -666,6 +666,8 @@ def state_modulename_name(self, line: str) -> None:
         if equals:
             existing = existing.strip()
             if libclinic.is_legal_py_identifier(existing):
+                if self.forced_text_signature:
+                    fail("Cannot use @text_signature when cloning a function")
                 # we're cloning!
                 names = self.parse_function_names(before)
                 return self.parse_cloned_function(names, existing)
@@ -689,7 +691,8 @@ def state_modulename_name(self, line: str) -> None:
             kind=self.kind,
             coexist=self.coexist,
             critical_section=self.critical_section,
-            target_critical_section=self.target_critical_section
+            target_critical_section=self.target_critical_section,
+            forced_text_signature=self.forced_text_signature
         )
         self.add_function(func)
 
@@ -1324,13 +1327,14 @@ def state_function_docstring(self, line: str) -> None:
 
         self.docstring_append(self.function, line)
 
+    @staticmethod
     def format_docstring_signature(
-        self, f: Function, parameters: list[Parameter]
+        f: Function, parameters: list[Parameter]
     ) -> str:
         lines = []
         lines.append(f.displayname)
-        if self.forced_text_signature:
-            lines.append(self.forced_text_signature)
+        if f.forced_text_signature:
+            lines.append(f.forced_text_signature)
         elif f.kind in {GETTER, SETTER}:
             # @getter and @setter do not need signatures like a method or a 
function.
             return ''
diff --git a/Tools/clinic/libclinic/function.py 
b/Tools/clinic/libclinic/function.py
index 572916bbe123b4..93901263e44c04 100644
--- a/Tools/clinic/libclinic/function.py
+++ b/Tools/clinic/libclinic/function.py
@@ -107,6 +107,7 @@ class Function:
     # functions with optional groups because we can't represent
     # those accurately with inspect.Signature in 3.4.
     docstring_only: bool = False
+    forced_text_signature: str | None = None
     critical_section: bool = False
     target_critical_section: list[str] = dc.field(default_factory=list)
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to