Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    Ellis Percival
    @flyte
    OK, I've spent the last three solid days digging myself into a very confusing hole, but I feel like I'm close with this one. I've boiled the code down as much as I can, but it does lose a bit of the context of what I'm trying to achieve.
    I'm at the point where Pylance is pretty much on board, but mypy is not:
    from abc import ABC, abstractmethod
    from typing import Any, Dict, Generic, TYPE_CHECKING, Type, TypeVar, cast
    
    T = TypeVar("T")
    RT = TypeVar("RT")
    
    
    class Base(Generic[T, RT]):
    
        registry: Dict[Type[Any], Type[Any]] = {}
    
        def __init_subclass__(cls, return_type: Type[RT]) -> None:
            Base.registry[return_type] = cls
    
        @staticmethod
        def get(return_type: Type[Any]) -> T:
            subclass = Base.registry[return_type]
            return cast(T, subclass)
    
    
    class InterfaceA(Generic[RT], ABC):
        @property
        @abstractmethod
        def prop_a1(self) -> RT:
            pass
    
        @property
        @abstractmethod
        def prop_a2(self) -> RT:
            pass
    
    
    class ConcreteA(InterfaceA[str], Base[InterfaceA[str], str], return_type=str):
        @property
        def prop_a1(self) -> str:
            return "prop_a1"
    
        @property
        def prop_a2(self) -> str:
            return "prop_a2"
    
    
    x = Base[InterfaceA[str], str].get(str)
    
    print(x)  # <class '__main__.ConcreteA'>
    
    # "object" not callable  [operator] mypy(error)
    print(x().prop_a1)  # prop_a1
    
    # "T" has no attribute "prop_a1"  [attr-defined] mypy(error)
    print(x.prop_a1)  # <property object at 0x7fdc69f4c5e0>
    
    
    if TYPE_CHECKING:
        # Type of "x" is "InterfaceA[str]" Pylance
        # Revealed type is "T`1" mypy(note)
        reveal_type(x)
    Ellis Percival
    @flyte

    I recognise that this may seem odd, but the Base class is meant to be part of a library, whereas the code below is part of the implementation. The user implements Event classes (not shown) and NotificationTemplates (InterfaceA) which specify target types for the Notification (prop_a1, prop_a2) that differ for each NotificationTemplate (InterfaceA). The NotificationTemplates (InterfaceA) then has concrete implementations for an Email adapter or Firebase Messaging adapter (ConcreteA, etc.) which have a specific return type (RT) such as EmailMessage (str) and so on.

    When sending the notification after the event triggers, the worker checks on a user's contact preferences to see which notification adapter to use, then uses NotificationTemplate[ PostPublishedNotificationTemplate[EmailMessage], EmailMessage ].get(PostPublishedEvent, EmailNotificationAdapter) (Base[InterfaceA[str], str].get(str)) to get the concrete implementation of the notification adapter which has the correct interface for the event, and returns the right object for the notification adapter :sweat_smile:

    Honestly it's a ridiculous API, but I haven't been able to work out a way of doing this registry pattern otherwise. I'd love it if someone knows of any more sensible examples!
    Nate McMaster
    @natemcmaster

    Hi all, question about PEP 561 and mypy 0.900 -- with stubs unbundling from typeshed, have there been changes to the order in which mypy selects type information? If I install a stubs-only package types-foo and foo also has inline type hints + is marked with py.typed, what takes priority? Per my interpretation of https://www.python.org/dev/peps/pep-0561/#type-checker-module-resolution-order, I assume the stubs-only package types-foo wins

    Stub packages - these packages SHOULD supersede any installed inline package.

    2 replies
    EXPLOSION
    @A5rocks
    https://media.discordapp.net/attachments/673607501630930957/853071926527918090/unknown.png Has anyone else gotten a similar error? I somewhat dumbly enabled verbose mode after this which wiped away the logs (so I can't get text output) and can't reproduce it (after clearing my mypy cache), but I think it was some cache thing -- however, the type_guard key not being there feels like it might be a regression?
    Kyle Altendorf
    @altendky
    https://mypy-play.net/?mypy=latest&python=3.9&gist=2f8fc472d2b8f5a1d728140034a24518 raising an integer to a non-literal integer power seems to turn into Any. is this supposed to be this way for some reason?
    3 replies
    Ellis Percival
    @flyte
    I've boiled my previous question down a bit more. Is this a mypy bug?
    from typing import Generic, Protocol, TypeVar, cast
    
    T = TypeVar("T")
    RT_co = TypeVar("RT_co", covariant=True)
    
    class HasMyprop(Protocol[RT_co]):
        @property
        def myprop(self) -> RT_co:
            ...
    
    class Concrete(HasMyprop[str]):
        @property
        def myprop(self) -> str:
            return "beans"
    
    class MyPropFactory(Generic[T]):
        @staticmethod
        def get() -> T:
            return cast(T, Concrete())
    
    my_instance = MyPropFactory[HasMyprop[str]].get()
    
    # pylance: (variable) my_instance: HasMyprop[str]
    # mypy: "T" has no attribute "myprop"  [attr-defined] mypy(error)
    my_instance.myprop  # pylance: (property) myprop: str
    pylance has it "right" here, and indeed if you print my_instance.myprop it outputs "beans"
    Ellis Percival
    @flyte
    mypy doesn't seem to be resolving T to the type passed in to the MyPropFactory[HasMyprop[str]].get() call
    Ellis Percival
    @flyte
    ok, boiled down further..
    from typing import Generic, TypeVar, cast
    
    T = TypeVar("T")
    
    class MyClass(Generic[T]):
        @staticmethod
        def get() -> T:
            return cast(T, object())
    
    x: str = MyClass[str].get()
    # Incompatible types in assignment (expression has type "T", variable has type "str")  [assignment]mypy(error)
    2 replies
    Ellis Percival
    @flyte
    really striking out on this one..
    Does someone maybe have any ideas/examples/disparagements about how one could keep a registry of generic Protocol implementations and then at runtime retrieve the implementation that returns a given type from its methods/properties? Something like this, but without the ambiguous generic class reference:
    from typing import Any, Dict, Generic, Protocol, Type, TypeVar
    
    RT = TypeVar("RT")
    
    class MyProtocol(Protocol[RT]):
        implementations: Dict[Type[Any], Type["MyProtocol[RT]"]] = {}
    
        def run(self) -> RT:
            ...
    
        def __init_subclass__(cls, return_type: Type[Any]) -> None:
            # Access to generic instance variables via class is ambiguous  [misc]mypy(error)
            MyProtocol.implementations[return_type] = cls
    
    class MyImplementation(MyProtocol[str], return_type=str):
        def run(self) -> str:
            return "beans"
    
    x: str = MyProtocol.implementations[str]().run()
    Ellis Percival
    @flyte
    this might be the first time I've ever posted something wrong on the internet and nobody's tried to correct me :sweat_smile:
    Kyle Altendorf
    @altendky
    python-qt-tools/PyQt5-stubs#155 having trouble with pycharm accepting my pyqt5-stubs hints for signals. any ideas other than 'submit a pycharm issue'?
    Shantanu
    @hauntsaninja

    @flyte the following seems to work for me

    from typing import Any, Dict, Generic, Protocol, Type, TypeVar
    
    T = TypeVar("T")
    RT = TypeVar("RT", covariant=True)
    
    class MyProtocol(Protocol[RT]):
        @staticmethod
        def get_implementation(x: Type[T]) -> MyProtocol[T]:
            ...
    
        def run(self) -> RT:
            ...
    
    reveal_type(MyProtocol.get_implementation(str).run())

    if you really need implementations to be a dict, you can create something with the appropriately typed __getitem__

    1 reply
    I didn't mess around with the __init_subclass__ implementaiton but I figure if you run into issues there a type ignore isn't too bad since it doesn't affect users of your abstraction
    Alex Grönholm
    @agronholm
    mypy seems to return 0 as an exit code even when errors are detected – is everybody else seeing this too?
    Ellis Percival
    @flyte
    just checked 0.901 and 0.902 and I get a 1 on errors
    Alex Grönholm
    @agronholm
    I get 0 on 0.902
    Ellis Percival
    @flyte
    $ mypy --strict test2.py 
    test2.py:107: error: "object" not callable
    test2.py:111: error: "TemplateT" has no attribute "post_author"
    Found 2 errors in 1 file (checked 1 source file)
    flyte@demosthenes:~/dev/mything
    $ echo $?
    1
    flyte@demosthenes:~/dev/mything
    $ mypy --version
    mypy 0.902
    (same without --strict)
    Alex Grönholm
    @agronholm
    hmm...this seems to have something to do with --install-types
    if I add that, I get a 0 even when there are errors
    Ellis Percival
    @flyte
    flyte@demosthenes:~/dev/mything
    $ mypy --install-types test2.py 
    test2.py:107: error: "object" not callable
    test2.py:111: error: "TemplateT" has no attribute "post_author"
    Found 2 errors in 1 file (checked 1 source file)
    flyte@demosthenes:~/dev/mything
    $ echo $?
    0
    confirmed
    Alex Grönholm
    @agronholm
    I'll post a ticket on the issue tracker I guess
    I'll check with the latest dev version first
    Ellis Percival
    @flyte
    affects 0.900 too, but <=0.812 didn't have --install-types
    Alex Grönholm
    @agronholm
    yup, it's broken even on master
    Pyprohly
    @Pyprohly
    Why can’t the type arguments for f(b) be inferred here?
    from typing import TypeVar, Generic, Sequence
    
    T = TypeVar('T')
    
    class Container(Generic[T]):
        def __init__(self, a: T) -> None:
            self.a = a
    
    def f(a: Container[Sequence[T]]) -> None:
        pass
    
    '''
    l: Sequence[int] = [1, 2, 3]
    b = Container(l)
    '''
    b = Container([1, 2, 3])
    '''#'''
    f(b)
    Marti Raudsepp
    @intgr

    @Pyprohly You need to make T typevar covariant for that to work

    https://mypy.readthedocs.io/en/stable/common_issues.html#covariant-subtyping-of-mutable-protocol-members-is-rejected

    T = TypeVar('T')
    T_co = TypeVar('T_co', covariant=True)
    
    class Container(Generic[T_co]):
        def __init__(self, a: T_co) -> None:
            self.a = a
    
    def f(a: Container[Sequence[T]]) -> None:
        pass
    
    b = Container([1, 2, 3])
    reveal_type(b)
    f(b)
    1 reply
    Actually that link doesn't describe the situation very well :)
    Evin Sellin
    @evinism
    Hey, friends! I'm considering opening up a pull to create a new config to disable simply the "found module but no type hints or library stubs" that seems to plague a number of repos. I assume this is a common source of discussion, but i'm having trouble finding issues describing it -- is there any history I should be aware of before submitting a patch, beyond just the contributing doc?
    considering --ignore-missing-type-hints for such a flag
    (this is for mypy)
    Evin Sellin
    @evinism
    oh, nvm, found python/mypy#4542
    Froger David
    @dfroger

    Hello!

    I have an abstract base class. In an abstract method, how can I express that one of the argument must be a subclass of a a base class?

    from dataclasses import dataclass
    from abc import ABC, abstractmethod
    
    
    @dataclass
    class ItemBase:
        a: int
    
    
    @dataclass
    class Item1(ItemBase):
        b: int
    
    
    class Base(ABC):
        @abstractmethod
        def meth(self, x: ItemBase) -> None:
            print(x.a)
    
    
    class Derived(Base):
        def meth(self, x: Item1) -> None:
            print(x.a, x.b)

    For example, I would like to ensure that argument x of Base.meth must be a subclass of ItemBase.

    Thanks!

    Froger David
    @dfroger
    Just see that mypy output a note with a doc link... (my editor only show me the error line...)
    the doc is https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides , maybe I'll try generic type for ItemBase instead
    xaltsc
    @xaltsc
    Hey, is this the place to ask concrete (i.e. I have a problem typing my existing code) questions here ?
    Pyprohly
    @Pyprohly
    Go for it
    xaltsc
    @xaltsc
    I've seen that I can set an upper bound for a type variable, however, in my case, I want that upper bound to not be a maximum: i.e. the class that I use as the upper bound is abstract so mypy says that trying to, say, call methods that are defined as abstract in the class, is an error.
    xaltsc
    @xaltsc
    Also, I'm running into a problem where I pass a class as an argument to a method, and I run into the error described here https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases (def fun2...)
    The type of the said variable being Type[T] where T is a bound TypeVar
    graingert
    @graingert:matrix.org
    [m]
    @xaltsc show your code?
    Maybe make a mypy-play.net
    xaltsc
    @xaltsc
    graingert, Pyprohly: here's the code https://pastebin.com/Swx1KCst, for more context (but the repo is not up to date as I've only recently began typing my code), you can check at https://hg.sr.ht/~xaltsc/xettel
    Sorry for the delay btw
    xaltsc
    @xaltsc
    the file I've provided on pastebin is at xettel.base.Zettelkasten
    Tomas Aschan
    @tomasaschan

    i'm seeing the exact same behavior as python/mypy#10273, and i've tried the recommendations in https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules but i'm not getting it to work.

    basically, i have a file src/tasks/foo.py and a file tests/tasks/test_foo.py, where test_foo.py contains import tasks.foo, and i get the error (on the import statement) if i run mypy tests/tasks/test_foo.py, but not if i run mypy src/tasks/foo.py tests/tasks/test_foo.py.

    since the editor integration runs mypy on one file at a time, i don't understand how to make it understand that the types for bar.py are available. what do i do to tell mypy that bar.py exports types, when running mypy foo.py?

    Henry Schreiner
    @henryiii
    MyPy seems like it should be able to deduce the type of os.environ if some_bool else {}, but it fails, and just makes it object. Pyright correctly deduces it to be Union[_Environ, dict], which then is acceped in a place that requires a Mapping. See https://github.com/henryiii/cibuildwheel/pull/2#discussion_r654112955