-
Notifications
You must be signed in to change notification settings - Fork 10
Description
Currently, in the PySide6 stub files (.pyi), all function parameters are marked as normal positional-or-keyword arguments. In practice, however, non-default arguments cannot be passed by keyword at all – doing so always raises runtime errors like "no matching signature" or "not enough arguments".
This behavior is consistent with PYSIDE-1964, where it is explained that C++ does not support keyword arguments, and only default parameters can be used that way. The .pyi stubs are therefore misleading for both type checkers and developers.
A proposed solution would be to update all stubs so that parameters without default values are explicitly marked as positional-only using the Python 3.8+ / syntax. I have written a LibCST-based script that can automatically refactor the stubs accordingly, while preserving formatting and comments.
This would make the stubs consistent with actual PySide6 behavior and prevent confusing errors when users try to call functions with keyword arguments that aren’t supported.
import sys
import libcst as cst
from pathlib import Path
class AddPosOnlyTransformer(cst.CSTTransformer):
def leave_FunctionDef(self, original: cst.FunctionDef, updated: cst.FunctionDef) -> cst.FunctionDef:
p: cst.Parameters = updated.params
if hasattr(p, "posonly_params") and len(p.posonly_params) > 0:
return updated
if not p.params:
return updated
last_required_idx = -1
for i, prm in enumerate(p.params):
if isinstance(prm, cst.Param) and prm.default is None:
last_required_idx = i
if last_required_idx == -1:
return updated
leading_required = list(p.params[: last_required_idx + 1])
remaining_params = list(p.params[last_required_idx + 1 :])
new_params = p.with_changes(
posonly_params=list(p.posonly_params) + leading_required,
params=remaining_params,
)
return updated.with_changes(params=new_params)
def process_file(infile: str, outfile: str | None = None) -> None:
with open(infile, encoding="utf8") as f:
src = f.read()
mod = cst.parse_module(src)
new_mod = mod.visit(AddPosOnlyTransformer())
new_code = new_mod.code
if outfile is None:
outfile = infile
with open(outfile, "w", encoding="utf8") as f:
f.write(new_code)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python make_positional.py input.pyi [output.pyi]")
sys.exit(1)
infile = Path(sys.argv[1])
outfile = Path(sys.argv[2]) if len(sys.argv) > 2 else infile
process_file(infile, outfile)