by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
    jack1142
    @jack1142
    How should I type hint a __class_getitem__ method?

    I currently have:

    _T_OPT = TypeVar("_T_OPT", bound=Type)
    
    class UserInputOptional(Generic[_T_OPT]):
        def __class_getitem__(cls, key: _T_OPT) -> _T_OPT:
            if isinstance(key, tuple):
                raise TypeError("Must only provide a single type to Optional")
            return key

    which is not valid for usage like (or any other generic alias): UserInputOptional[Union[int, str]]

    jack1142
    @jack1142
    well, for that matter, it doesn't seem to be working with mypy even for usage like UserInputOptional[str]
    jack1142
    @jack1142
    In worst case, I can probably just use _T = TypeVar("_T"), but maybe there's something better.
    jack1142
    @jack1142
    After trying this a bit more, it seems that mypy doesn't use __class_getitem__ for figuring out type annotations, so it seems like I will have to settle for making this an alias of Union, even if it may result in unwanted multiple arguments. Only other alternative I see is writing mypy plugin and that seems a bit much.
    Joshua Bronson
    @jab
    Hi all, I just hit a typing related bug in Python 3.6 while trying to type hint my bidirectional mapping library (https://bidict.rtfd.io) and reported it here: https://bugs.python.org/issue41451
    If anyone familiar with the killer combination of weak references, slots, and multiple inheritance from generic types can suggest a workaround, I, as well as many bidict users hoping to get a version with type hints, would appreciate it. Thanks!
    Joshua Oreman
    @oremanj
    @jab looks like you can work around this by deleting the __slots__ member after the class is constructed
    (this does not affect the slottedness of the class)
    >>> T = TypeVar("T")
    >>> class Base(Generic[T]):
    ...   __slots__ = ("__weakref__",)
    ...
    >>> Base[T]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/local/lib/python3.6/typing.py", line 682, in inner
        return func(*args, **kwds)
      File "/usr/local/lib/python3.6/typing.py", line 1143, in __getitem__
        orig_bases=self.__orig_bases__)
      File "/usr/local/lib/python3.6/typing.py", line 978, in __new__
        self = super().__new__(cls, name, bases, namespace, _root=True)
      File "/usr/local/lib/python3.6/typing.py", line 137, in __new__
        return super().__new__(cls, name, bases, namespace)
      File "/usr/local/lib/python3.6/abc.py", line 133, in __new__
        cls = super().__new__(mcls, name, bases, namespace, **kwargs)
    TypeError: __weakref__ slot disallowed: either we already got one, or __itemsize__ != 0
    >>> del Base.__slots__
    >>> Base[T]
    __main__.Base[~T]
    >>> class Derived(Base[T]):
    ...   __slots__ = ()
    ...
    >>> Derived()
    <__main__.Derived object at 0x7fcb99be7888>
    >>> Derived().__dict__
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Derived' object has no attribute '__dict__'
    >>>
    Alex Grönholm
    @agronholm
    I think I've found a bug in mypy, could someone confirm?
    from typing import Generic, AsyncContextManager, TypeVar
    
    T_Item = TypeVar('T_Item', str, int)
    
    class Foo(Generic[T_Item]):
        async def foo(self, manager: AsyncContextManager):
            async with manager:
                pass
    Result:
    bug.py:7: error: "AsyncContextManager[Any]" has no attribute "__enter__"; maybe "__aenter__"?
    bug.py:7: error: "AsyncContextManager[Any]" has no attribute "__exit__"; maybe "__aexit__"?
    this doesn't happen if you remove the constraints from T_Item
    no clue why they affect this
    Joshua Bronson
    @jab

    Hi all, I submitted a PR adding type hints to bidict, my bidirectional mapping library: jab/bidict#112
    As I commented in the PR:
    """
    Doesn't have to block merging, but there are a few places where I used # type: ignore internally that may actually be worth type checking. Maybe. (In many of these places though I suspect appeasing mypy would not be worth it, e.g. namedbidict and _invcls.) Open to suggestions.

    One thing I had trouble with was using @typing.overload with __init__() and update(). For example, mypy rejects:

        @_t.overload
        def update(self, **kw: VT) -> None: ...
        @_t.overload
        def update(self, map: _t.Mapping[KT, VT], **kw: VT) -> None: ...
        @_t.overload
        def update(self, col: IterItems[KT, VT], **kw: VT) -> None: ...
        def update(self, *args: MapOrIterItems[KT, VT], **kw: VT) -> None:
            # (implementation here)

    I think this may be a current limitation in mypy, so I left those without the overloads. If someone wants to suggest an alternative, I'll be happy to take another look.
    """

    Would much appreciate another pair of eyes on this if anyone is interested. bidict poses some particularly interesting type hinting challenges, so maybe several would find it interesting to review. (For example, I hit https://bugs.python.org/issue41451 and https://github.com/python/typing/issues/548#issuecomment-621571821 while working on this.)

    Maybe the best way to review is to start at https://github.com/jab/bidict/blob/dev/bidict/_abc.py and then follow the suggested code review path in the header comments, focusing on the type hints. Please comment (or just react to the PR with :eyes:) if you're interested in reviewing, and I'll wait for feedback before I merge.

    Thanks in advance to anyone who can help this lonely solo-maintainer make bidict's first type hints as good as can be! :heart:

    ishaan
    @pikachu
    heyo! @hauntsaninja was super kind to approve this pr python/mypy#9203, but he said he does not have merging permissions. what's the process for moving this forward?
    Madhura Bhogate
    @madhurabhogate

    Hi all,

    I want to define a Callable
    my function looks like -

    def foo1(read: ReadClient, name: str)  -> Any:
    
    def foo2(read: ReadClient, name: str, count: int)  -> Any:
    
    def too1(write: WriteClient, name: str)  -> Any:
    
    def too2(read: WriteClient, name: str, count: int)  -> Any:

    The function call will have the 1st positional argument as ReadClient or WriteClient and it can variable args, kwargs.
    I tried something like

    Callable[[ReadClient,  Any, ...], Any]
    Callable[[ReadClient,  ...], Any]

    but none of the above formats work

    Hence I want to define Callable somewhat like -

    Callable[[ReadClient,  *args: Any, **kwargs: Any], Any]
    
    Callable[[WriteClient,  *args: Any, **kwargs: Any], Any]

    I know the above format is incorrect, but is there a way to define a Callable where the first positional argument is ReadClient and it could take variable args/kwargs of Any type. Same would be true for WriteClient.

    I tried using Protocol as -

    class ReadCmdFunc(Protocol):
        def __call__(self, device: ReadClient, *args: Any, **kwargs: Any) -> Any: ...
    
    class WriteCmdFunc(Protocol):
        def __call__(self, device: WriteClient, *args: Any, **kwargs: Any) -> Any: ...

    HOwever, mypy throws an error as -
    Argument 1 has incompatible type "Callable[[WriteClient, str,], Any]"; expected "WriteCmdFunc"
    but my WriteCmdFunc is defined exactly as in the __call__ of WriteCmdFunc.

    So not sure, what I am missing here.

    I really appreciate your help.

    Joshua Oreman
    @oremanj
    @madhurabhogate too1 is not a subtype of WriteCmdFunc because a WriteCmdFunc can take any arguments while too1 takes at most one extra str
    8 replies
    I'm guessing you want to use this in something that takes the callable and also its arguments? otherwise I don't see how one could safely call any of your example concrete functions knowing only the type of their first argument
    I think you want ParamSpec from PEP 612, but mypy doesn't support it yet.
    Nikita Sobolev
    @sobolevn
    I have made a small fix to mypy to solve a problem with nested overloades in our own plugin dry-python/returns/@curry: python/mypy#9259 I would love to hear the feedback on it! :+1:
    Joshua Oreman
    @oremanj
    @madhurabhogate because the definition of WriteCmdFunc says you can pass anything after an initial WriteClient argument, but the “implementations” expect particular specific types there
    so you can write lots of calls that would be valid on the WriteCmdFunc protocol but don’t work on the “implementations”
    therefore the “implementations” aren’t actually subtypes of the protocol, which is why I’ve been putting the word in quotes
    Madhura Bhogate
    @madhurabhogate

    I get a certain errror

    error: "Callable[..., Any]" has no attribute "for_platform"  [attr-defined]

    This error could be in any line in a file.
    Is there a way to ignore this error from all the files using mypy config file where I say, if I get the above error in any files, just ignore it instead of having #type: ignore [attr-defined] in each file ?

    Joshua Oreman
    @oremanj
    you don't want to ignore all attribute definition errors, surely?
    just the ones on functions. there's not a good way to do that afaik
    (mypy doesn't support assigning arbitrary attributes on functions currently, because it doesn't distinguish functions from other callables)
    Max Fischer
    @maxfischer2781

    Hi folks, trying to understand this result and how to fix it:

    T = TypeVar('T')
    
    def f(a: T, b: T) -> T: ...
    
    reveal_type(f(1, '2'))  # builtins.object*

    I assumed T to be invariant and thus either int or str. It seems that variance is taken from the function (contravariant arguments, covariant return) and T's variance being ignored.
    Can I force T to be invariant here?

    Andre Delfino
    @andresdelfino
    T = TypeVar('T') # Can be anything
    A = TypeVar('A', str, bytes) # Must be str or bytes
    I understand you need to use TypeVar('A', str, int) to get what you want
    Ah, I think I misread that, sorry.
    Max Fischer
    @maxfischer2781
    Yeah, I would like it to act similar to A but not to restrict T to some pre-determined domain. It should just be some concrete type, though I could see inference selecting something like Union[type(a), type(b)]. Definitely did not expect a generic catch-all like Any/object.
    Joshua Oreman
    @oremanj
    @maxfischer2781 Using the common base class instead of a union when a common subtype is needed was a decision made early on in mypy development; I don't think it's customizable
    Julin S
    @ju-sh
    I have function that accepts a List[int] with None as default argument. I used None instead of [] to avoid this problem. When mypy complained about the type List[int] being wrong, I made it def fn(Optional[List[int]] = None) -> None: But operations inside cannot be done with None and mypy complains. How can I get around this? Can some one help me out?
    Julin S
    @ju-sh
    I'm using dataclass. And the fn() I meant is actually 'init__ equivalent' of dataclass.
    Shantanu
    @hauntsaninja
    @maxfischer2781 object is a concrete type!
    Julin S
    @ju-sh
    I just noticed that when written as an ordinary function, mypy has no error.

    This one has error.

    from dataclasses import dataclass
    from typing import List, Optional
    
    class Example:
        lst: List[int] = None
    
        def __post__init__(self):
            if self.lst is None:
                self.lst = [1, 2, 3]
    
        def fn(self):
            if len(self.lst) < 2:
                print("Aww...")
            elif self.lst[1] == 2:
                print("Yes")
            else:
                print("No")

    This one has no error.

    def fn(lst: Optional[List[int]] = None) -> None:
        if lst is None:
            lst = [1, 2, 3]
        if len(lst) < 2:
            print("Aww...")
        elif lst[1] == 2:
            print("Yes")
        else:
            print("No")

    I have no idea what I'm doing wrong..

    Max Fischer
    @maxfischer2781

    @maxfischer2781 object is a concrete type!

    Sorry, poor choice of words. I meant that as "a concrete type the function receives" – in this case either type(a) or type(b).

    @ju-sh If Are you aware that a dataclass can have a factory for a field? The example in the docs is specifically for a List[int].
    Julin S
    @ju-sh
    @maxfischer2781 Thanks. That fixed it.
    Joshua Bronson
    @jab

    Hi typing folks, the following code matches https://github.com/python/typeshed/blob/master/stdlib/3/typing.pyi#L452-L457 and yet mypy complains with a few hard-to-debug errors :

    # mymapping_with_overload.py
    
    import typing as _t
    
    KT = _t.TypeVar('KT')
    VT = _t.TypeVar('VT')
    
    class MyMapping(_t.MutableMapping[KT, VT]):
        @_t.overload
        def update(self, arg: _t.Mapping[KT, VT], **kw: VT) -> None: ...
        @_t.overload
        def update(self, arg: _t.Iterable[_t.Tuple[KT, VT]], **kw: VT) -> None: ...
        @_t.overload
        def update(self, **kw: VT) -> None: ...
        def update(self, *args: _t.Union[_t.Mapping[KT, VT], _t.Iterable[_t.Tuple[KT, VT]]], **kw: VT) -> None:
            ...  # implementation here (validate either 0 or 1 positional arg provided)
    
        # more methods defined here

    Results:

    ➜ mypy mymapping_with_overload.py
    mymapping_with_overload.py:9: error: Signature of "update" incompatible with supertype "MutableMapping"
    mymapping_with_overload.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 1
    mymapping_with_overload.py:15: error: Overloaded function implementation does not accept all possible arguments of signature 2
    Found 3 errors in 1 file (checked 1 source file)

    Any ideas? Thanks for any help!

    davidatbu
    @davidatbu

    Hi @jab ,
    Your code does not in fact match the typing.py code you linked, because the code you linked uses positional only arguments (python/mypy#2497), but your code allows keyword args(ie, one can do map.update(arg=some_other_map)). You should add __ to the name of the argument to make it positional only(look at the mypy issue I linked, also the typing.py code that you linked)

    The last two errors also have to do with this difference. The overloaded signatures indicate one can domap.update(arg=some_other_map), but that is in fact not allowed by the signature of the implementation, where keyword arguments must be of VT type, not Mapping[KT, VT])

    Joshua Bronson
    @jab
    Thank you so much, @davidatbu, makes sense, and merely changing arg to __arg on lines 10 and 12 was all it took to fix this! This seems unusually hard to debug and Google for. Could mypy give users a more helpful error message here, given how rare it is that the problem is with the formal param name, not the actual type hints?
    ishaan
    @pikachu
    Hi - is there are regular release schedule for mypy? We're looking for a recent merged commit in the next release. I couldn't find a schedule on the repo's README.
    1 reply
    davidatbu
    @davidatbu

    You're welcome @jab . From my experience, this is a problem with overloading in general, not this specific positional-only thing. In fact, if you remove the overloading and recreate the error, mypy gives a reasonable error message "unexpected keyword argument ..."

    While I think error reporting for overloading can be improved, I think the mypy team has other priorities(by the look of their Github issues). I'd bet that they would be happy for a PR about it though.

    FYI, I'm totally unaffiliated iwht the mypy team, I've just been following thir work kind of closely :)

    Andre Delfino
    @andresdelfino
    Any idea why I'm getting "error: Cannot find implementation or library stub for module named 'X'" for an installed module instead of "error: Skipping analyzing 'X': found module but no type hints or library stubs"?
    Andre Delfino
    @andresdelfino
    This is with bravado and bravado.core
    Shantanu
    @hauntsaninja
    do you need to set mypypath or pass mypy the python executable you expect to have the module available for?