microsoft / microsoft/pyright

Propagating ParamSpec through wrappers differ by class & function

Open
#10,660 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
15.6k
Forks
1.8k
Avg merge
12h 13m
Merged PRs (30d)
52

Description

**Describe the bug**

When using two levels of function wrappers, pyright is unable to correctly determine the function signature of wrapped function when the outer wrapper is expressed as a class. This seems to only apply when the inner wrapper (`inner` in code) calls the inner function directly. I.e in addition to the inner function, the inner wrapper also takes the same arguments as the inner function.

Expected behavior: Expressing the outer wrapper as a function or a class (`outer_func`/`outer_class`) should behave similarly when determining the wrapped function's signature. The class-based method should correctly determine the wrapped function's signature; the same/equivalent as `inner`.

**Code or Screenshots**

```python
from typing import Callable

### Outer Wrapper ###
def outer_func[**P, T](f: Callable[P, T]) -> Callable[P, T]:
def _outer_func(*args: P.args, **kwargs: P.kwargs) -> T:
return f(*args, **kwargs)
return _outer_func

class outer_class[**P, T]:
def __init__(self, f: Callable[P, T]):
self._f = f

def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
return self._f(*args, **kwargs)

### Inner Wrapper ###
def inner[**P, T](f: Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
return f(*args, **kwargs)

### Nested Wrappers ###
nested_func = outer_func(inner)
nested_class = outer_class(inner)

### Inner functions ###
def foo() -> None: print("foo")
def bar(x: int) -> int: return x

### Demo ###
inner(foo) # OK
nested_func(foo) # OK
nested_class(foo) # ERR: Arguments for ParamSpec "P@inner" are missing

assert inner(bar, 1) == 1 # OK
assert nested_func(bar, 1) == 1 # OK
assert nested_class(bar, 1) == 1 # ERR: Expected 1 positional argument
```

**VS Code extension or command-line**

VS Code Extension, ms-python.vscode-pylance (2025.6.2)

Contributor guide

Open the contributing guide

Research direction

Reproduce the supplied nested-wrapper example with Pyright through the command line or VS Code extension, comparing outer_func and outer_class diagnostics. Trace how ParamSpec signatures are inferred for the class-based __call__ path; done when nested_class accepts both foo and bar with the same inferred signature and diagnostics as nested_func.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.