mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
[3.11] gh-86457: Fix signature for code.replace() (GH-23199) (GH-107746)
Also add support of @text_signature in Argument Clinic.
(cherry picked from commit 0e6e32fb84)
This commit is contained in:
parent
0aa3b9d76c
commit
edaa0db93e
4 changed files with 173 additions and 168 deletions
|
|
@ -4072,6 +4072,7 @@ def reset(self):
|
|||
self.indent = IndentStack()
|
||||
self.kind = CALLABLE
|
||||
self.coexist = False
|
||||
self.forced_text_signature: str | None = None
|
||||
self.parameter_continuation = ''
|
||||
self.preserve_output = False
|
||||
|
||||
|
|
@ -4201,6 +4202,11 @@ def at_coexist(self):
|
|||
fail("Called @coexist twice!")
|
||||
self.coexist = True
|
||||
|
||||
def at_text_signature(self, text_signature):
|
||||
if self.forced_text_signature:
|
||||
fail("Called @text_signature twice!")
|
||||
self.forced_text_signature = text_signature
|
||||
|
||||
def parse(self, block):
|
||||
self.reset()
|
||||
self.block = block
|
||||
|
|
@ -4903,142 +4909,145 @@ def format_docstring(self):
|
|||
add(f.cls.name)
|
||||
else:
|
||||
add(f.name)
|
||||
add('(')
|
||||
if self.forced_text_signature:
|
||||
add(self.forced_text_signature)
|
||||
else:
|
||||
add('(')
|
||||
|
||||
# populate "right_bracket_count" field for every parameter
|
||||
assert parameters, "We should always have a self parameter. " + repr(f)
|
||||
assert isinstance(parameters[0].converter, self_converter)
|
||||
# self is always positional-only.
|
||||
assert parameters[0].is_positional_only()
|
||||
parameters[0].right_bracket_count = 0
|
||||
positional_only = True
|
||||
for p in parameters[1:]:
|
||||
if not p.is_positional_only():
|
||||
positional_only = False
|
||||
else:
|
||||
assert positional_only
|
||||
if positional_only:
|
||||
p.right_bracket_count = abs(p.group)
|
||||
else:
|
||||
# don't put any right brackets around non-positional-only parameters, ever.
|
||||
p.right_bracket_count = 0
|
||||
# populate "right_bracket_count" field for every parameter
|
||||
assert parameters, "We should always have a self parameter. " + repr(f)
|
||||
assert isinstance(parameters[0].converter, self_converter)
|
||||
# self is always positional-only.
|
||||
assert parameters[0].is_positional_only()
|
||||
parameters[0].right_bracket_count = 0
|
||||
positional_only = True
|
||||
for p in parameters[1:]:
|
||||
if not p.is_positional_only():
|
||||
positional_only = False
|
||||
else:
|
||||
assert positional_only
|
||||
if positional_only:
|
||||
p.right_bracket_count = abs(p.group)
|
||||
else:
|
||||
# don't put any right brackets around non-positional-only parameters, ever.
|
||||
p.right_bracket_count = 0
|
||||
|
||||
right_bracket_count = 0
|
||||
right_bracket_count = 0
|
||||
|
||||
def fix_right_bracket_count(desired):
|
||||
nonlocal right_bracket_count
|
||||
s = ''
|
||||
while right_bracket_count < desired:
|
||||
s += '['
|
||||
right_bracket_count += 1
|
||||
while right_bracket_count > desired:
|
||||
s += ']'
|
||||
right_bracket_count -= 1
|
||||
return s
|
||||
def fix_right_bracket_count(desired):
|
||||
nonlocal right_bracket_count
|
||||
s = ''
|
||||
while right_bracket_count < desired:
|
||||
s += '['
|
||||
right_bracket_count += 1
|
||||
while right_bracket_count > desired:
|
||||
s += ']'
|
||||
right_bracket_count -= 1
|
||||
return s
|
||||
|
||||
need_slash = False
|
||||
added_slash = False
|
||||
need_a_trailing_slash = False
|
||||
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):
|
||||
# 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
|
||||
|
||||
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_a_trailing_slash = True
|
||||
break
|
||||
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
|
||||
add_parameter('*,')
|
||||
|
||||
added_star = False
|
||||
p_add, p_output = text_accumulator()
|
||||
p_add(fix_right_bracket_count(p.right_bracket_count))
|
||||
|
||||
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)
|
||||
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 construct a new instance.
|
||||
p_add('$')
|
||||
|
||||
for p in parameters:
|
||||
if not p.converter.show_in_signature:
|
||||
continue
|
||||
assert p.name
|
||||
if p.is_vararg():
|
||||
p_add("*")
|
||||
|
||||
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
|
||||
name = p.converter.signature_name or p.name
|
||||
p_add(name)
|
||||
|
||||
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 not p.is_vararg() and p.converter.is_optional():
|
||||
p_add('=')
|
||||
value = p.converter.py_default
|
||||
if not value:
|
||||
value = repr(p.converter.default)
|
||||
p_add(value)
|
||||
|
||||
if p.is_keyword_only() and not added_star:
|
||||
added_star = True
|
||||
add_parameter('*,')
|
||||
if (p != last_p) or need_a_trailing_slash:
|
||||
p_add(',')
|
||||
|
||||
p_add, p_output = text_accumulator()
|
||||
p_add(fix_right_bracket_count(p.right_bracket_count))
|
||||
add_parameter(p_output())
|
||||
|
||||
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 construct a new instance.
|
||||
p_add('$')
|
||||
|
||||
if p.is_vararg():
|
||||
p_add("*")
|
||||
|
||||
name = p.converter.signature_name or p.name
|
||||
p_add(name)
|
||||
|
||||
if not p.is_vararg() and p.converter.is_optional():
|
||||
p_add('=')
|
||||
value = p.converter.py_default
|
||||
if not value:
|
||||
value = repr(p.converter.default)
|
||||
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(')')
|
||||
add(fix_right_bracket_count(0))
|
||||
if need_a_trailing_slash:
|
||||
add_parameter('/')
|
||||
add(')')
|
||||
|
||||
# PEP 8 says:
|
||||
#
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue