Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression in type inference with constructors with type arguments #5369

Closed
adampauls opened this issue Jun 23, 2023 · 8 comments
Closed

Regression in type inference with constructors with type arguments #5369

adampauls opened this issue Jun 23, 2023 · 8 comments
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working

Comments

@adampauls
Copy link
Contributor

Describe the bug
The following code no longer typechecks as of 1.1.315:

@dataclass(frozen=True, slots=True)
    class Test(Generic[G]):
        g: G

    x: Callable[[int], Test[int]] = Test
    assert x(5).g == 5

The error is

error: Expression of type "type[Test[G@Test]]" cannot be assigned to declared type "(int) -> Test[int]"
    Type "type[Test[G@Test]]" cannot be assigned to type "(int) -> Test[int]"
      Parameter 1: type "int" cannot be assigned to type "G@Test"
        Type "int" cannot be assigned to type "G@Test" (reportGeneralTypeIssues)

The code runs fine. The following code typechecks:

@dataclass(frozen=True, slots=True)
    class Test(Generic[G]):
        g: G

    x: Callable[[int], Test[int]] = Test[int] # note the type argument here
    assert x(5).g == 5

but fails at runtime with

/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/typing.py:1250: in __call__
    result.__orig_class__ = self
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = test_xxx.<locals>.Test(g=5), name = '__orig_class__'
value = tests.anchor.test_events.test_xxx.<locals>.Test[int]

>   ???
E   TypeError: super(type, obj): obj must be an instance or subtype of type

Expected behavior
I believe the old pyright behavior was correct given the runtime behavior.

VS Code extension or command-line
Command-line version 1.1.315.

@adampauls adampauls added the bug Something isn't working label Jun 23, 2023
@adampauls
Copy link
Contributor Author

adampauls commented Jun 23, 2023

I was too hasty in following the bug. This is a minimization of my actual problem, which involves a decorator that expects a factory method to which I had been passing class names:

def publishes_events(
    event_factory: Callable[
        Concatenate[MethodCallEventType, Publisher, Output | None, Exception | None, Params],
        MethodCallEventT,
    ],
) -> SyncOrAsyncMethod[Publisher, Params, Output]:
   ...

class SyncOrAsyncMethod(Protocol[Slf, Params, Return]):
    @overload
    def __call__(self, func: Callable[Concatenate[Slf, Params], Return]) -> Callable[Concatenate[Slf, Params], Return]:
        ...

    @overload
    def __call__(
        self,
        func: Callable[Concatenate[Slf, Params], Awaitable[Return]],
    ) -> Callable[Concatenate[Slf, Params], Awaitable[Return]]:
        ...

As of 1.1.313, passing a bare classname (like @publishes_events(Test)) for a class that takes type parameters was working fine, but as of 1.1.315 it is failing. If I add the type arguments, then Pyright is happy but the decorator crashes at runtime with the same error as above.

However, it appears that the code I have above never type-checked, at least not for 1.1.312 <= <= 1.1.315. Not sure what's going on here.

@erictraut
Copy link
Collaborator

Could you please provide a minimal, self-contained code sample? The sample above references a bunch of types that are not defined within the sample. I could guess at how they are defined, but it would be better if you could provide them. I presume that some of the identifiers refer to type variables, others refer to classes.

@erictraut
Copy link
Collaborator

As for your top example, I can't explain why the runtime is generating an exception here. This strikes me as a runtime bug. If I remove the frozen=True or the slots=True, it works fine. Here's a simplified example if you care to report it as a bug against the Python runtime.

from dataclasses import dataclass
from typing import Generic, TypeVar

T = TypeVar("T")
@dataclass(frozen=True, slots=True)
class Test(Generic[T]):
    x: T

Test[int](3)

Here's the output:

Traceback (most recent call last):
  File "<redacted.py>", line 12, in <module>
    Test[int](3)
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/typing.py", line 1253, in __call__
    result.__orig_class__ = self
    ^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 5, in __setattr__
TypeError: super(type, obj): obj must be an instance or subtype of type

@erictraut erictraut added the question Further information is requested label Jun 23, 2023
@adampauls
Copy link
Contributor Author

I will try (again) to minimize. Do you believe the very first (self-contained) example should fail to typecheck, even though the type can be inferred?

@erictraut
Copy link
Collaborator

Yes, I agree that the top example should type check. It does with other generic functions, like this:

def func(x: G) -> Test[G]: ...
y: Callable[[int], Test[int]] = func

So it should work consistently when applied to a constructor call. I'll investigate further.

Is your second sample the same problem? If so, then it's unnecessary for you to provide an updated version. If you think it's different from the first problem, then I'd appreciate a complete sample so I can repro it.

erictraut pushed a commit that referenced this issue Jun 23, 2023
… assign a class to a `Callable` type when the class is generic and the constructor includes class-scoped type variables in its parameter annotations. This addresses #5369.
@erictraut erictraut removed the question Further information is requested label Jun 23, 2023
erictraut added a commit that referenced this issue Jun 23, 2023
… assign a class to a `Callable` type when the class is generic and the constructor includes class-scoped type variables in its parameter annotations. This addresses #5369. (#5372)

Co-authored-by: Eric Traut <[email protected]>
@erictraut erictraut added the addressed in next version Issue is fixed and will appear in next published version label Jun 23, 2023
@mgmira
Copy link

mgmira commented Jun 23, 2023

For the runtime error, I've encountered a similar error with python 3.11 a few months ago, and there is a related issue in the bug tracker python/cpython#90562

@adampauls
Copy link
Contributor Author

I'm not sure if the original issue and my issue are the same. There must be something else going on since the regression happened with a change in behavior for the issue you fixed, but it may be that some other change I have not identified unmasked this issue. I will investigate more after the next release and file a new issue if it still exists and I can minimize. As always, thank you for the quick responses.

@erictraut
Copy link
Collaborator

This is addressed in pyright 1.1.316, which I just published. It will also be included in this week's insiders build of pylance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addressed in next version Issue is fixed and will appear in next published version bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants