#type-hinting
1 messages · Page 67 of 1
isn't that the same with __init__?
Why not just use @final?
@final is only for type-checking, i want implement runtime behaviour
-> None: will be fine
Yeah, just put None
no you can return whatever from init subclass
at least at runtime
interesting, the runtime actually throws an error if __init__ returns something. I didn't know that
really?
!e
class Foo:
def __init__(self):
return 42
Foo()
@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'
!e
Same with some other methods
class Bar:
def __len__(self):
return -1
print(len(Bar()))
@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
Objects/typeobject.c line 7735
PyErr_Format(PyExc_TypeError,```
how do you avoid a # type: ignore here without using isinstance checks?
SupportsSave = SupportsWrite[bytes] & {seek = (x: int, /) -> object}
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
mypy also doesnt like this https://mypy-play.net/?mypy=master&python=3.10&gist=3e2a15ad2634cebc9fc88f231fb52b97
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
maybe ill open an issue on pyright
cant wait to get as designed
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
i think that will just have to wait until conditional types happen
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
Looks to me like you need typing_extensions.Self
Sorry, but I didn't quite catch that. Could elaborate a bit more?
You need a SupportsSeek
Many objects can be written too but not seeked eg socket files
async def __init__(self): Is this allowed?
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"]\
The actual overloads work fine but in the body the types cannot be narrowed conditionally
Umm you should change the return type to typing_extensions.Self
You need to subclass SupportsWrite
I do
It apparently works 👀
Really? What xD
I just did from __future__ import annotations and hardcoded the class name, and called it a day lol
Are you sure?
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
Very positive.
This is a working (but old and unsafe) version of it : https://gist.github.com/Achxy/1de44e4e4781ec92ce6cdd1cb6c67738
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
Ohhh, apparently this is not true, and then you call the __init__ method directly, that is why it doesnt complain
Interesting, I would have never thought of that xD
!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())
@foggy thicket :white_check_mark: Your eval job has completed with return code 0.
Innit !!
🥴 just use a classmethod really
^ 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
can you typehint lambda arguments?
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
What does the Callable do?
It is type hint
@trim tangle https://decorator-factory.github.io/typing-tips/antipatterns/optional-attributes/ isn't this fixable using overload? or is that also considered a bad thing because of how messy/complicated it can get?
although i am generally big in favor of converting "logic" into "data", it's probably one of the best things about functional programming style
You can't really overload a dataclass
For functions:
if you have a function with 5 boolean/optional values, there are potentially 32 combinations, and you need to specify them manually.
right, exactly. it's possible but prohibitive
indicates that the function accepts functions as params/args iirc
thanks
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
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
We need an ```py
if TYPE_CHECKER == 'mypy':
...
else:
...
It's an excerpt from https://github.com/python/mypy/blob/master/mypy/bogus_type.py
i mean this
what change occured?
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')
ah would've been good if I could do this:
class MyTypedDict(TypedDict, Mapping[str, Parent]):
...
but unfortunately cannot subclass TypedDict and another class
pyright now reports errors because fullname is actually FlexibleAlias[T, T]
which it correctly resolves now
MYPY = False
if MYPY: ...
should already let you do this
wouldnt you just do a: Parent, b: Parent etc?
aesthetically, no.
functionally, well, see #esoteric-python
Yeah I had forgotten about that but its not standardised across type checkers is it?
And the problem in my case is the way its typed internally in mypy so theres not much I can actually do
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)
hmmm, how does pyright generally handle descriptors?
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
It works without self.stack.enter_context(): ```python
class Test:
abc: RuntimeVar[str] = RuntimeVar()
def test(self, value: str) -> None:
self.abc = value
This passes.
I'll probably open a GitHub Discussion on PyRight's repository 🤔
it works in mypy, not sure why any other type checker would need to understand if MYPY? unless the question is how to special case something for some other type checker
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
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
its a type hint, a return type annotation
it means that the function is supposed to return an instance of the Colour class
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
🤔
the typehint just tells you that when you do y = x.color the type of y is supposed to be Colour
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?
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? 😆
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
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
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
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
We'd be talking miliseconds though I'd think unless the dict is massive
versus 100s of methods
But, I get it for the typings
well, get_colour could be properly typed too, str -> Colour
would that read as follows
@classmethod
def get_colour(cls: str) -> CT:
"""A factory method that returns a :class:`Colour` with a value of ``0x9b59b6``."""
# colorvalue = somelookupfunc(cls)
return colorvalue
?
!warn 922468559391698994 this is not the place to post chainmail
:incoming_envelope: :ok_hand: applied warning to @dense cedar.
Or well, instead of just asking here. Is there a good place for docs on typings like this?
Should I refer to pythons documentation?
no i meant sthn like
@classmethod
def get_colour(cls: Type[CT], colour_name: str) -> CT:
colour_value = lookup_colour_name(colour_name)
return colour_value
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
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
@brazen jolt what you want is called "higher kinded types"
and Python doesn't support them
oh
that's a shame
is there at least some pending PEP or some progress on making one?
no
or at least I'm not aware of one
The use case is pretty narrow in normal Python tbh.
I suppose that's true, still though, it'd be nice to have when you do need it
I guess I'll just go with this and every subclass will need to override it...
this is really annoying, since this already has 3 overloads like these, and all subclasses will need their own overloads
You can use protocols to somewhat emulate it, so l: Mapping[int, T] where T doesn't have a bound
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
@trim tangle is pyright playground's back end down?
hmmmmm
idk
I'll check it out in a few hours
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
@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')
no promisify?
ooh
I don't actually have to reinvent the wheel, nice
maybe I should read the docs and not outdated StackOverflow answers
node might gain tempfile support one day
Seems stalled https://github.com/nodejs/node/pull/33549
what's the pyright option to prevent using implicit Any as a type hint?
are you referring to [1, 'a', 3.4] being inferred to List[Union[int, str, float]] instead of List[Any]?
No
I mean like:
from untyped_package import SomeAny
def foo(v: SomeAny) -> None:
...
I cant seem to find anything about it on the configurations page, I know mypy had a option for it iirc
Yeah me too. Imho it's ok to use an unknown thing from an unknown source at runtime, but not in a type annotation
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.'
Right but that just fails the import. I want to allow the import but disallow use in a type hint
Does pyright not disallow this by default in strict mode?
Because that enables the reportUnknown* diagnostics
I don't think so
What are you using that would would be an implicit Any (with pyright) for type hints?
anything from an imported untyped library, potentially
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
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
Np ^^
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?
ah ok, thats what i have
Humm weird that it doesnt
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
hey, i totally got it working, i typehinted some othe variable by mistake... sigh
Ahh alight
this is excellent, thanks very much
Just a FYI, if in case of some major disaster Pyright Playground (https://github.com/decorator-factory/pyright-playground) goes down, anyone feel free to host it
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
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"
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
why do you need the cast?
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....
seems like Callable[[F], F] should be enough
Nope, Callable[[F], F] means a different thing
does it? it seems to work
for example, this is illegal:
Identity = Callable[[T], T]
why?
Or rather, it's a different thing
It is a generic type alias, not the type of a generic function
Yes, that's what I thought based on my experience in statically typed languages
ah right, the TypeVar scope is different
but mypy accepts it
e.g. Protocol is the only way to make this work: ```py
def apply_identity(f: Identity) -> tuple[str, int]:
return f("foo"), f(42)
there was a discussion about this on typing-sig a while ago; I think different type checkers use different TypeVar scopes here
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
In Haskell, you can do type Identity = forall a. a -> a ||If you enable rank 2 types||
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
i wouldn't expect protocols to support generic functions
the protocol can be generic, yes
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)
why would get be a generic function
the return type depends on the second argument
that's a very strange choice IMHO
definitely not one I would make even in the language supported it
get(k: str, val: T) -> V | T
yeah it's generic and overloaded
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
how would you suggest dict.get be typed?
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
that's because they don't have bare unions
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
what languages are these? do they support a two-argument version of get? e.g. TypeScript's Map.get just returns V | undefined
@oblique urchin It depends how precise of a match you want
if you want an example that matches up perfectly, Kotlin
other languages don't get to choose, do they?
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
I meant that they simply don't have Union
that's very different
sure
things: Sequence[int] = dict_of_lists.get("some_key", ())
^ this seems perfectly fine to me
my point is that if they wanted to do it, they could still do it
hm?
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
is there any way to type hint class attributes using Protocol ?
ClassVar should work
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
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
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
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
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
you may need to distinguish between "value is None" and "key is absent"
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
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 😔
I find it annoying that if I type an opening quote I end up with ""key"
Yeah I get that with GitHub Autopilot often, I don't make note of it as much with the autocomplete though.
single quotes are a crime
Not if I am the one using them 😮💨
That's weird, I haven't experienced that and I use single quotes
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 🤔
🤔
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
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 🤔
weird that mypy doesn't say it can't infer the type https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=0ef2dc191ac866bc6016aa5413ee6edc
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
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
if TYPE_CHECKING:
VerifyEntity = ...
else:
VerifyEntity = ...
Discord.py does sadly not support Annotated
😔 I see
Using Annotated is still not type safe -- you can put the wrong converter as the metadata
It is a hard problem I guess 🤷♂️
Yeah you can accidentally mess up
This channel is for discussing type annotations, now typing on a keyboard 🙂
good job though
this seems like garden-variety type inference for a statically typed language
i am really happy to see how popular monkeytype has become. the creator is a hard worker and the site is high quality
</ot>
In this case the two functions are concrete, I don't see why mypy wouldn't be able to figure that one out
Btw, the reverse pyright also complains which makes sense just the message is a little cryptic imo
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"
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
I guess typestates yeah? (Keep a typevar for each property)
Alternatively could probably do some funky union with literals and self-types
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?
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>)...
ah
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)
I'm excited for PEP 646 support which will likely make this much nicer :^)
(At least then I can append to the end and people can use **kwargs and not worry about pos-only args)
Yeah I'd say just have an alternate classmethod that has kwargs really.
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?
class BaseClass(Generic[T]):
@classmethod
async def build_object(cls) -> T:
instance = cls()
# do some async work
return instance.get_object()
def get_object() -> T:
raise NotImplementedError
class Subclass1(BaseClass[int]):
def get_object() -> int:
...
class Subclass2(BaseClass[str]):
def get_object() -> str:
...
thanks
Is this a correct typehint?
x: types.ModuleType = __import__("sys")
yes but dont do this
Oh yeah, was just curious 
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?
generic alias?
Generic TypeAlias?
pretty much. they are not supported in python, and in many languages
afaik? anyway
actually, apparently python does support it
til
yeah
and its only get worse :)
and you still can't even swap the order of the arguments 😩
RIP PEP 637
how would that work?
oh with kwargs
i thought you wanted Generic.reverse_args or something cursed like that
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
why can't you swap argument order?
What would that be?
it's a generic type alias with one argument, presumably
If you have a class like Foo[A, B], you can't have SomeAlias[int, str] evaluate to Foo[str, int]
python's notation for generics is pretty terrible unfortunately
right now it's Pair = tuple[T, T]
Ohh I didnt know that
How do you do it?
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
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
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
yep, I agree that this makes them harder to process
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
"hm... this is a strange type alias... oh, it's a generic type alias" - me, usually
The <>, no keyword approach in a lot of languages is quite nice, IMHO
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
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?
I don't want to keep it, but I also see the argument of it also feeling Pythonic. Previously, you can always trace variable back to a definition (excluding builtins).
For example, super() is a special proxy class, self is an actual argument (JavaScript this ://), etc.
maybe. i feel like SC has been pretty clear that they don't want things that make typed Python feel like a different language than regular Python.
it already does feel like a different language tho 
yeah, I recently had to refactor an untyped codebase and it feels like a different language
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)
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
I mean depends what you mean by a full solution
you can copy what typeshed puts in. do you really want fd support though?
Well the one that is in typeshed should be the full solution
presumably something like int | str | bytes | os.PathLike[str | bytes]
seems to be
_OpenFile = Union[StrOrBytesPath, int]
then
StrOrBytesPath = Union[str, bytes, PathLike[str], PathLike[bytes]] # stable
from os import PathLike
from typing import PathLikewould be nice
Are you annotating code that's already widely used
Or annotating new code
Because if this is new code
new code in my small project
Just annotate Path
we've talked about putting some of the _typeshed helpers in typing_extensions, that could be useful
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
yes, that feels more likely to be the result of a bug than anything else
And paths should always be represented by paths, not strings or bytes
unless you're writing really low-level code
Agree, when you can I'd annotate path parameters as Path to enforce that you're always representing paths as Path objects
thank you all
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
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
That would be nice if it was enforced but there are even typed libraries in which if you pass a Path object mypy complains because the author only annotated it as str
sure, those annotations should be fixed
i was talking more about writing an application that you're starting from scratch
Ah, yeah I agree that path should probably be used then
ah yes, the stringly typed system
when you're passing around stuff like list[Mapping[str, list[tuple[str, str]]]] which has some important business meaning
@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
wtf
I don't even have it open which is the weirdest part
A lot of activity on https://bugs.python.org/issue44863 all of a sudden
whats the default for a TypeVarTuple if its unbound?
is it *tuple[()] or *tuple[Any, ...]?
Should be the latter, but not sure if this was clearly defined
ah it's specified https://www.python.org/dev/peps/pep-0646/#behaviour-when-type-parameters-are-not-specified
yeah i goofed up in https://gist.github.com/Gobot1234/8c9bfe8eb88f5ad42bf69b6f118033a7#subscription, it didnt seem right
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.
Yeah, that doesn't seem to be the case 
Nothing much, all most other vars were hinted, thought it would satisfying to see this hinted too. Quite silly 
But hinting it with TypeVar itself not working got me pretty curious, that's why I asked
T: type works so there's that
Not really
pyright accepts it
mypy doesn't
interesting
although I have a bound kwarg, but I don't think it is playing a role
you shouldnt need to annotate a type var
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
interesting
(yeah as others have pointed out this is invalid under mypy: under mypy, a type can be constructed... TypeVar("T") can't be.)
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 😭
Are TypeVar constraints supposed to be invariant?
TypeVars with constrains can have any variance
but I'm not sure there's a useful behavior where they're not invariant
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
typevars with constraints can only be solved to their constraints
variance matters only if one of the constraints is a subtype of another
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
yeah that seems like a bug, is this mypy? mypy has some order-dependence issues with constrained TypeVars
Pyright
report a bug 🙂
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]] = [...]
Do you know about generic types?
A generic class is a class you can parametrize with another type, like list[int] or dict[str, bool]
I have a tutorial, but it's a bit raw
https://decorator-factory.github.io/typing-tips/tutorials/generics/
There is also the mypy documentation: https://mypy.readthedocs.io/en/stable/generics.html
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?
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)
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>)
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
What is list[T, int] supposed to mean?
ah
Why do you need to have the type at runtime though?
list[T, int] is not a valid type hint
So how do I get this?
you can't, use a tuple
Then its not a list
why do you need a list?
Our teacher said we have to
are type annotations a requirement?
yes
They said to just do this even though its wrong
For the purposes of this task
well... there's not much point in making a type annotation type checkers will reject 🙂
And this represents [the_added_value_of_type_T, pointer_to_next_item]
that's a strange linked list...
?
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
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
so you're using a normal linked list to store a linked list?
list isn't linked
Im using a 2d array to represent a linked list
sorry yes, a normal list to store a linked list
Thats what I'm doing yes
Just what we have to do for our exams
Ive got all the code working just want to sort out the typehints
So ignoring the list[T, int] thing not being valid is this correct?
why do you need _type?
are you checking it at runtime on each element?
what if you want a linked list of functions? like a list of Callable[[int], str]
I ensure the items being added correspond to the type
I just need to support base types and unions
Dont need to worry about functions and stuff
Also is it better to do for x in self or for x in self.__iter__()?
Okay
also, keep in mind that isinstance with Union works only since 3.10
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?
class Things(Generic[T]):
def __init__(self, ty: type[T]) -> None:
self._data: list[list[T]] = []
self._type = ty
that way, when you call things = Things(int), things will be inferred as Things[int]
(note that that will not accept Unions under mypy :^)
How does type hinting work.
you can check out the top pin 🙂
def foo(): # -> ?:
class Bar:
pass
return Bar
how do you annotate returned classes?
you can say -> type. However, dynamically created classes don't tend to work well with static type checking
you can use Protocols, you would need to define the methods and the types your generated class would have in the Protocol declaration. I can't imagine even Protocols will work if you are going crazy on the dynamic nature of things....
Dynamicslly created types don't sound very static 😅
macros plz
nevar
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? 🤔
What is that? 🤔
No it is properly structured
!docs typing.TypedDict
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...
You can make use of that then
When you access the key, the type checker will either complain the key doesnt exist or it will know its type exactly
Np
I never knew that!
This is certainly very very useful to know!
Ah! This solves many unrelated problems as well!!
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
dataclass on a dict?
Are you talking about this https://docs.python.org/3/library/dataclasses.html ?
Oh!
hmm
So my class would inherit from a dict and then I should proceed to properly type hint the attributes of that class?
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
omgI didnt see it gg
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
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
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)
what does the if TYPE_CHECKING do?
whats that stuff for
i wanna add it to look fancy
!d typing.TYPE_CHECKING
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.
i see...
what would the typehint be for a courotine func
!d typing.Callable
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").
!d collections.abc.Coroutine
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.
o
would this be used for typehinting?
You need both of these (except Callable from collections.abc)
Yes
These are the recommended classes to use now
CoroFunc = Union[collections.abc.Courotine, typing.Callable]?
Callable has a syntax
collections.abc.Callable[[parameter1, parameter2, parametern], collections.abc.Coroutine[return type]]
[[TParam, TParam, …], TReturn] iirc
okay
so
im returning a decorator
which just returns an asynchronous function
is it really that much of a pain in the ass?
You should probably be using type vars in a decorator
And you should also use import from for the types you want from collections.abc
!d typing.TypeVar
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...
why nottt
Cause you probably don't want to use constraints
It will only allow that generic to be the types in the constraint
The docs kinda suck atm I think there's a pr to improve them to point people to use the bound argument
sometimes you want them, sometimes you don’t lol
Which I think is what you want
so what do i do? 😩
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
So you return the function in place
No
?
return func
yes
Ok so this is a time that you want to use type vars
solid
Because they allow you to preserve info about func
OnT = TypeVar("OnT", bound=Callable[..., Coroutine]) is the type var definition you need
OnT?
probably Awaitable instead of Coroutine? I feel like there's little reason to use Coroutine most of the time
That's probably fair
what does the ... signify
takes any arguments
it's like Any but for the arguments to a callable
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
um
ok
i should probably work on my type-hinting aspect of python a bit more
If I say OnT is a coroutine function it's probably a bit easier to read (although slightly incorrect)
https://devblogs.microsoft.com/typescript/a-proposal-for-type-syntax-in-javascript/
so they're proposing to head in the direction of python+mypy?
what would the 'official' way of making a function for the purpose of doing nothing be?
define doing nothing?
if _name in EVENT_CONVERTERS:
data = EVENT_CONVERTERS[_name](_raw_data)
else:
data = _raw_data
if you put that inside a function, the function returns None
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
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)
ah redundant cast doesn't work for Protocol at all:
https://mypy-play.net/?mypy=latest&python=3.10&flags=strict%2Cwarn-redundant-casts&gist=bc9999020b0fb32610c0c3ec99a8568c
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
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
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"] == ...
Is this Mypy?
Pyright supports this (type narrowing of typed dicts)
it's pyright 🙁
ot, but when did the peps website change
ah alright, thanks
yesterday, PEP 676 was just implemented
yes you can, I put it there 😄
sounds like a bug
It's tricky to search for bugs with flags because everyone posts their config so there's loads of issues with warn_redundant_casts in
I don't remember seeing it before and I have been following the mypy repo for a while. I might have forgotten though
To Reproduce https://mypy-play.net/?mypy=latest&python=3.10&flags=strict%2Cwarn-redundant-casts&gist=bc9999020b0fb32610c0c3ec99a8568c from typing import Protocol, cast class...
Even the simplest redundant cast isn't detected @oblique urchin https://mypy-play.net/?mypy=latest&python=3.10&flags=strict%2Cwarn-redundant-casts&gist=c166276e9623ced551d45e7a74d31510
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
I guess it only warns if the type matches exactly? That seems kind of justifiable because casting to a subtype may not be redundant due to variance
It wouldn't be a subtype if variance didn't allow it
eg if you could use the hypothetical safe_cast in that position then it would be redundant
The implementation of warn redundant cast should be equivalent to removing the cast and running the type checker again imho
There are some subtle differences between cast and a type hint https://github.com/microsoft/pyright/issues/2757
At least for pyright a type hint can be narrowed where a cast will not
re.Pattern seems to be Generic, but what do the type arguments correspond to?
Bytes or Str
Which corresponds to what it'll be matched against?
yes
👍 Thanks!
It seems like it, although it looks like they would allow you to write any garbage you wanted as a type comment, which in python even with PEP 563 that is not the case
wdym
seems like they haven't really finalized the syntax yet: https://github.com/giltayar/proposal-types-as-comments/#kinds-of-types
It seems that you would have more freedom then you do in python hints
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
That doesn't sound too different from PEP 563 to be honest. Though I guess even with PEP 563 you can get types from __annotations__
How would this work from a parser perspective...? I mean, if any kind of syntax is allowed in a typehint then when does the parser understand that the function has begun?
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
there's some handwavy text about having syntax like < ... > where it just eats anything until the closing tag
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
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
The first one is right
ohk, thanks
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
Optional[MyClass]
While implicit optionals are pretty cool they're generally discouraged from what I've seen
The value would always be either None or the custom class.
Oh, then yeah
In that case, Union[MyClass, None]. Or Optional[MyClass] which is exactly the same
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
You could make a property or method that raises an exception if something is not None
If they are always set when accessed I would just annotate it in the class body
...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 🤷
Thanks guys! I was just wondering since my IDE was complaining, and I was wondering if there was a better way.
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)
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.
Cool, thanks again!
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.
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
I have a class that starts with Bones for attrs and gets filled from a log file it follows, and mostly gave up on the typing around it, wanted to add some asserts but after maybe 30 asserts that added basically nothing I reverted it back
not unique to python, for sure
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
ahh that's a nice idea
In Kotlin you'd be able to just write !!, in C++ you'd write .value();, in Rust you'd write .unwrap(), etc
that reminds me of how you'd work with Maybe in haskell
that honestly doesn't seem so bad as a helper function
ill probably keep using assert
Kotlin also has some stuff specifically around late init, to help
so you wouldn't typically actually annotate it as Optional/nullable
i see, that's useful
Builders I think?
well, builders are kinda non-type-safe
a builder is overclocked multi-step initialization
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?
Can you show the code?
mypy is telling you that the object you're trying to access on could be None
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
I think hasattr won't work with type checkers
ah, maybe the latter in that case
Is it possible to override a method where a parameter's type changes to a subclass of the original?
@clever bridge what do you mean?
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:
...
Pyright is correct. This violates Liskov's Substitution Principle. In other words, any function that works with C must also work with D.
def do_something_with(c: C) -> None:
a = A()
c.func(a)
do_something_with(D()) # type checks fine, but breaks at runtime
Can you show a more realistic example? Why do you need this?
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
# 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:
...
Then maybe you need a generic class?
Probably
what is in options=[...]?
Whatever the user provides as the options to the command
Something like
[Option(name="first"), Option(name="second")]
ah
Probably a Generic is best
so the user has to make sure MyOptions and the options list are synced?
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. 
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
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
@clever bridge You can just make one typevar per module, unless you need different bounds or variances
but yeah it is... extremely verbose
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
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
I'm just going to take the easy way out and make it options: Any
Oh wait I misread.
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
bumping this since it got covered
reproducible via pip install py-cord~=2.0.0b5 mypy && mypy --namespace-packages test.py
test.py: ```py
from discord.ext import commands
Has anyone requested pyright to report an error if a comparison is discarded? e.g. ```py
response.status_code == 400
return response
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
Yeah, pyright reports an error when you discard a coroutine without awaiting it which is a similar issue so maybe
That is weird that it complains about that
Doesnt seem to be a job for the type checker
I'm guessing the reasoning is that there is no case where you would ever want to do that intentionally
I am sure that are tons of things that have to practical use and yet pyright doesnt complain
this is difficult to do in a normal linter
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
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?
probably some, but to do this thing in full generality you need type inference
I guess you would have to go through imports
Yeah thinking about it more it would be quite hard
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.
yeah Rust has this, both for a type and an individual function
you can ignore this, but in that case you need to explicitly do let _ = function_with_very_important_result()
can you reuse TypeVars in unrelated functions?
Yes
when do you use type: ignore when returning an inner func for a decorator?
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?
meh im trying to look fancy
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?
you would just use Callable[..., Coroutine[Any, Any, Any]] there, as the error says, the typevar doesn't have any meaning there
wtf even is a typevar 😢
hmmm i see
type ignore is the opposite of fancy :P
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?
because flake8 doesnt like the spaces
its not an enum
just some variables in a class
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
its one or the other? o
well its just redundant
er
flake8 has some other checks beyond formatting, but yeah I'd recommend turning off any formatting-related flake8 errors if you're running Black
how?
config file
is it an arg when running flake8?
you'd have to read the docs for details, but normally it's a .flake8 file at the root of your project
Or you could look at the docs xD
?
this is different
no
this is just me trying to name my func
prob wrong channel actually
bumping for probably the last time
you probably have a from . import __init__ somewhere?
that's probably making mypy confused
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?
Not easily. You can make the class Generic over the attribute and then use TypeGuards, but that's not going to be pretty. Ideally you'd refactor to avoid this pattern
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.
Does sound like something you would Generic for
I might just end up the route of asserts
Or some sort of subclass mixin of some kind defined in if TYPE_CHECKING blocks
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
something similar to that (that's the generic method they were talking about)
you'd be able to typeguard
Ahh
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
Would be nice to not have Generics everywhere, but it's getting less and less likely that's going to work out
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
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.
only if you use generics
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: ...
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]])
a type var is not a type alias
In this case, just use Callable[..., Coroutine]]
You are not using any typevarness here. As in, you're not linking any two types together
Maybe this can help https://decorator-factory.github.io/typing-tips/pitfalls/typevar-typealias-newtype/ 🔌
i see...thanks
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
it would be nice if they were logical subclasses of Callable
k
I think we've mostly just avoided using those in typing
Yeah but there are cases where I think it's useful , especially for typing completeness
It would be really useful in case of decorating methods
because a Callable isn't necessarily a descriptor
I have no idea how to do this properly in pyright, actually.
hmmmm
Slightly confusing to call an async function a coro
why?
it's a function that returns a coro when called, it itself is not a coro
its literally a coroutine

