Skip to content

Commit 007e714

Browse files
committed
Fix signature generation for historical positional-only syntax
1 parent 92327e6 commit 007e714

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

mypyc/ir/func_ir.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,20 @@ def get_text_signature(fn: FuncIR) -> str | None:
412412
"""
413413
parameters = []
414414
mark_self = fn.class_name is not None and fn.decl.kind != FUNC_STATICMETHOD
415-
for arg in fn.decl.sig.args:
415+
# Pre-scan for end of positional-only parameters.
416+
# This is needed to handle signatures like 'def foo(self, __x)', where mypy
417+
# currently sees 'self' as being positional-or-keyword and '__x' as positional-only.
418+
pos_only_idx = -1
419+
for idx, arg in enumerate(fn.decl.sig.args):
420+
if arg.pos_only and arg.kind in (ArgKind.ARG_POS, ArgKind.ARG_OPT):
421+
pos_only_idx = idx
422+
for idx, arg in enumerate(fn.decl.sig.args):
416423
if arg.name.startswith("__bitmap") or arg.name == "__mypyc_self__":
417424
continue
418425
kind = (
419-
inspect.Parameter.POSITIONAL_ONLY if arg.pos_only else _ARG_KIND_TO_INSPECT[arg.kind]
426+
inspect.Parameter.POSITIONAL_ONLY
427+
if idx <= pos_only_idx
428+
else _ARG_KIND_TO_INSPECT[arg.kind]
420429
)
421430
default: object = inspect.Parameter.empty
422431
if arg.optional:
@@ -428,7 +437,7 @@ def get_text_signature(fn: FuncIR) -> str | None:
428437
curr_param = inspect.Parameter(arg.name, kind, default=default)
429438
parameters.append(curr_param)
430439
if mark_self:
431-
# Parameter.__init__ does not accept $
440+
# Parameter.__init__/Parameter.replace do not accept $
432441
curr_param._name = f"${arg.name}" # type: ignore[attr-defined]
433442
mark_self = False
434443
sig = inspect.Signature(parameters)

mypyc/test-data/run-signatures.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,25 @@ def test_methods() -> None:
123123

124124
assert getattr(Foo().f3, "__text_signature__") == "(x)"
125125
assert str(inspect.signature(Foo().f3)) == "(x)"
126+
127+
[case testSignaturesHistoricalPositionalOnly]
128+
import inspect
129+
130+
def f1(__x): pass
131+
def f2(__x, y): pass
132+
def f3(*, __y): pass
133+
def f4(x, *, __y): pass
134+
def f5(__x, *, __y): pass
135+
136+
class A:
137+
def func(self, __x): pass
138+
139+
def test_historical_positional_only() -> None:
140+
assert str(inspect.signature(f1)) == "(__x, /)"
141+
assert str(inspect.signature(f2)) == "(__x, /, y)"
142+
assert str(inspect.signature(f3)) == "(*, __y)"
143+
assert str(inspect.signature(f4)) == "(x, *, __y)"
144+
assert str(inspect.signature(f5)) == "(__x, /, *, __y)"
145+
146+
assert str(inspect.signature(A.func)) == "(self, __x, /)"
147+
assert str(inspect.signature(A().func)) == "(__x, /)"

0 commit comments

Comments
 (0)