#type-hinting

1 messages · Page 67 of 1

tranquil turtle
buoyant swift
#

isn't that the same with __init__?

trim tangle
tranquil turtle
#

@final is only for type-checking, i want implement runtime behaviour

#

-> None: will be fine

trim tangle
#

Yeah, just put None

soft matrix
#

at least at runtime

oblique urchin
#

interesting, the runtime actually throws an error if __init__ returns something. I didn't know that

trim tangle
rough sluiceBOT
#

@trim tangle :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 5, in <module>
003 | TypeError: __init__() should return None, not 'int'
trim tangle
#

!e
Same with some other methods

class Bar:
    def __len__(self):
        return -1

print(len(Bar()))
rough sluiceBOT
#

@trim tangle :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 5, in <module>
003 | ValueError: __len__() should return >= 0
rough sluiceBOT
#

Objects/typeobject.c line 7735

PyErr_Format(PyExc_TypeError,```
soft matrix
#

how do you avoid a # type: ignore here without using isinstance checks?
SupportsSave = SupportsWrite[bytes] & {seek = (x: int, /) -> object}

boreal ingot
#

so I am assuming you want to be able to pass something not supporting .seek if seek_begin is False?

#

in that case @overload combined with Literal would do that job

#

@soft matrix

#

wait, ....

#

okay so I am blind

#

I would think that should work, let me check something

soft matrix
#

maybe ill open an issue on pyright

#

cant wait to get as designed

boreal ingot
#

from what I can tell it seems like pyright only checks the signature of the methods when considering @overload.

when checking the body it just ignores all the info from @overload

#

so this is fine ```py
from typing import overload, Literal

@overload
def foo(x: int) -> int: ...

@overload
def foo(x: str) -> str: ...

def foo(x: int | str) -> int | str:
return "abc"but this is notpy
from typing import overload, Literal

@overload
def foo(x: int) -> int: ...

@overload
def foo(x: str) -> str: ...

def foo(x: int | str) -> str:
return "abc"``` but the first one should also be wrong, as it will return a str even when the overload would suggest it would only return int

soft matrix
#

i think that will just have to wait until conditional types happen

foggy thicket
#

I have a class that returns instance, what is the alternative of enclosing the class name within quotes to denote return type

class aobject(object):
    """
    Inheriting this class allows you to define an async __init__.
    """

    async def __new__(cls, *a: Any, **kw: Any) -> "aobject": # <- alternative of this?
        instance = super().__new__(cls)
        await instance.__init__(*a, **kw)
        return instance

    async def __init__(self):
        pass
soft matrix
#

Looks to me like you need typing_extensions.Self

foggy thicket
grave fjord
#

You need a SupportsSeek

#

Many objects can be written too but not seeked eg socket files

hearty shell
#

No

#

xD

#

I also dont know if you can async __new__ since it complains if it doesn't return an instance of the class in which it is defined

#

async def __new__(cls, *a: Any, **kw: Any) -> "aobject" this signature would be incorrect, it would be Awaitable["aobject"]\

soft matrix
soft matrix
grave fjord
#

You need to subclass SupportsWrite

soft matrix
#

I do

foggy thicket
hearty shell
#

Really? What xD

foggy thicket
hearty shell
foggy thicket
# hearty shell Really? What xD

Yes, I just had a case where making the __init__ was the only real option.
I was making a inherited class where overwriting parent's __await__ gave alot of hard to debug race conditions, so I resided on this crazy solution

foggy thicket
#

Parent's async__init__() must be called within the innit, alternative solution of locking the resource is just opening plethora's box, and I wouldn't even know that much asyncio to fix that lol

hearty shell
#

Interesting, I would have never thought of that xD

foggy thicket
# hearty shell Ohhh, apparently this is not true, and then you call the `__init__` method direc...

!e

from __future__ import annotations
import asyncio
from typing import *  # tad bit lazy


class aobject(object):
    """
    Inheriting this class allows you to define an async __init__.
    """

    async def __new__(cls, *a: Any, **kw: Any) -> aobject:
        instance = super().__new__(cls)
        await instance.__init__(*a, **kw)
        return instance

    async def __init__(self):
        pass


class foo(aobject):
    async def __init__(self) -> None:
        await asyncio.sleep(1)
        print("Innit !!")


asyncio.run(foo())
rough sluiceBOT
#

@foggy thicket :white_check_mark: Your eval job has completed with return code 0.

Innit !!
blazing nest
proven fog
#

^ yeah someone was talking about making __init__ async in #python-discussion so he can await a method in the initialization. dunder methods aren’t really meant for async so hence class methods

verbal ginkgo
#

can you typehint lambda arguments?

soft matrix
#

yes

#

well not using the normal methods, you need to get a bit creative

#
x: Callable[[str, int], str] = lambda input, times: input * times```
#

should work

verbal ginkgo
tranquil turtle
#

It is type hint

fierce ridge
#

although i am generally big in favor of converting "logic" into "data", it's probably one of the best things about functional programming style

trim tangle
#

For functions:
if you have a function with 5 boolean/optional values, there are potentially 32 combinations, and you need to specify them manually.

fierce ridge
#

right, exactly. it's possible but prohibitive

proven fog
nova venture
#

Is there a way to enforce via an annotation / other at-type-hint-time construct that all fields of a dataclass are a certain type

#

for example:

@dataclass
class DataClass:
    a: Child1
    b: Child2
    c: Orphan
    d: Parent
#

assuming I want all fields of DataClass to be a Parent, removing c would fix the error

hasty hull
#

After the new pyright change, its now reporting errors on my mypy plugin because mypy type hints use Bogus

#

What does Bogus i.e. FlexibleAlias do?

#

Nevermind:

A Bogus[T] type alias for marking when we subvert the type system

We need this for compiling with mypyc, which inserts runtime
typechecks that cause problems when we subvert the type system. So
when compiling with mypyc, we turn those places into Any, while
keeping the types around for normal typechecks.

Since this causes the runtime types to be Any, this is best used
in places where efficient access to properties is not important.
For those cases some other technique should be used.

#

This does break everything for pyright though

little hare
#

i'll be honest I'm completely confused with everything you just said

hasty hull
#

We need an ```py
if TYPE_CHECKER == 'mypy':
...
else:
...

hasty hull
little hare
#

what change occured?

hasty hull
#

A change with type inference, mypy does not explicitly type hint all properties when subclassing

#

So in pyright, type.fullname was Unknown which disabled type checking in my case

expr.node: TypeInfo = ...
expr.node.bases[0].type.fullname.lower().endswith('dict')
nova venture
hasty hull
#

pyright now reports errors because fullname is actually FlexibleAlias[T, T]

#

which it correctly resolves now

hallow flint
acoustic thicket
boreal vessel
hasty hull
#

And the problem in my case is the way its typed internally in mypy so theres not much I can actually do

blazing nest
#

I don't understand what's wrong here, why does Pyright complain with: ```
Cannot assign member "abc" for type "Test"
  "RuntimeVar[str]" is incompatible with "str"

Argument of type "ContextManager[str]" cannot be assigned to parameter "cm" of type "AbstractContextManager" in function "enter_context"
  TypeVar "_T_co@ContextManager" is covariant
    Type "str" cannot be assigned to type "RuntimeVar[str]"

```python
class RuntimeVar(Generic[T]):
    value: T

    def __get__(self, instance: Optional[object], cls: Type[object]) -> T:
        if instance is None:
            raise AttributeError(f'{cls.__name__!r} object has no attribute {self.name!r}')

        if not hasattr(self, 'value'):
            raise RuntimeError(
                f'Cannot access runtime variable {self.name!r} before assignment'
            )

        return self.value

    def __set__(self, instance: object, value: T) -> None:
        self.value = value


class Test:
    abc: RuntimeVar[str] = RuntimeVar()
    stack: ExitStack

    def test(self, value: ContextManager[str]) -> None:
        self.abc = self.stack.enter_context(value)
trim tangle
#

hmmm, how does pyright generally handle descriptors?

nova venture
# acoustic thicket wouldnt you just do `a: Parent`, `b: Parent` etc?

a and b are already Parent (as well as d), it's only c that isn't Parent. Sure I could just change the variable types if I remembered to, but idea is if I have a method def consume_data_class(x: T), I want to restrict T to have variables w/ only type Parent such that all(isinstance(x_field, Parent) for x_field in vars(x).values()) is true

#

A similar concept exists for functions, enforcing each parameter to be a Parent

class OnlyParentTypes(Protocol):
    def __call__(self, *args: Parent):
        pass
#

instead, I want each variable of a class I use to be Parent

blazing nest
#

I'll probably open a GitHub Discussion on PyRight's repository 🤔

hallow flint
hasty hull
#

Yeah sorry I should've clarified that. I would love to be able to special case types for Pyright so that I could actually use recursive types or just any new features that only pyright understands

#

Because I mainly write library code, the types have to be accepted by as many type checkers as possible

#

The only way I get around this at the moment is because of codegen

#

Which is slightly annoying and it means that users have to explicitly specify the type checker they're using

#

and it isn't a viable solution for projects that don't already use codegen

main hollow
#

Hello, I am looking at discord py's code and I see this

    @property
    def color(self) -> Colour:
        """:class:`Colour`: A property that returns a color denoting the rendered color for
        the member. If the default color is the one rendered then an instance of :meth:`Colour.default`
        is returned.

        There is an alias for this named :attr:`colour`.
        """
        return self.colour

is -> Colour type inferement?

What is the arrow operator doing here, Colour is a class

acoustic thicket
main hollow
#

Ah, so self.colour may more specifically fire the functions

#

since this is an alias, that's why it appears to do nothing

#

but realistically, self.colour is doing the work

#

And -> Colour is just returning the type the Class Colour is returning

#

If I understand correctly

acoustic thicket
#

🤔
the typehint just tells you that when you do y = x.color the type of y is supposed to be Colour

main hollow
#

but Colour is a class, so I assume it's grabbing the __repr__?

#

or is there another magic method it would use to infer that?

acoustic thicket
#

the type of an object is a class

#

type annotations are basically like comments

main hollow
#

Ok, so I'm laughing a bit here reading through this. There's no way this is efficient or am I crazy?
There's like a hundred of these

    @classmethod
    def blue(cls: Type[CT]) -> CT:
        """A factory method that returns a :class:`Colour` with a value of ``0x3498db``."""
        return cls(0x3498DB)

    @classmethod
    def dark_blue(cls: Type[CT]) -> CT:
        """A factory method that returns a :class:`Colour` with a value of ``0x206694``."""
        return cls(0x206694)

    @classmethod
    def purple(cls: Type[CT]) -> CT:
        """A factory method that returns a :class:`Colour` with a value of ``0x9b59b6``."""
        return cls(0x9B59B6)
#

shouldn't this have been a single function, and you pass a value that returns from a list? 😆

hearty shell
#

efficient? performance wise it is the same if you were using a dict, but this way you get to create an instance of color via well defined classmethods, instead of a single function in which you pass a string that may or may not be typed correctly

acoustic thicket
#

this way you can do Colour.blue() instead of Colour.get_colour("blue")
you get better ide autocomplete in the former because your ide can look up what methods the class has
in the latter you don't, your ide cannot infer that stuff from a string

main hollow
#

Ah, I come from JS/TS

#

where in TS you can just type any value from a list

#

So, this is done specifically to have proper typing using Type[CT] with a return value of CT as the return type

hearty shell
#

Actually, by doing Colour.get_colour("blue") you are doing two dictionary lookups, which is actually worse

#

Not that this is the reason they are doing it

main hollow
#

versus 100s of methods

#

But, I get it for the typings

acoustic thicket
#

well, get_colour could be properly typed too, str -> Colour

main hollow
acoustic thicket
#

!warn 922468559391698994 this is not the place to post chainmail

rough sluiceBOT
#

:incoming_envelope: :ok_hand: applied warning to @dense cedar.

main hollow
#

Or well, instead of just asking here. Is there a good place for docs on typings like this?

Should I refer to pythons documentation?

acoustic thicket
main hollow
#

ah, because CLS would only inherit a value, so colour_name would have to have been a default value or applied above.

Instead it's safer to add another arg and type that

#

This is actually helping me a lot.

Reading the discord.py source makes a lot more sense

brazen jolt
#

Let's say I have a generic class like this: ```py
T = TypeVar(bound=list)

class Foo(Generic[T]):
def init(self, l: T):
self.l = l

def get(self, key: int) -> Something:
    return self.l[key]

``` how could I set the return type for the get function (currently Something), to the type of the list. i.e. if I marked the class like Foo[list[int]], return type for get should be int, if the class is Foo[list], it should be Any, etc.

#

I was thinking of something like this, but pyright complains: ```py
def get(self: Self[T[T2]], key: int) -> T2:

#

hm, seems like I can't use Self, since it isn't generic, with the class name directly, I was able to do self: MyCls[list[V]] which did solve it, though that's quite annoying when subclassing

trim tangle
#

@brazen jolt what you want is called "higher kinded types"

#

and Python doesn't support them

brazen jolt
#

oh

#

that's a shame

#

is there at least some pending PEP or some progress on making one?

trim tangle
#

no

#

or at least I'm not aware of one

#

The use case is pretty narrow in normal Python tbh.

brazen jolt
#

I suppose that's true, still though, it'd be nice to have when you do need it

brazen jolt
#

this is really annoying, since this already has 3 overloads like these, and all subclasses will need their own overloads

brisk hedge
#

Pretty much you can do this if you get rid of the bound on list

#

And pick either a concrete type or a protocol to use in place of the list-bound-typevar

soft matrix
#

@trim tangle is pyright playground's back end down?

trim tangle
#

idk

#

I'll check it out in a few hours

soft matrix
#

Thanks

#

It doesn't seem to be revealing errors/types

trim tangle
#

hmmm, my serverless provider apparently doesn't have npx now

#

nice backwards compatibility 🖕

#

idk what to do to be honest

#

I think pyright-playground is just down for today, sorry

trim tangle
#

@soft matrix fixed it

#

worst hack ever

#
const runPyright = (code) => new Promise((resolve) => {
  const filename = `/tmp/${mkFilename()}`;
  fs.writeFile(filename, code, () => {
    exec(`${process.argv[0]} ./run_pyright.js --outputjson ${filename}`, (error, stdout, stderr) => {
      fs.unlink(filename, () => {});
      resolve({stdout, stderr});
    });
  })
});

// run_pyright.js
require('pyright')
soft matrix
#

Nice

#

Thank you

trim tangle
#

Sounds nice, I'm a javascript noob

trim tangle
#

ooh

#

I don't actually have to reinvent the wheel, nice

#

maybe I should read the docs and not outdated StackOverflow answers

grave fjord
#

what's the pyright option to prevent using implicit Any as a type hint?

boreal ingot
grave fjord
#

No

#

I mean like:

from untyped_package import SomeAny

def foo(v: SomeAny) -> None:
    ...
boreal ingot
#

I cant seem to find anything about it on the configurations page, I know mypy had a option for it iirc

grave fjord
#

Yeah me too. Imho it's ok to use an unknown thing from an unknown source at runtime, but not in a type annotation

boreal ingot
#

it does have an option to report missing stub files

reportMissingTypeStubs [boolean or string, optional]: Generate or suppress diagnostics for imports that have no corresponding type stub file (either a typeshed file or a custom type stub). The type checker requires type stubs to do its best job at analysis. The default value for this setting is 'none'. Note that there is a corresponding quick fix for this diagnostics that let you generate custom type stub to improve editing experiences.'

grave fjord
hasty hull
#

Because that enables the reportUnknown* diagnostics

grave fjord
#

I don't think so

hasty hull
#

What are you using that would would be an implicit Any (with pyright) for type hints?

oblique urchin
#

anything from an imported untyped library, potentially

hasty hull
#

Yes but this is still treated as Unknown by pyright e.g. ```py
from mureq import Request

def foo(response: Request):
...

Pyright reports three errors for me, `mureq` is untyped, `Request` is an unknown import symbol and type of `response` is unknown (because the `Request` class doesn't actually exist, at least not in a way that pyright can resolve)
#

If you use a class that exists then you will only get errors when using it: ```py
from mureq import Response

def foo(response: Response):
print(response.body)

#

response.body is unknown

terse dome
#

hey I have a class, customclass

I'd like to create an empty list of type customclass, so that my IDE pycharm with auto complete the classes methods inside the list

#

ive been trying to figure this outt for a whhile, but its super confusing to mee for some reason

hearty shell
#

empty_list: list[CustomClass] = []

#

Is that what you are trying to do?

terse dome
#

ya

#

really just that simple?

#

facepalm

#

thanks man!

hearty shell
#

Np ^^

terse dome
#

ahhhh added complexity: The customclass is being imported

#

im still not seeing my methods in autocomplete

#

i think it migght be because i have the class in a separate file...? maybe?

hearty shell
#

baz.py:

class A:
    def foo(self) -> int: ...

main.py:

from baz import A

bar: list[A] = []

bar[1].f  # if your ide doesnt autocomplete that, it is on your ide
terse dome
#

ah ok, thats what i have

hearty shell
#

Humm weird that it doesnt

terse dome
#

i'll poke my ide a bit

#

its pycharm

#

oh

#

the list im making is inside another class

#

so a class inside main has a self.newlist

hearty shell
#

Could you show where it is not auto completing?

terse dome
#

hey, i totally got it working, i typehinted some othe variable by mistake... sigh

hearty shell
#

Ahh alight

terse dome
#

this is excellent, thanks very much

trim tangle
fierce ridge
#

maybe the pyright project might want to setup an "official" one

#

and/or some aspiring web dev or ux designer wants to make a pretty interface for it

terse sky
#

I was surprised to find today that protocols seem to work just fine with generic methods

#

not generic protocols, but generic methods on a regular protocol

#
F = TypeVar('F', bound=Callable)

class F1P(Protocol):
    def __call__(self, func: F) -> F:
        ...
#

Here's a full example I helped someone with (until they surprised me by adding protocols... 🙂 )

from typing import Any, Callable, Protocol, TypeVar, cast
from dataclasses import dataclass

F = TypeVar('F', bound=Callable)

class F1P(Protocol):
    def __call__(self, func: F) -> F:
        ...

class F1:
    def __call__(self, func: F) -> F:
        print("Inspecting", func)
        return cast(F, func)

@dataclass
class Thing2:
    f1: F1P

thing2 = Thing2(F1())

@thing2.f1
def foo(a: int) -> str:
    return str(a)

@thing2.f1
def bar(a: dict) -> tuple:
    return tuple(a.values())

a = foo(12)
b = bar({"hello": "world"})
reveal_type(a)  # str
reveal_type(b)  # tuple[Any]
reveal_type(thing2.f1)  # F1P
foo('x')    # Type check error: incompatible type "str"; expected "int"
fierce ridge
#

i feel like i've used this before

#

good demonstration

terse sky
#

in fact, I just realized, you don't even need F1 as a class any more

#

functions also satisfy the call protocol

#

we can just have

def F1(func: F) -> F:
    print("Inspecting", func)
    return cast(F, func)

@dataclass
class Thing2:
    f1: F1P

thing2 = Thing2(F1)
#

now

oblique urchin
#

why do you need the cast?

terse sky
#

it was probably left over from some original code

#

I copied some mypy example that actually decorated F

#

my friend is just inspecting F

#

Is there some way to do this example without using a Protocol?

#

With just Callable and typevars?

#

it feels like it's not possible to do it with Callable + TypeVars, but then, I also didn't think the protocol would work....

oblique urchin
#

seems like Callable[[F], F] should be enough

trim tangle
#

Nope, Callable[[F], F] means a different thing

terse sky
#

does it? it seems to work

trim tangle
#

for example, this is illegal:

Identity = Callable[[T], T]
oblique urchin
#

why?

trim tangle
#

Or rather, it's a different thing

#

It is a generic type alias, not the type of a generic function

terse sky
#

Yes, that's what I thought based on my experience in statically typed languages

oblique urchin
#

ah right, the TypeVar scope is different

terse sky
#

but mypy accepts it

trim tangle
oblique urchin
#

there was a discussion about this on typing-sig a while ago; I think different type checkers use different TypeVar scopes here

terse sky
#

Callable is supposed to be something that maps types; you feed it some types, you get a type out

#

F isn't a type and it's not a type variable of anything around

trim tangle
#

In Haskell, you can do type Identity = forall a. a -> a ||If you enable rank 2 types||

terse sky
#

right

#

the fact that it's an extension even in Haskell basically proves my point 🙂

#

but it's also just about as shocking to me that this works for Protocols

trim tangle
#

in PureScript you don't need an extension 😎

#

yeah it's a pleasant surprise

terse sky
#

i wouldn't expect protocols to support generic functions

#

the protocol can be generic, yes

trim tangle
#

this is not specific to protocols, you can do this with an ABC or a normal class

#

like, Mapping.get is a generic function

#

(the type of default is generic)

terse sky
#

why would get be a generic function

oblique urchin
#

the return type depends on the second argument

terse sky
#

that's a very strange choice IMHO

#

definitely not one I would make even in the language supported it

oblique urchin
#

get(k: str, val: T) -> V | T

trim tangle
#

yeah it's generic and overloaded

terse sky
#

idk i can imagine very rare cases where it could be convenient, in most cases what it's going to do is just allow you to pointlessly shift work from one place to another

#

and in other cases what its' going to do is to cause a type error to appear much later in your code than it should

oblique urchin
#

how would you suggest dict.get be typed?

terse sky
#

that way such functions are typed in basically all languages

#

enforce that the default is of value type

#

the reason I was surprised that it was a generic function is because I've seen things like get in half a dozen statically typed languages and I've never seen anything other than what I described

trim tangle
#

that's because they don't have bare unions

terse sky
#

If you have get(k: K, val: V) -> V | T then you're going to need a follow-up isinstance check in most cases anyway

#

there are the rare cases where you pass V | T to something downstream, or where V and T have a common Protocol

#

but those are rare

oblique urchin
terse sky
#

@oblique urchin It depends how precise of a match you want

#

if you want an example that matches up perfectly, Kotlin

trim tangle
#

other languages don't get to choose, do they?

terse sky
#

Java, C#, Rust, etc

#

are similar

#

well, not if they want their hash table to fulfill a trait

#

trait/interface

#

that includes that API

#

if they just want the method to standalone, then sure

trim tangle
#

I meant that they simply don't have Union

terse sky
#

they can choose

#

Rust has enums

trim tangle
#

that's very different

terse sky
#

sure

trim tangle
#

things: Sequence[int] = dict_of_lists.get("some_key", ())
^ this seems perfectly fine to me

terse sky
#

my point is that if they wanted to do it, they could still do it

terse sky
#

err, there's absolutely no reason to do it that way

#

sorry there's "performance"

trim tangle
#

hm?

terse sky
#

that code doesn't constitute a use case for this behavior

#

you could write dict_of_lists.get("some_key", list())

#

and it'd be fine, and work in many more circumstances, to boot

#

I did say though:

there are the rare cases where you pass V | T to something downstream, or where V and T have a common Protocol

#

So yes, there are going to be similar examples that are more useful, involving common protocols/interfaces.
My point is just that these examples are rare. It's going to be more common that someone accidentally passes a default of the wrong type to get; I know I've made this mistake plenty of times

#

in which case it's just far more useful to have the benefit of your IDE/mypy immediately flagging the error.
Instead of quietly giving you a result which could give an error quite a few lines later

solar saffron
#

is there any way to type hint class attributes using Protocol ?

oblique urchin
solar saffron
#
from __future__ import annotations

from typing import TYPE_CHECKING, Dict, Generic, Protocol, Tuple, TypeVar

if TYPE_CHECKING:

    class PermissionsProtocol(Protocol):
        create_post: bool
        delete_post: bool


T = TypeVar("T")


class DictToDot(Generic[T]):
    def __init__(self, data) -> None:
        self.__dict__.update(data)

    def __getattr__(self, attribute: str) -> bool:
        return False


class User(object):
    __slots__: Tuple[str, ...] = ("username", "password", "_permissions")

    def __init__(
        self,
        username: str,
        password: str,
        permissions: Dict[str, bool | Dict[str, bool]],
    ) -> None:
        self.username = username
        self.password = password
        self._permissions = permissions

    @property
    def permissions(self) -> DictToDot[PermissionsProtocol]:
        return DictToDot(self._permissions)


user = User(
    username="Server Bot",
    password="aStrongPassword",
    permissions={"create_post": True, "delete_post": False},
)

print(user.permissions)

I want to type hint DictToDot class attributes so I can access user.permissions.create_post

oblique urchin
# terse sky in which case it's just far more useful to have the benefit of your IDE/mypy imm...

we could try this, but people already think .get() is too strict for only accepting keys with a compatible type: https://github.com/python/typeshed/issues/7015

GitHub

list.index and list.count currently only accepts values of the element type and Sequence.index and Sequence.count use Any for the type of the argument. In my opinion all of these functions could sa...

terse sky
#

yeah, I'm not surprised

#

I mean, in the end it's differing philosophies

#

there's people who want their dynamic python in the end; they accept type annotations only if "false positives" are totally minimized

#

and there's people who want the types to make sense

#

even if the cost is that sometimes, code that could technically work ok at runtime, will get flagged

#

I'm definitely in the latter camp

#

FWIW I think there is a better use case for e.g. x in my_dict to work, even when x is KeyType | SomethingElse

#

then there is for get's second argument to differ

oblique urchin
#

Might be worth changing it just to see what mypy-primer says. I expect there are cases where people do something like d.get(k, sentinel) where sentinel is some unrelated type

terse sky
#

I'm sure people do do that

#

for me, the sentinel case is already handled by .get(k)

#

granted, it would be much nicer if we had null aware operators, but even so

oblique urchin
#

you may need to distinguish between "value is None" and "key is absent"

terse sky
#

yes, in some cases, but that is even more rare

#

you'd have to have a dict with optional values

#

in generic code you'd probably have to make that distinction

#

in non-generic code it's very rare that this is a sane thing to do, IME

blazing nest
#

Has anyone ever had issues with Pyright / Pylance autocompleting typed dictionaries with the wrong quotes?

#

If I try to use single quotes all my intellisense options will disappear 😔

soft matrix
#

I find it annoying that if I type an opening quote I end up with ""key"

blazing nest
#

Yeah I get that with GitHub Autopilot often, I don't make note of it as much with the autocomplete though.

trim tangle
#

single quotes are a crime

blazing nest
#

Not if I am the one using them 😮‍💨

hasty hull
blazing nest
#

It's odd because not that I've made a note of it, it actually switches back and forth - but if I get autocomplete for one type than I can't use the other type.

#

This used to be in double quotes 🤔

hasty hull
#

🤔

#

Yeah it should use the quote method that is used most in that file

#

I contributed the support autocompleting TypedDict dictionary expressions and I remember there being methods to determine which quote to use

#

Although I didn't look at the dictionary member expression part so that could be different

hearty shell
#

Is there a name for the feature that "allows" pyright to somewhat correctly understand this or is this just generally better type inference?

from typing import Callable, TypeVar

A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C')

T = TypeVar('T')

def compose(g: Callable[[B], C], f: Callable[[A], B]) -> Callable[[A], C]:
    def o(a: A) -> C:
        return g(f(a))
    return o

def g(t: int) -> str: ...

def idT(a: T) -> T:
    return a

reveal_type(compose(idT, g))  # Type of "compose(id, g)" is "(int) -> (T@id | str)"

Mypy just gives up an says it cant infer the type of the first argument

#

Or maybe it is just cheating because it infers the return type as an Union of T and str 🤔

grave fjord
near vigil
#

so y'all know how discord.py uses type-hints for conversion,
well I made a custom converter,

async def foo(ctx, my_arg: VerifyEntity[discord.Member]):
``` how'd I make it so the linter knows what what's returned is Member not a VerifyEntity instance
blazing nest
near vigil
#

😔 I see

trim tangle
#

It is a hard problem I guess 🤷‍♂️

blazing nest
#

Yeah you can accidentally mess up

drowsy edge
#

let's gooooooooooooooo

#

sirupate gangstar from 10 number gali

trim tangle
# drowsy edge

This channel is for discussing type annotations, now typing on a keyboard 🙂

buoyant swift
#

good job though

fierce ridge
#

i am really happy to see how popular monkeytype has become. the creator is a hard worker and the site is high quality

#

</ot>

hearty shell
hearty shell
#
Argument of type "(a: T@id) -> T@id" cannot be assigned to parameter "f" of type "(A@compose) -> B@compose" in function "compose"
  Type "(a: T@id) -> T@id" cannot be assigned to type "(A@compose) -> B@compose"
    Function return type "T@id" is incompatible with type "B@compose"
      Type "T@id" cannot be assigned to type "int"
trim tangle
#

Is there some nice way to make a type-safe Builder?
This is what I came up with https://mypy-play.net/?mypy=latest&python=3.10&gist=4f7748f60013bdeecb06789c18caaf1e

foo_builder().with_fizz(42).build()  # error
foo_builder().with_fizz(42).with_fizz(69).build()  # error
foo_builder().with_fizz(42).with_buzz("yo").build()  # error
foo_builder().with_fizz(42).with_buzz("yo").with_bar([1, 2, 3]).build()  # ok

But it seems kinda boilerplatey and complex. And it requires ugly non-null assertions and keeping track of stuff yourself

mint inlet
#

I guess typestates yeah? (Keep a typevar for each property)

Alternatively could probably do some funky union with literals and self-types

trim tangle
#

I don't think I actually ever used the Builder pattern... not sure what it's useful for

#

I was under the impression it only exists because Java/C#/C++ don't have keyword arguments?

mint inlet
#

I have to use it to work around some limitations (paramspec needs to be constructed)

#

So I do stuff like builder.with_int_option('name' 'description').with_injected_arg(<idk I haven't made this completely, just been a thought in my head>)...

trim tangle
#

ah

mint inlet
#

And construct some Builder[[CoolInjectedProtocol, int]] that matches a (a: CoolInjectedProtocol, b: int) -> None

#

There's probably a better way, but that's a way I've had to use it

#

(Though yeah for a configuration thing I don't see any reason to not just use function args)

mint inlet
blazing nest
loud zodiac
#

I have a base class with async classmethod, it calls subclass methods to finish its operations, each subclass method may return different data types, I want to be able to type hint that

class BaseClass:

    @classmethod
    async def build_object(cls):
        instance = cls()
        # do some async work
        return instance.get_object()

    def get_object():
        raise NotImplementedError

class Subclass1(BaseClass):
    
    def get_object() -> int:
        ...

class Subclass2(BaseClass):

    def get_object() -> str:
       ...

how can I do that without hardcoding possible return types as a Union in base class?

trim tangle
loud zodiac
#

thanks

foggy thicket
#

Is this a correct typehint?

x: types.ModuleType = __import__("sys")
soft matrix
#

yes but dont do this

foggy thicket
#

Oh yeah, was just curious dogge

soft matrix
#

whats the name for something like ```py
class NotEntirelySubscripted(Generic[T, T2]): ...
NotEntirelySubscripted[int, T3] # this

#

is there a name for something like this that can be further subscripted?

soft matrix
#

Generic TypeAlias?

terse sky
#

pretty much. they are not supported in python, and in many languages

#

afaik? anyway

#

actually, apparently python does support it

#

til

soft matrix
#

yeah

oblique urchin
#

yes, source of a lot of runtime complexity in typing.py

soft matrix
#

and its only get worse :)

terse sky
#

C++ has it, Kotlin and I think Java do not

#

actually, could be wrong about that too 😛

trim tangle
oblique urchin
#

RIP PEP 637

soft matrix
#

how would that work?

#

oh with kwargs

#

i thought you wanted Generic.reverse_args or something cursed like that

trim tangle
#

I wish there was something simpler - like

Pair = Fun[T, tuple[T, T]]
#

also more explicit, it's now 100% clear that this is a generic alias

terse sky
#

why can't you swap argument order?

terse sky
#

it's a generic type alias with one argument, presumably

trim tangle
terse sky
#

python's notation for generics is pretty terrible unfortunately

trim tangle
terse sky
#

why not?

#

what will happen?

trim tangle
terse sky
#

oh, I see

#

hah

#

awful

#

yes, python's notation for these things is just awful, generic function/classes don't explicitly declare their argument list

trim tangle
#

in a normal language ™️ it's as simple as rs type SomeAlias<A, B> = Foo<B, A>; ```hs
type SomeAlias a b = Foo b a

terse sky
#

it just depends on these magic typevariables showing up on the right hand side

#

there's also no visual indication that something is generic

#

you just have to know that T is a TypeVar and not a type

trim tangle
#

yep, I agree that this makes them harder to process

terse sky
#

i guess for classes you at least "inherit" from Generic, which is still bad, but at least it's explicit

#

well, maybe that's just as bad actually

trim tangle
terse sky
#

The <>, no keyword approach in a lot of languages is quite nice, IMHO

oblique urchin
#

I think the generics syntax was mostly motivated by trying to avoid introducing new Python syntax

#

If someone comes up with a nice syntax we could replace it

hearty shell
#

If there was no motivation to introduce new functionality to [] in order to improve typing alone, why would there be motivation to add an entirly new syntax just for typing?

blazing nest
hallow flint
trim tangle
#

it already does feel like a different language tho lemon_pika

hasty hull
#

yeah, I recently had to refactor an untyped codebase and it feels like a different language

fierce ridge
#

i think it steers you away from some "old school" python idioms and towards other new idioms

#

so it's different in that sense

#

(imo it's for the better mostly)

tranquil turtle
#
def open_something(path: ???) -> Something:
    with open(path) as fs:
        return Something.from_bytes(fs.read())

how to annotate path in this case?

#

it can be int, str, bytes and path-like objects

#

there is alias for this type in typeshed, but typeshed cant be imported at runtime

#

i think str | pathlib.Path is nice for most cases, but i want full solution

terse sky
#

I mean depends what you mean by a full solution

oblique urchin
hearty shell
#

Well the one that is in typeshed should be the full solution

oblique urchin
#

presumably something like int | str | bytes | os.PathLike[str | bytes]

hearty shell
#

seems to be

#

_OpenFile = Union[StrOrBytesPath, int]

#

then

#

StrOrBytesPath = Union[str, bytes, PathLike[str], PathLike[bytes]] # stable

#

from os import PathLike

tranquil turtle
#

from typing import PathLikewould be nice

terse sky
#

Are you annotating code that's already widely used

#

Or annotating new code

#

Because if this is new code

tranquil turtle
#

new code in my small project

terse sky
#

Just annotate Path

oblique urchin
#

we've talked about putting some of the _typeshed helpers in typing_extensions, that could be useful

terse sky
#

It's better to be more strict, unless there's some reason not to

#

Fd integers should be a very very very rare thing to pass around in python

oblique urchin
#

yes, that feels more likely to be the result of a bug than anything else

terse sky
#

And paths should always be represented by paths, not strings or bytes

oblique urchin
#

unless you're writing really low-level code

oblique urchin
tranquil turtle
#

thank you all

oblique urchin
#

I've come to think that having an int or str annotation is kind of a code smell, you usually should have a more precise type (e.g., NewType) to indicate what the int or str means

terse sky
#

Eh

#

I mean there's lots of places that int or str is fine

#

If you use NewType for everything you'll lose your mind and constantly be casting

#

Using it judiciously is the key IMHO

hearty shell
oblique urchin
#

i was talking more about writing an application that you're starting from scratch

hearty shell
#

Ah, yeah I agree that path should probably be used then

trim tangle
#

when you're passing around stuff like list[Mapping[str, list[tuple[str, str]]]] which has some important business meaning

soft matrix
#

@trim tangle I think I'm addicted

#

Gonna have to install a time limit on pyright playground heck

#

Wait it's actually 24h yesterday and the day before

soft matrix
#

I don't even have it open which is the weirdest part

grave fjord
soft matrix
#

whats the default for a TypeVarTuple if its unbound?

#

is it *tuple[()] or *tuple[Any, ...]?

oblique urchin
#

Should be the latter, but not sure if this was clearly defined

soft matrix
foggy thicket
#

What should TypeVar be typehinted with?

#

T: ... = TypeVar('T'), what should be in the place of the ellipsis?

#

Apparently, mypy raises a lot of problems when I hint it with TypeVar itself, which got me curiou.

acoustic thicket
#

TypeVar should work 🤔

#

hmm it doesnt

foggy thicket
#

Yeah, that doesn't seem to be the case remthink

acoustic thicket
#

oh wait

#

nvm

#

why do you want to hint T anyway

foggy thicket
#

But hinting it with TypeVar itself not working got me pretty curious, that's why I asked

acoustic thicket
#

T: type works so there's that

foggy thicket
acoustic thicket
#

pyright accepts it

foggy thicket
#

mypy doesn't

acoustic thicket
#

interesting

foggy thicket
#

although I have a bound kwarg, but I don't think it is playing a role

soft matrix
#

you shouldnt need to annotate a type var

oblique urchin
#

even more than that, you should not annotate a TypeVar. Type checkers specifically recognize the T = TypeVar("T") pattern, anything else likely won't work

#

don't do T, U = TypeVar("T"), TypeVar("U") either for example

acoustic thicket
#

interesting

mint inlet
#

does anyone know if it's legal to unpack a typevartuple into a concatenate expression 🤔

#

i did a quick ctrl+f in pep 646 and saw no mention of paramspec/concatenate which is why i'm asking

#

(why? cause i'd like to allow any kwargs/args in addition to the typevartuple lol)

#

oh i could just (maybe? if it's possible to be generic) use pyright's TypedDict Unpack extension thing; but mypy compat 😭

brisk hedge
#

Are TypeVar constraints supposed to be invariant?

oblique urchin
#

but I'm not sure there's a useful behavior where they're not invariant

brisk hedge
#
class A:...
class B(A):...
class C:...

T = TypeVar("T", A, C)
def wrap(cls: type[T]) -> type[T]:
    return cls

WrappedB = wrap(B)

reveal_type(WrappedB)

Here, WrappedB is revealed to be type[A] rather than type[B] which I would have expected

oblique urchin
#

typevars with constraints can only be solved to their constraints

#

variance matters only if one of the constraints is a subtype of another

brisk hedge
#

TypeVar("T", A, B, C) also reveals type[A], is that also intended?

#

Though if I rearrange it so the child comes first, it resolves to B

oblique urchin
#

yeah that seems like a bug, is this mypy? mypy has some order-dependence issues with constrained TypeVars

brisk hedge
#

Pyright

oblique urchin
#

report a bug 🙂

worthy swan
#

Whats the correct way to typehint a list of items where the type is stored in a variable? Something like self._type = int then data: list[list[self._type]] = [...]

trim tangle
worthy swan
#

Whats that?

#

This needs to work in 3.9.5 and 3.10.0 by the way

trim tangle
#

So you'd have something like ```py
T = TypeVar("T")

class Things(Generic[T]):
def init(self) -> None:
self._data: list[list[T]] = []
or maybe something likepy
T = TypeVar("T")

class Things(Generic[T]):
def init(self, factory: Callable[[object], T]) -> None:
self._factory = factory
self._data: list[list[T]] = []

#

maybe you can tell more about what you are doing?

worthy swan
#

Im doing a linked list thing

#

Where you specify what type(s) can be added

#

Like in java where you have Array<String> or whatever but SortedLinkedList(str)

trim tangle
#

ah

#

maybe you just need a generic class?

#

is there some reason you want to check it at runtime?

#

(generic classes in Python are very similar to generic classes in Java, like Array<String>)

worthy swan
#
from typing import Generic, TypeVar #plus other stuff

T = TypeVar("T")

def __init__(self, _type: type, *args):
    self._data: list[list[T, int]] = []
    
    self._type = _type #there's other validation but not showing here

def __iter__(self) -> Generator[tuple[int, list[T, int]], None, None]:
    ...

def get(self, count_to_get: int) -> list[T, int]:
    ...```does this seem right? I think generic is what i want
trim tangle
#

What is list[T, int] supposed to mean?

#

ah

#

Why do you need to have the type at runtime though?

worthy swan
#

I'm using list like tuple

#

So list[float, int] would be [3.14, 5]

trim tangle
#

list[T, int] is not a valid type hint

worthy swan
trim tangle
#

you can't, use a tuple

worthy swan
#

Then its not a list

trim tangle
#

why do you need a list?

worthy swan
#

Our teacher said we have to

trim tangle
#

are type annotations a requirement?

worthy swan
#

yes

worthy swan
#

For the purposes of this task

trim tangle
#

well... there's not much point in making a type annotation type checkers will reject 🙂

worthy swan
trim tangle
#

that's a strange linked list...

worthy swan
#

?

trim tangle
#

A linked list node stores its value and a reference to the next node, like

class Node(Generic[T]):
    def __init__(self, value: T, next_node: Optional["Node[T]"] = None) -> None:
        self.value = value
        self.next_node = next_node
worthy swan
#
sll = SortedLinkedList(int | float)
sll.add(5)
sll.add(3.14)
print(sll) #[3.14, 5]
print(sll._data) #[[5, None], [3.14, 0]]```
#

This is how we were told to do it

#

So instead of use the Node we just do a list containing the two attrs

#

And its not a tuple since that implies the value doesnt change but the pointer can change

oblique urchin
#

so you're using a normal linked list to store a linked list?

trim tangle
worthy swan
oblique urchin
worthy swan
#

Just what we have to do for our exams

#

Ive got all the code working just want to sort out the typehints

worthy swan
trim tangle
#

are you checking it at runtime on each element?

worthy swan
#

For union stuff

trim tangle
#

what if you want a linked list of functions? like a list of Callable[[int], str]

worthy swan
#

I ensure the items being added correspond to the type

worthy swan
#

Dont need to worry about functions and stuff

#

Also is it better to do for x in self or for x in self.__iter__()?

trim tangle
#

ah, okay

#

for x in self

worthy swan
#

Okay

trim tangle
#

also, keep in mind that isinstance with Union works only since 3.10

worthy swan
#

yeah I have stuff for handling that

#
if version_info >= (3, 10, 0):
    from types import UnionType
elif version_info >= (3, 9, 0):
    from typing import _UnionGenericAlias as UnionType
else:
    raise SystemExit("This program requires a Python version >= 3.9.0")

class SortedLinkedList:
    ...
    def add(...)...:
       ...
        if version_info < (3, 10, 0) and isinstance(self._type, UnionType):
            #In versions < 3.10, to do isinstance on a Union object,
            #we need to do on the `__args__` attribute
            type_to_check = self._type.__args__
        else:
            #In all other occassions, we can do isinstance on the _type
            type_to_check = self._type

        if not isinstance(item_to_add, type_to_check):
            expected_type_str = str(self._type) if isinstance(self._type, UnionType) else self._type.__name__
            raise TypeError(f"Expected type {expected_type_str!r} for parameter 'item' but got type {type(item_to_add).__name__!r}")```
#

Is the stuff with Generic correct though @trim tangle

#

And is there a way I can link T and self._type?

trim tangle
#

that way, when you call things = Things(int), things will be inferred as Things[int]

worthy swan
#

hmm

#

thanks

#

yeah that seems to be working

#

thank you

mint inlet
#

(note that that will not accept Unions under mypy :^)

fresh current
#

How does type hinting work.

trim tangle
rustic gull
#
def foo(): # -> ?:
    class Bar:
        pass
    return Bar

how do you annotate returned classes?

oblique urchin
#

you can say -> type. However, dynamically created classes don't tend to work well with static type checking

shrewd venture
blazing nest
grave fjord
#

macros plz

mint inlet
#

nevar

solemn sapphire
#
def func() -> list[dict[str, str | bool]]:
  ...
def another_func(argument: str):
  ...

another_func(func()["some_key"])

Pyright will complain here saying that another_func accepts only str and I am passing str | bool

What would you do? 🤔

hearty shell
#

Could you turn that dict into a TypedDict?

#

Or is the data too unstructured

solemn sapphire
#

What is that? 🤔

solemn sapphire
hearty shell
#

!docs typing.TypedDict

rough sluiceBOT
#

class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict "dict").

`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
hearty shell
#

You can make use of that then

solemn sapphire
#

Oh!

#

Thanks!

hearty shell
#

When you access the key, the type checker will either complain the key doesnt exist or it will know its type exactly

#

Np

solemn sapphire
#

I never knew that!
This is certainly very very useful to know!

#

Ah! This solves many unrelated problems as well!!

hearty shell
#

It is indeed very useful, although It seems this is the general consensus so I should point it out, TypedDicts are more meant to patch old code then to design around them, the proper solution would be to serialise the data via a dataclass or something like that

solemn sapphire
#

dataclass on a dict?

hearty shell
#

No I mean, like instead of using dicts, use dataclasses instead

#

Yeah

solemn sapphire
#

Oh!

#

hmm

#

So my class would inherit from a dict and then I should proceed to properly type hint the attributes of that class?

hearty shell
#

No, the idea is to not work with dicts, just to work with dataclasses and pass those around your application. So if have a function that returns some sort of serialised data, make a dataclass for that grouping of data and create an instance of that in the function

#

if you are getting a dict from an api, serialise that into a dataclass as soon as you get that dict, and then proceed to pass that around your application

solemn sapphire
#

Oh I see

#

Interesting idea!

#

Did you think of that right now?

hearty shell
#

Nope

#

x)

solemn sapphire
#

Ah, but still very genius

#

Thanks mate!

fresh current
near vigil
#

Hey so I have this

class MultipleConverter(commands.Converter):

    def __init__(self, **kwargs):
        self.kwargs = kwargs

    async def convert(self, ctx, argument):
        for key, value in self.kwargs.items():
            converted = some_discordpy_blackmagic(ctx, argument, value)
            # Converted is an instance of `value`
            setattr(self, key, converted)

basically you can like x: MultipleConverter(role=discord.Role, member=discord.Member)

after some discord.py conversion black magic

you will be able to access x.role & x.member being an instance of what you passed to to that kwarg.

How do I annotate this stuff so my linter knows that the kwargs will become attributes of the class, each being an instance of what was passed to that kwarg

brisk heart
#

you can't

#

that pattern makes no sense

#

normally you'd be able to at least use Annotated but I doubt discord.py supports it

brisk hedge
#

You can't type it without just creating static versions of the class each time you use it.

#

As opposed to dynamically created types (which are quite difficult to type statically)

near vigil
#

fair enough

#

crawls back to type: ignore

dire bobcat
#

what does the if TYPE_CHECKING do?

#

whats that stuff for

#

i wanna add it to look fancy

proven fog
#

!d typing.TYPE_CHECKING

rough sluiceBOT
#

typing.TYPE_CHECKING```
A special constant that is assumed to be `True` by 3rd party static type checkers. It is `False` at runtime. Usage:

```py
if TYPE_CHECKING:
    import expensive_mod

def fun(arg: 'expensive_mod.SomeType') -> None:
    local_var: expensive_mod.AnotherType = other_fun()
```  The first type annotation must be enclosed in quotes, making it a “forward reference”, to hide the `expensive_mod` reference from the interpreter runtime. Type annotations for local variables are not evaluated, so the second annotation does not need to be enclosed in quotes.
dire bobcat
#

i see...

dire bobcat
#

what would the typehint be for a courotine func

proven fog
#

!d typing.Callable

rough sluiceBOT
#

typing.Callable```
Callable type; `Callable[[int], str]` is a function of (int) -> str.

The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type.

There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types. `Callable[..., ReturnType]` (literal ellipsis) can be used to type hint a callable taking any number of arguments and returning `ReturnType`. A plain [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable "typing.Callable") is equivalent to `Callable[..., Any]`, and in turn to [`collections.abc.Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable "collections.abc.Callable").
soft matrix
#

!d collections.abc.Coroutine

rough sluiceBOT
#

class collections.abc.Coroutine```
ABC for coroutine compatible classes. These implement the following methods, defined in [Coroutine Objects](https://docs.python.org/3/reference/datamodel.html#coroutine-objects): [`send()`](https://docs.python.org/3/reference/datamodel.html#coroutine.send "coroutine.send"), [`throw()`](https://docs.python.org/3/reference/datamodel.html#coroutine.throw "coroutine.throw"), and [`close()`](https://docs.python.org/3/reference/datamodel.html#coroutine.close "coroutine.close"). Custom implementations must also implement `__await__()`. All [`Coroutine`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Coroutine "collections.abc.Coroutine") instances are also instances of [`Awaitable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Awaitable "collections.abc.Awaitable"). See also the definition of [coroutine](https://docs.python.org/3/glossary.html#term-coroutine).

Note

In CPython, generator-based coroutines (generators decorated with [`types.coroutine()`](https://docs.python.org/3/library/types.html#types.coroutine "types.coroutine") or [`asyncio.coroutine()`](https://docs.python.org/3/library/asyncio-task.html#asyncio.coroutine "asyncio.coroutine")) are *awaitables*, even though they do not have an `__await__()` method. Using `isinstance(gencoro, Coroutine)` for them will return `False`. Use [`inspect.isawaitable()`](https://docs.python.org/3/library/inspect.html#inspect.isawaitable "inspect.isawaitable") to detect them.

New in version 3.5.
proven fog
#

o

dire bobcat
soft matrix
#

You need both of these (except Callable from collections.abc)

#

Yes

#

These are the recommended classes to use now

dire bobcat
#

CoroFunc = Union[collections.abc.Courotine, typing.Callable]?

proven fog
#

Callable has a syntax

soft matrix
#

collections.abc.Callable[[parameter1, parameter2, parametern], collections.abc.Coroutine[return type]]

proven fog
#

[[TParam, TParam, …], TReturn] iirc

dire bobcat
#

so

#

im returning a decorator

#

which just returns an asynchronous function

#

is it really that much of a pain in the ass?

soft matrix
#

You should probably be using type vars in a decorator

soft matrix
#

And you should also use import from for the types you want from collections.abc

dire bobcat
#

how come?

#

i havent really clocked typevars btw so

soft matrix
#

!d typing.TypeVar

rough sluiceBOT
#

class typing.TypeVar```
Type variable.

Usage:

```py
T = TypeVar('T')  # Can be anything
A = TypeVar('A', str, bytes)  # Must be str or bytes
```  Type variables exist primarily for the benefit of static type checkers. They serve as the parameters for generic types as well as for generic function definitions. See [`Generic`](https://docs.python.org/3/library/typing.html#typing.Generic "typing.Generic") for more information on generic types. Generic functions work as follows...
dire bobcat
#

can i just do

#

CF = TypeVar('CF', Callable, Courotine)?

soft matrix
#

No

#

Well probably not

dire bobcat
#

why nottt

soft matrix
#

Cause you probably don't want to use constraints

proven fog
#

It will only allow that generic to be the types in the constraint

soft matrix
#

The docs kinda suck atm I think there's a pr to improve them to point people to use the bound argument

proven fog
#

sometimes you want them, sometimes you don’t lol

soft matrix
#

Which I think is what you want

dire bobcat
#

so what do i do? 😩

soft matrix
#

Actually before I tell you

#

Can you show me the decorator?

dire bobcat
#
def on(self, event_name: str) -> None:
        """
        A function used to decorate event
        functions with to declare them
        as an event.

        Parameters
        ----------
        event_name: :class:`str`
            the event to assign the 
            function to.
        """

        def decorator(func) -> :
            if not asyncio.iscoroutinefunction(func):
                raise InvalidFunction("Your event must be asynchronous.")
            self._events[event_name] = func
            return func

        return decorator

#

should i whip in a #type:ignoreat the return decorator for the banter?

#

ive seen other libraries do that

soft matrix
#

So you return the function in place

dire bobcat
soft matrix
#

return func

dire bobcat
#

yes

soft matrix
#

Ok so this is a time that you want to use type vars

dire bobcat
#

solid

soft matrix
#

Because they allow you to preserve info about func

#

OnT = TypeVar("OnT", bound=Callable[..., Coroutine]) is the type var definition you need

dire bobcat
#

OnT?

oblique urchin
soft matrix
#

That's probably fair

dire bobcat
#

what does the ... signify

oblique urchin
#

it's like Any but for the arguments to a callable

soft matrix
#
def on(self, event_name: str) -> Callable[[OnT], OnT]:
        def decorator(func: OnT) -> OnT:
            return func

        return decorator
#

So this is saying to the type checker that the function on returns another callable/function (as most decorators do) that takes something that is compatible with OnT and returns the same type

dire bobcat
#

ok

#

i should probably work on my type-hinting aspect of python a bit more

soft matrix
#

If I say OnT is a coroutine function it's probably a bit easier to read (although slightly incorrect)

acoustic thicket
dire bobcat
#

what would the 'official' way of making a function for the purpose of doing nothing be?

hearty shell
#

define doing nothing?

dire bobcat
hearty shell
#

if you put that inside a function, the function returns None

dire bobcat
#

I need to make EVENT_CONVERTERS have a key for ready and the func absorbs _raw_data, does nothing, and spits nothing back

#

never mind

grave fjord
#

why isn't this warning of a redundant cast?

from __future__ import annotations

import importlib.metadata
from collections.abc import Iterable
from typing import Protocol, cast

import importlib_metadata


class _EntryPoints(Protocol):
    def __call__(self, **kwargs: str) -> Iterable[importlib_metadata.EntryPoint]:
        pass


_entry_points: _EntryPoints = importlib_metadata.entry_points
_entry_points_cast = cast(_EntryPoints, importlib_metadata.entry_points)
#

my understanding was that typing.cast was syntactic sugar for:
v: SomeThing = something_else # type: ignore[assignment]

#

which does get recognised as an unnecessary type ignore

noble elm
#

how can i narrow between a Union of typed dicts?

class UserInfo(TypedDict):
    type: Literal["user_added", ...] # there are a few more here
    id: str
    by: NotRequired[str]


class ChannelInfo(TypedDict):
    type: Literal["channel_renamed", ...]
    by: str
    name: NotRequired[str]

i thought of doing

def foo(data: UserInfo | ChannelInfo) -> str:
  if "id" in data:
    # data should be UserInfo
``` but that does not seem to work (type still remains `UserInfo | ChannelInfo`)
#

i want to avoid writing a large data["type"] == "user_added" or data["type"] == ...

blazing nest
#

Pyright supports this (type narrowing of typed dicts)

noble elm
#

it's pyright 🙁

acoustic thicket
#

could make a typeguard

#

if you're on 3.10

noble elm
#

not on 3.10 😔

#

actually, i can get it from typing_extensions right

acoustic thicket
#

not sure

#

probably

noble elm
#

ot, but when did the peps website change

trim tangle
noble elm
#

ah alright, thanks

oblique urchin
oblique urchin
grave fjord
oblique urchin
oblique urchin
grave fjord
#

It wouldn't be a subtype if variance didn't allow it

#

The implementation of warn redundant cast should be equivalent to removing the cast and running the type checker again imho

hasty hull
#

At least for pyright a type hint can be narrowed where a cast will not

clever bridge
#

re.Pattern seems to be Generic, but what do the type arguments correspond to?

grave fjord
#

Bytes or Str

clever bridge
#

Which corresponds to what it'll be matched against?

oblique urchin
#

yes

clever bridge
#

👍 Thanks!

hearty shell
acoustic thicket
#

wdym

oblique urchin
hearty shell
#

The proposal here differs significantly from Python's types, as the types in this proposal are entirely ignored, not evaluated as expressions or accessible at runtime as metadata. This difference is largely motivated by the existing community precedent, where JS type systems do not tend to use JS expression grammar for their types, so it is not possible to evaluate them as such.

#

I saw someone's comment on how this might be a bad idea because it essentially means you could use the type hints as you wish and use it for different purposes, but if no meta data is even stored I think that it would be difficult to use them for anything useful other then type checking

oblique urchin
blazing nest
#

I get that it would be removed in minimization by more-or-less all tools, but that shouldn't be a requirement and at that point you're just back at TypeScript

oblique urchin
blazing nest
#

Hmm wait but then those characters would be disallowed. This now means that no JavaScript syntax can be used in the typehints I suppose.

#

Maybe {} could wrap the type, as it's not used for anything in the typing world.

#

<> is, and if the tool used , then that wouldn't be able to be used in types

#

Ah yeah sure actually, thinking about it there's some alternatives for characters working as markers for the end

maiden mesa
#

Is this the correct way to create a nested dictionary typehinting?

from typing import TypedDict

class A(TypedDict):
    value: int
    is_active: bool

class B(TypedDict):
    value: int
    is_active: bool

class Amount(TypedDict):
    default_value: int
    a_field: A
    b_field: B 
#

Or should I do this instead:

amount = TypedDict("Amount", {
    "default_value": int,
    "a_field": {
        "value": int,
        "is_active": bool
    },
    "b_field": {
        "value": int,
        "is_active": bool
    }
})```
#

Ah nvm, the second one throws an error with __annotations__ method

oblique urchin
#

The first one is right

maiden mesa
#

ohk, thanks

tall wing
#

How should I set a default value for type-hinting on a class attribute? For example, I have a variable called self.selected which I want to type-hint as a custom class, but initially I set it to None since nothing is selected when the class is first initialized. Should I do:

self.selected: Union[MyClass, None] = None

Or just do:

self.selected: MyClass = None
brisk heart
#

Optional[MyClass]

#

While implicit optionals are pretty cool they're generally discouraged from what I've seen

hearty shell
#

Hum but you are saying it always has a value?

#

After init

tall wing
#

The value would always be either None or the custom class.

hearty shell
#

Oh, then yeah

trim tangle
#

In that case, Union[MyClass, None]. Or Optional[MyClass] which is exactly the same

brisk heart
#

Actually, what's the recommended approach for attributes that only get set later but are only ever realistically accessed after being set?

#

say a session created after some client is started

trim tangle
#

You could make a property or method that raises an exception if something is not None

hearty shell
#

If they are always set when accessed I would just annotate it in the class body

trim tangle
#

...or you can refactor the code so that the None is gone. It's very often possible

#

You're going to need to provide some reasoning as to why it won't be None when it will be used anyway. So why not automate this reasoning 🤷

tall wing
#

Thanks guys! I was just wondering since my IDE was complaining, and I was wondering if there was a better way.

brisk heart
#

Well I just remembered a problem I had while back. I solved it by just not having sessions as attributes and instead creating a new one every time

#

didn't actually affect performance at all (if we disregard the creation of objects)

tall wing
#

The selected variable is a rectangle on a tkinter Canvas, and when the user first boots the app, you wouldn't have any rectangle selected. I also want to be able to deselect a rectangle, so that would mean setting it to None. Not sure if there's a better way, but that's the general approach I am taking with this.

trim tangle
#

if it can be None, it should be Optional

#

that's the idea

tall wing
#

Cool, thanks again!

terse sky
#

FWIW I'd suggest really trying to avoid the class initializing with a member as None, if it later expects it to always be set
Once youhave something like that, it is basically what's often called 2 phase init: you contruct the class but it's not really a functional instance yet, until you do some more stuff (set the None members)

#

probably everybody has done this as a workaround when you have an existing body of code and want to a change relatively minimal, but if you're writing new code or trying to make the change properly, you could consider deferring construction of the type until you have the member you need, or refactor it into two separate types, one before the MyClass is availab,e one after, etc.

fierce ridge
#

the problem with two-stage init (at least in python) is that you end up with assert self.foo is not None everywhere because python has no way to know that a certain thing is definitely not null by the time the method is called

void panther
fierce ridge
#

i actually wonder how this works in statically-typed languages

#

same issue?

terse sky
#

though, in languages with better support for either optional, or null, there's usually some quick way to access that is technically type safe

#

in python you can do this too it's just ugly

#

because it has to be a free function

#
T = TypeVar("T")

def unwrap(t: Optional[T]) -> T:
    if t is None:
        raise RuntimeError("Unwrapped empty optional!")
    return t
#

so then instead of doing assert everywhere, you'd unwrap

fierce ridge
#

ahh that's a nice idea

terse sky
#

In Kotlin you'd be able to just write !!, in C++ you'd write .value();, in Rust you'd write .unwrap(), etc

fierce ridge
#

that reminds me of how you'd work with Maybe in haskell

terse sky
#

yeah

#

Python's "bare" approach to optional/union is really painful

fierce ridge
#

that honestly doesn't seem so bad as a helper function

#

ill probably keep using assert

terse sky
#

Kotlin also has some stuff specifically around late init, to help

#

so you wouldn't typically actually annotate it as Optional/nullable

fierce ridge
#

i see, that's useful

blazing nest
trim tangle
#

well, builders are kinda non-type-safe

#

a builder is overclocked multi-step initialization

torpid yarrow
#

getting this error with mypy: venv/lib/python3.10/site-packages/discord/commands/__init__.py: error: Source file found twice under different module names: "discord.commands.__init__" and "discord.commands"
I assume the issue here is because I have "discord.commands" listed in the packages in setup.py and I also have __init__.py in discord/commands, but I assume there's a way to get around this, right?

trim tangle
#

Can you show the code?

#

mypy is telling you that the object you're trying to access on could be None

torpid yarrow
#

you'd probably be able to do something likepy foo.attr if hasattr(foo, "attr") else None or even ```py
foo.attr if foo is not None else None

trim tangle
#

I think hasattr won't work with type checkers

torpid yarrow
#

ah, maybe the latter in that case

clever bridge
#

Is it possible to override a method where a parameter's type changes to a subclass of the original?

trim tangle
#

@clever bridge what do you mean?

clever bridge
#
class A:
  ...

class B(A):
  ...

class C:
  def func(self, a: A) -> None:
    ...

class D(C):
  # pyright complains about B being incompatible with A
  def func(self, a: B) -> None:
    ...
trim tangle
#

Can you show a more realistic example? Why do you need this?

clever bridge
#

Yes, it's for a library I'm creating. The user subclasses a base class and overrides a callback method. As part of the subclass init users specify options which end up on the options parameter. To provide proper typehinting for attributes of options, my intention was just that people typehint their override with a custom subclass (in the example class B).

#

I don't know if that made any sense

trim tangle
#

can you show the code?

#

like, an example

clever bridge
#
# dummy class
class Options:
  ...

class Command:
  # options specified here
  def __init_subclass__(cls, options: list[Option]) -> None:
    ...

  async def callback(self, options: Options) -> None:
    ...
class MyOptions(Options):
  first: int
  second: str

class MyCommand(Command, options=[...]):
  async def callback(self, options: MyOptions) -> None:
    ...
trim tangle
#

Then maybe you need a generic class?

clever bridge
#

Probably

trim tangle
#

what is in options=[...]?

clever bridge
#

Whatever the user provides as the options to the command

#

Something like

[Option(name="first"), Option(name="second")]
trim tangle
#

ah

clever bridge
#

Probably a Generic is best

trim tangle
#

so the user has to make sure MyOptions and the options list are synced?

clever bridge
#

Yeah

#

MyOptions is just meant to provide type hinting and IDE autocomplete for what they specify in options

#

I am presently questioning how I managed to think making Options generic in some form was a solution but making Command generic was not. dead

trim tangle
#

Yeah, you can make a generic, like ```py
Opt = TypeVar("Opt", bound=Options)

class Command(Generic[Opt]):

options specified here

def init_subclass(cls, options: list[Option]) -> None:
...

async def callback(self, options: Opt) -> None:
...

and then users can just subclass it
clever bridge
#

Yeah

#

Somehow I thought of a bunch of other Generic things but none on Command, good lord my brain

#

Thank you!

#

Oh I think I remember why I didn't use a Generic

#

TypeVar hell

#

Oh lord

trim tangle
#

@clever bridge You can just make one typevar per module, unless you need different bounds or variances

#

but yeah it is... extremely verbose

clever bridge
#

Oh it's just me having like twenty vars for various functions and things for other features all bound to different types and things with them all getting cranky about being bound to a generic and whatnot

trim tangle
#

Unfortunately, mypy just cannot understand that if depth == 4, then the attributes are not None. You could use an assert statement or put # type: ignore

clever bridge
#

I'm just going to take the easy way out and make it options: Any

trim tangle
#

What guarantees that these nodes are not None?

#

If left is None and right is not None, we're not a leaf

#
    def is_leaf(self) -> bool:
        return not self.left and not self.right
#

if you're trying to figure out an algorithm, maybe sort out the types later?

#

If you are sure for some reason that at that point it is not None, you could put an assertion or a # type: ignore

torpid yarrow
#

reproducible via pip install py-cord~=2.0.0b5 mypy && mypy --namespace-packages test.py
test.py: ```py
from discord.ext import commands

hasty hull
#

Has anyone requested pyright to report an error if a comparison is discarded? e.g. ```py
response.status_code == 400
return response

trim tangle
#

that's an interesting idea

#

I think it's more of a job for a linter like flake8, seems pretty easy to detect with just parsing the AST

#

Although I guess pyright has a completely random option to warn against implicit string concatenation. Maybe it drove someone really mad lol

hasty hull
#

Yeah, pyright reports an error when you discard a coroutine without awaiting it which is a similar issue so maybe

hearty shell
#

That is weird that it complains about that

#

Doesnt seem to be a job for the type checker

hasty hull
#

I'm guessing the reasoning is that there is no case where you would ever want to do that intentionally

hearty shell
#

I am sure that are tons of things that have to practical use and yet pyright doesnt complain

oblique urchin
#

so it makes sense to do it in a type checker. I do it in pyanalyze too and mypy just merged support for the same thing

hearty shell
#

Humm, didn't think about it that way

#

Do linters not store any metadata on the "objects" it has seen when it goes through a file?

oblique urchin
hearty shell
#

I guess you would have to go through imports

#

Yeah thinking about it more it would be quite hard

terse sky
#

this exists in other languages, fwiw, and it's generally a property of the type itself

#

i.e. there are types for which, when they are returned from a function, you will not be allowed not to assign the result of the functional call to a variable

#

Both C++ and Rust support this, for example

#

it can also be a property of the function, at least in C++

#

it's common to annotate functions without side effects that way (since they have no side effects, there's no point calling them if you don't use the result)

#

although I'm honestly a bit skeptical how many real problems the second approach catches
The first approach is often used on types that indicate errors, the idea being that if someone calls foo(); to do something they want, and foo(); indicates errors via its return, then obviously now you haven't checked for errors

#

it seems like the first approach is for the type checker, the second more appropriate for a linter, fwiw.

trim tangle
#

you can ignore this, but in that case you need to explicitly do let _ = function_with_very_important_result()

rustic gull
#

can you reuse TypeVars in unrelated functions?

soft matrix
#

Yes

dire bobcat
#

when do you use type: ignore when returning an inner func for a decorator?

upbeat wadi
#

type: ignore is used when you need to ignore an error from type checkers, without context your question can't really be answered-can you show your code?

dire bobcat
#

when running flake8 i get error: Type variable "Coro" has no meaning in this context

#

im doing self.events: Dict[str, List[Coro]] where Coro = TypeVar("Coro", bound=Callable[..., Coroutine[Any, Any, Any]])

#

whats wrong with this?

upbeat wadi
#

you would just use Callable[..., Coroutine[Any, Any, Any]] there, as the error says, the typevar doesn't have any meaning there

dire bobcat
#

hmmm i see

soft matrix
dire bobcat
#

oh

#

also

#

i feel like a scammer doing this

#
# fmt: off
    DISPATCH           = 0 # noqa: ignore
    HEARTBEAT          = 1 # noqa: ignore
    IDENTIFY           = 2 # noqa: ignore
    PRESENCE           = 3 # noqa: ignore
    VOICE_STATE        = 4 # noqa: ignore
    VOICE_PING         = 5 # noqa: ignore
    RESUME             = 6 # noqa: ignore
    RECONNECT          = 7 # noqa: ignore
    REQUEST_MEMBERS    = 8 # noqa: ignore
    INVALIDATE_SESSION = 9 # noqa: ignore
    HELLO              = 10 # noqa: ignore
    HEARTBEAT_ACK      = 11 # noqa: ignore
    GUILD_SYNC         = 12 # noqa: ignore
    # fmt: on
``` but it stops the errors
#

how should i really do this?

soft matrix
#

why does that need noqa: ignore?

#

cause enums shouldnt

dire bobcat
#

because flake8 doesnt like the spaces

#

its not an enum

#

just some variables in a class

soft matrix
#

well thats not really type checking related but you probably shouldnt be using flake8 for this stuff if you have black running on the project

soft matrix
#

well its just redundant

dire bobcat
#

er

oblique urchin
#

flake8 has some other checks beyond formatting, but yeah I'd recommend turning off any formatting-related flake8 errors if you're running Black

oblique urchin
#

config file

dire bobcat
#

is it an arg when running flake8?

oblique urchin
#

you'd have to read the docs for details, but normally it's a .flake8 file at the root of your project

hearty shell
#

Or setup.cfg

#

sadly no pyproject.toml

dire bobcat
#

im going to describe something

#

you guys need to tell me the variable/name for it

hearty shell
#

Or you could look at the docs xD

dire bobcat
#

?

#

this is different

#

no

#

this is just me trying to name my func

#

prob wrong channel actually

torpid yarrow
oblique urchin
#

that's probably making mypy confused

clever bridge
#

Complicated question, so I frequently use objects that have attributes can be, say A | B or Optional[A], however, if that object is makes its way to a specific function, instead of A | B it'll only be A or instead of Optional[A] it'll be None due to type guarding and whatnot beforehand. Is there some way I can declare that without repeatedly spamming asserts?

oblique urchin
clever bridge
#

Hmm

#

Well it’s not really something I can avoid, especially when modelling stuff I didn’t design

#

It’s more like, there’s a base and a couple dozen places it’s used, but each place has some checks run before making it to the main function. For example, if something is Optional[A] then it’ll never be None in that body since a check will fail beforehand if it’s None.

hearty shell
#

Does sound like something you would Generic for

clever bridge
#

Well, it might be like six things so that sounds like pain

#

And different things

clever bridge
#

I might just end up the route of asserts

#

Or some sort of subclass mixin of some kind defined in if TYPE_CHECKING blocks

mint inlet
#

something similar to that (that's the generic method they were talking about)

#

you'd be able to typeguard

clever bridge
#

Ahh

mint inlet
#

though yeah if you don't want the generic (could probably stick in a type checking block if no type args are given at runtime) you can always just assert

clever bridge
#

Would be nice to not have Generics everywhere, but it's getting less and less likely that's going to work out

mint inlet
#

lol

#

yeah concrete types are very nice to work with and wrap your head around

clever bridge
#

It's causing issues when I don't have a choice with how things are structured

#

I just made it Generic one one attribute which was painful and now I need to figure out the next it seems

clever bridge
#

Is it possible to have a method overload that depends on the type of a variable? I.e. A.func returns int if A.attr is int. A.attr would be typed as say str | int and by the time A.func is called it has been narrowed down to int.

hearty shell
#

Would it return str if attr was string?

#

Yeah you would need generics

#
T = TypeVar('T', int, str)

class A(Generic[T]):
    attr: T
    
    def func(self) -> T: ...
clever bridge
#

Oh great

#

More Generics

#

Why is the solution to all my problems more Generics? dead

dire bobcat
#

warning: TypeVar "Coro" appears only once in generic function signature

#

what does this mean? 😢

#

async def _run_event(self, coro: Coro, *args, **kwargs) -> None:

#

it takes coro

#

whats wrong with that?

#

Coro = TypeVar("Coro", bound=Callable[..., Coroutine[Any, Any, Any]])

trim tangle
#

In this case, just use Callable[..., Coroutine]]

#

You are not using any typevarness here. As in, you're not linking any two types together

soft matrix
#

Does anyone know why Function/MethodType isn't generic at runtime?

#

I can't find anything in pep 585 or on python/typing

#

Ideally it would make some mypy issues relating to Callable's descriptor behaviour

fierce ridge
rustic gull
#

k

oblique urchin
soft matrix
#

Yeah but there are cases where I think it's useful , especially for typing completeness

trim tangle
#

because a Callable isn't necessarily a descriptor

#

I have no idea how to do this properly in pyright, actually.

acoustic thicket
#

protocol?

#

w __call__ and __get__

trim tangle
#

hmmmm

grave fjord
buoyant swift
#

it's a function that returns a coro when called, it itself is not a coro

dire bobcat
#

its literally a coroutine

trim tangle
#

An async function is not a coroutine. An async function returns a coroutine

#

Coroutine is the type of a coroutine