pulumi icon indicating copy to clipboard operation
pulumi copied to clipboard

classmethods on pulumi.Output are no longer typechecking successfully in python with pyright>=1.1.354

Open doy-materialize opened this issue 1 year ago • 1 comments

What happened?

we use a lot of methods like pulumi.Output.from_input, but when trying to update the version of pyright we use, we are now getting type errors

Example

with a pulumi python project that looks like this:

# pyright: strict

import pulumi

output = pulumi.Output.from_input("foo")

this used to produce no errors, but now fails with:

/home/doy/tmp/pulumi-pyright/__main__.py
  /home/doy/tmp/pulumi-pyright/__main__.py:5:1 - error: Type of "output" is partially unknown
    Type of "output" is "Output[Unknown]" (reportUnknownVariableType)
  /home/doy/tmp/pulumi-pyright/__main__.py:5:10 - error: Type of "from_input" is partially unknown
    Type of "from_input" is "(val: Unknown | Awaitable[Unknown] | Output[Unknown]) -> Output[Unknown]" (reportUnknownMemberType)
2 errors, 0 warnings, 0 informations

Output of pulumi about

CLI          
Version      3.112.0
Go Version   go1.22.1
Go Compiler  gc

Plugins
NAME    VERSION
python  unknown

Host     
OS       arch
Version  
Arch     x86_64

This project is written in python: executable='/home/doy/tmp/pulumi-pyright/venv/bin/python3' version='3.11.8'

Current Stack: doy-materialize-com/pulumi-pyright/dev

Found no resources associated with dev

Found no pending operations associated with dev

Backend        
Name           pulumi.com
URL            https://app.pulumi.com/doy-materialize-com
User           doy-materialize-com
Organizations  doy-materialize-com, materialize
Token type     personal

Dependencies:
NAME        VERSION
pip         24.0
pulumi      3.112.0
setuptools  69.2.0
wheel       0.43.0

Pulumi locates its logs in /tmp by default

Additional context

this appears likely to be related to this changelog entry from pyright 1.1.354:

Changed the behavior when accessing a class attribute from a generic class that is not specialized. The class is now automatically specialized in the case using default type parameter values (from PEP 696) or Unknown. This change is required for conformance with PEP 696.

Contributing

Vote on this issue by adding a 👍 reaction. To contribute a fix for this issue, leave a comment (and link to your pull request, if you've opened one already).

doy-materialize avatar Apr 11 '24 16:04 doy-materialize

Thanks for reporting this!

Trying out a minimal repro in Pyright Playground:

# pyright: strict

from typing import (
    TypeVar,
    Generic,
    Union,
    Awaitable,
    cast,
)

T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)
U_co = TypeVar("U_co", covariant=True)

Input = Union[T, Awaitable[T], "Output[T]"]

class Output(Generic[T_co]):
    def __init__(self, value: T_co):
        self.value = value

    @staticmethod
    def from_input_old(val: Input[T_co]) -> "Output[T_co]":
        return Output(cast(T_co, val))

    @staticmethod
    def from_input_new(val: Input[U_co]) -> "Output[U_co]":
        return Output(cast(U_co, val))

output1 = Output.from_input_old("foo")

output2 = Output.from_input_new("foo")

I can repro the current issue, with output1 = Output.from_input_old("foo"):

Type of "from_input_old" is partially unknown
  Type of "from_input_old" is "(val: Unknown | Awaitable[Unknown] | Output[Unknown]) -> Output[Unknown]"  (reportUnknownMemberType)
Type of "output1" is partially unknown
  Type of "output1" is "Output[Unknown]"  (reportUnknownVariableType)

However, if we use a new TypeVar (e.g. use U_co rather than T_co in from_input_new), then we don't get any reported issues from pyright.

The latest mypy doesn't complain on either of these.

I'm not sure if that's the recommended approach to typing a static factory method on a generic class, but seems to address the problem. We should check all the other static methods on Output to see if they have the same problem and fix in the same way.

justinvp avatar Apr 15 '24 05:04 justinvp