Skip to content

Pyright fails to recognize valid calls when ParamSpec, Concatenate, and factories are used #3559

@lexicalunit

Description

@lexicalunit

Describe the bug
Contrived code but in the real version this makes sense as we're working with pytest and using the factories-as-fixtures pattern.

See the inline comments on the three calls to bulk_maker() for the pyright errors:

from typing import Any, Callable, List

# For python 3.8:
from typing_extensions import Concatenate, ParamSpec, TypeAlias

# For python 3.10:
# from typing import Concatenate, ParamSpec, TypeAlias


class Company:
    id: int

    def __init__(self, id: int):
        self.id = id
        print(self)

    def __repr__(self) -> str:
        return f"<company {self.id}>"


Factory: TypeAlias = Callable[..., Company]


def make_company(*args: Any, **kwargs: Any) -> Company:
    return Company(*args, **kwargs)


P = ParamSpec("P")
BulkFactory: TypeAlias = Callable[Concatenate[int, P], List[Company]]


def make_n_companies() -> BulkFactory[P]:
    def factory(n: int, *args: P.args, **kwargs: P.kwargs) -> List[Company]:
        return [make_company(*args, **kwargs) for _ in range(n)]

    return factory


bulk_maker = make_n_companies()

print()
bulk_maker(5, 10)  # Expected 1 positional argument

print()
bulk_maker(5, id=10)  # No parameter named "id"

print()
bulk_maker(5)  # Arguments for ParamSpec "P@make_n_companies" are missing

The first two calls to bulk_maker() are fine. The last one is incorrect and will fail at runtime:

± python example.py

<company 10>
<company 10>
<company 10>
<company 10>
<company 10>

<company 10>
<company 10>
<company 10>
<company 10>
<company 10>

Traceback (most recent call last):
  File "foo.py", line 47, in <module>
    bulk_maker(5)  # Arguments for ParamSpec "P@make_n_companies" are missing
  File "foo.py", line 33, in factory
    return [make_company(*args, **kwargs) for _ in range(n)]
  File "foo.py", line 33, in <listcomp>
    return [make_company(*args, **kwargs) for _ in range(n)]
  File "foo.py", line 24, in make_company
    return Company(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'id'

Expected behavior
There should not be any pyright errors for the first two calls to bulk_maker().

VS Code extension or command-line

  • Are you running pyright as a VS Code extension or a command-line tool? Both
  • Which version? pyright 1.1.253 and **pylance v2022.6.10`
  • Error is seen when using python 3.8 and 3.10. No difference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    as designedNot a bug, working as intended

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions