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

This commit is contained in:
Erlend E. Aasland 2024-04-10 10:12:05 +02:00 committed by GitHub
parent 73906d5c90
commit 0d42ac9474
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 70 additions and 10 deletions

View File

@ -5,7 +5,7 @@
from functools import partial from functools import partial
from test import support, test_tools from test import support, test_tools
from test.support import os_helper 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 textwrap import dedent
from unittest import TestCase from unittest import TestCase
import inspect import inspect
@ -662,6 +662,61 @@ class ClinicWholeFileTest(TestCase):
err = "Illegal C basename: '.illegal.'" err = "Illegal C basename: '.illegal.'"
self.expect_failure(block, err, lineno=7) 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): class ParseFileUnitTest(TestCase):
def expect_parsing_failure( def expect_parsing_failure(

View File

@ -357,7 +357,7 @@ exit:
} }
PyDoc_STRVAR(unicode_find__doc__, PyDoc_STRVAR(unicode_find__doc__,
"find($self, sub, start=None, end=None, /)\n" "find($self, sub[, start[, end]], /)\n"
"--\n" "--\n"
"\n" "\n"
"Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\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 @@ exit:
} }
PyDoc_STRVAR(unicode_index__doc__, PyDoc_STRVAR(unicode_index__doc__,
"index($self, sub, start=None, end=None, /)\n" "index($self, sub[, start[, end]], /)\n"
"--\n" "--\n"
"\n" "\n"
"Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\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 @@ exit:
} }
PyDoc_STRVAR(unicode_rfind__doc__, PyDoc_STRVAR(unicode_rfind__doc__,
"rfind($self, sub, start=None, end=None, /)\n" "rfind($self, sub[, start[, end]], /)\n"
"--\n" "--\n"
"\n" "\n"
"Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\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 @@ exit:
} }
PyDoc_STRVAR(unicode_rindex__doc__, PyDoc_STRVAR(unicode_rindex__doc__,
"rindex($self, sub, start=None, end=None, /)\n" "rindex($self, sub[, start[, end]], /)\n"
"--\n" "--\n"
"\n" "\n"
"Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\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 @@ skip_optional_pos:
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=3aa49013ffa3fa93 input=a9049054013a1b77]*/ /*[clinic end generated code: output=9fee62bd337f809b input=a9049054013a1b77]*/

View File

@ -666,6 +666,8 @@ class DSLParser:
if equals: if equals:
existing = existing.strip() existing = existing.strip()
if libclinic.is_legal_py_identifier(existing): if libclinic.is_legal_py_identifier(existing):
if self.forced_text_signature:
fail("Cannot use @text_signature when cloning a function")
# we're cloning! # we're cloning!
names = self.parse_function_names(before) names = self.parse_function_names(before)
return self.parse_cloned_function(names, existing) return self.parse_cloned_function(names, existing)
@ -689,7 +691,8 @@ class DSLParser:
kind=self.kind, kind=self.kind,
coexist=self.coexist, coexist=self.coexist,
critical_section=self.critical_section, 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) self.add_function(func)
@ -1324,13 +1327,14 @@ class DSLParser:
self.docstring_append(self.function, line) self.docstring_append(self.function, line)
@staticmethod
def format_docstring_signature( def format_docstring_signature(
self, f: Function, parameters: list[Parameter] f: Function, parameters: list[Parameter]
) -> str: ) -> str:
lines = [] lines = []
lines.append(f.displayname) lines.append(f.displayname)
if self.forced_text_signature: if f.forced_text_signature:
lines.append(self.forced_text_signature) lines.append(f.forced_text_signature)
elif f.kind in {GETTER, SETTER}: elif f.kind in {GETTER, SETTER}:
# @getter and @setter do not need signatures like a method or a function. # @getter and @setter do not need signatures like a method or a function.
return '' return ''

View File

@ -107,6 +107,7 @@ class Function:
# functions with optional groups because we can't represent # functions with optional groups because we can't represent
# those accurately with inspect.Signature in 3.4. # those accurately with inspect.Signature in 3.4.
docstring_only: bool = False docstring_only: bool = False
forced_text_signature: str | None = None
critical_section: bool = False critical_section: bool = False
target_critical_section: list[str] = dc.field(default_factory=list) target_critical_section: list[str] = dc.field(default_factory=list)