#type-hinting
1 messages Β· Page 56 of 1
hm how come?
it doesn't do anything unless the program wants to look at it
yep, that's sometimes useful
and when does the program want to look at it?
when there's code that wants to do that, and afaik there's no stdlib that does look at typehinting
wait---
yes there is
!d dataclasses.dataclass
@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False)```
This function is a [decorator](https://docs.python.org/3/glossary.html#term-decorator) that is used to add generated [special method](https://docs.python.org/3/glossary.html#term-special-method)s to classes, as described below.
The [`dataclass()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass "dataclasses.dataclass") decorator examines the class to find `field`s. A `field` is defined as a class variable that has a [type annotation](https://docs.python.org/3/glossary.html#term-variable-annotation). With two exceptions described below, nothing in [`dataclass()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass "dataclasses.dataclass") examines the type specified in the variable annotation.
The order of the fields in all of the generated methods is the order in which they appear in the class definition.
okay now we're way beyond me knowledge in python xD
is your question "who wants to inspect type annotations?" @rustic gull
kind of, yes
(except for InitVar and ClassVar)
tldr libraries that do something based on the code's typehints
OR type-checkers π
A type checker just looks at your code, considers the annotations (statically, without running anything), and then tell you about the errors you've got. Or shows you what type something is. Kind of like a code reviewer.
oh right
Alr thanks
Last question, might not be considered in this topic but idk what it is, maybe it is a type π€· but i've seen people use yield, what is that?
is there a docs command i can run for that object?
!d yield
7.7. The yield statement
yield_stmt ::= yield_expression
``` A [`yield`](https://docs.python.org/3/reference/simple_stmts.html#yield) statement is semantically equivalent to a [yield expression](https://docs.python.org/3/reference/expressions.html#yieldexpr). The yield statement can be used to omit the parentheses that would otherwise be required in the equivalent yield expression statement. For example, the yield statements
```py
yield <expr>
yield from <expr>
``` are equivalent to the yield expression statements...
okay nvm i wont understand that xD, thanks guys!
For sure π
once you get into testing... and perhaps use pytest
Thank you
I actually need to get better at generators
I know how generators works
and iterators, iterables etc...
never crossed the field with yields though
nice, that's a lot better than me π¬
I doubt that xD π
er well
I made a class to implement a function I wrote to be a generator as __next__
then I learned that iter() exists
!d iter
iter(object[, sentinel])```
Return an [iterator](https://docs.python.org/3/glossary.html#term-iterator) object. The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, *object* must be a collection object which supports the [iterable](https://docs.python.org/3/glossary.html#term-iterable) protocol (the `__iter__()` method), or it must support the sequence protocol (the `__getitem__()` method with integer arguments starting at `0`). If it does not support either of those protocols, [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError "TypeError") is raised. If the second argument, *sentinel*, is given, then *object* must be a callable object. The iterator created in this case will call *object* with no arguments for each call to its [`__next__()`](https://docs.python.org/3/library/stdtypes.html#iterator.__next__ "iterator.__next__") method; if the value returned is equal to *sentinel*, [`StopIteration`](https://docs.python.org/3/library/exceptions.html#StopIteration "StopIteration") will be raised, otherwise the value will be returned.
the only thing i know @little hare about these are:
iterators are iterable
iterable can make iterators
list comp is a expression that leaves you with a iterable
generators is a expression that leaves you with a generator
a generator is a iterator, which is iterable
and the list is iterable, which means it can make a iterator
if that makes sense for you, idk xD I usually note stuff
ah lol
so basically: ```py
NOTES
#iterators are iterable
#iterables can make iterators
listcomp = [i for i in range(10)] #the expression gives you a iterable
#since it's iterable we can do:
for i in listcomp:
print(i)
#since we can make iterables a iterator we can do:
iter(listcomp)
print(next(listcomp))
generator = (i for i in range(10)) #the expression gives you a iterator, which is iterable
#since it's a iterator we can do:
print(next(generator))
#since iterators are also iterable we can do:
for i in generator:
print(i)
This is what I understood how generators works so π€·@little hare
Maybe it isn't 100% accurate but
list comps give you lists
and you can't do next(listcomp) since it's still a list, iter doesn't modify it
!e ```py
listcomp = [i for i in range(10)] #the expression gives you a iterable
#since we can make iterables a iterator we can do:
iter(listcomp)
print(next(listcomp))
@little hare :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | TypeError: 'list' object is not an iterator
!e ```py
listcomp = [i for i in range(10)] #the expression gives you a iterable
#since we can make iterables a iterator we can do:
print(next(iter(listcomp)))
@little hare :white_check_mark: Your eval job has completed with return code 0.
0
@rustic gull ^
Ah yeah sorry my bad
I'd like some feedback for my tutorial on TypeVars π
https://dev.to/decorator_factory/typevars-explained-hmo
and their subclases, of course
π©
so, shouldnt intellisense pick up that the type of records here is List[Record] or am i thinking that intellisense is smarter than it actually is
def create_records(raw_source: List[str]) -> List[Record]:
records = []
for line in raw_source:
date, name = line.split("\t")
records.append((date, name))
return records
other than that, seems alright, although you should probably get someone who doesn't know anything about type variables to read it
seconded
thirded
small nitpick, why is this lowercased
π€·
but this one isnt
stupidsense
what IDE is that?
vscode
ah, it does do that
goshdarn stupidsense
maybe it's another microoptimization π
don't want to waste those precious CPU cycles on unifying the names
the one case gives it a value of [] where the other one is a func param
drowning in them microoptimizations π₯΅
so in one it is confirmed to be type list whereas the other one is just a typehint of List
have you tried reading pyright's code?
it is cursed
tbh the cause doesnt matter to me, do you know how i could normalize this?
its bothering me
one or the other, doesnt matter
file a bug on the repo
Speaking of cursed...
def create_records(raw_source: List[str]) -> List[Record]:
def _gen():
for line in raw_source:
date, name = line.split("\t")
yield date, name
return list(_gen())
this should typecheck alright π
use 3.9+
this is 3.9.7
then use list[str]
i could i guess, no one in the office is using <3.9
if you use list[] then its not a problem
alright i'll do just that then
actually I blame dev.to
it has a terrible editor
and its markdown interprets single line breaks as actual line breaks π€¦
which is cursed because I was first writing the markdown in VSCode
i m in py3.10 but mypy keeps givin me tiz error: "tuple" is not subscriptable
here's the code: alias = tuple[int, int]
isn't that supposed to be fixed by now
ya gg
lemme check it's version
oh, i used the wrong mypy
wat does tiz mypy error mean then Type application has too many types (1 expected)?
code: alias = MyClass | tuple[Optional[int], Optional[int]] using py3.10 btw
the latest release of mypy is still pretty buggy with lowercase tuple
Also seems odd to mix the old and new syntax
It's MyClass | tuple[int | None, int | None]
oh okayy
how should i type hint any class?
wdym by 'any class'?
Like your_function(str)?
or ```py
class Foo:
pass
your_function(Foo)
ya kinda like tat
well, str is a class as well
The type of a type is type
so you'd do ```py
def your_function(cls: type):
...
If you want to require only classes that are subclasses of some other class (or something like Mapping[int, str]), wrap it in typing.Type[MyClass].
Traceback (most recent call last):
File "C:/Users/drago/PycharmProjects/playground2/main.py", line 73, in <module>
a: list[int] = [1]
TypeError: 'type' object is not subscriptable
```umm.... I got 3.9 tho?
If you've got that error then you're not using 3.9
What output does this give?py import sys print(sys.version)
Then you're using 3.8.10 :p
You just need to change what you're running
You need to configure PyCharm to use 3.9 instead of 3.8
You've still got it configured to 3.8
It says so at the bottom of that image
< Python 3.8 (playground2) >
3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)]
maybe restart
wait
this is weird
wait, isn't configuring the interpreter enough?
or...
I need to do something else
You have to configure the interpreter and then actually select it
Click the thing the arrow is pointing to and select the 3.9 interpreter @wicked scarab
I did
That's the one for the DiscordBot project
huh? Can't I use the same interpreter for different project?
You shouldn't.
I dont have 3.9...
Then you've not added the interpreter
No worries lol
PyCharm version control is very confusing to people who aren't familiar with it
yeah indeed
It took me ages to get used to it (and I still struggle sometimes)
thanks anyway
So is it all working now?
yeah
Nice π
How should I type-hint a variable that can hold any instance of any class
Also, is it correct if I type-hint a variable that can hold any class (but not the instance) as owner_cls: type?
object or typing.Any, depending on how exactly it's used
well, I'm trying to type-hint a descriptor class
so specifically, these methods:
def __set_name__(self, owner_cls: type, name: str) -> None:
...
def __get__(self, instance, owner_cls) -> Any:
...
def __set__(self, instance, value) -> None:
...
def __delete__(self, instance) -> None:
...
class typing.Protocol(Generic)```
Base class for protocol classes. Protocol classes are defined like this:
```py
class Proto(Protocol):
def meth(self) -> int:
...
``` Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example...
why? I mean, what's the advantage of having a protocol like that if I can just define them in my class directly?
so does typing.Any make sense in this use-case? also is owner_cls: type a proper way to decorate the non-initialized class or should it be something else?
What do you mean by
if I can just define them in my class directly
class Descriptor:
def __set_name__(self, owner_cls: type, name: str) -> None:
self.name = name
self.cls = owner_cls
def __get__(self, instance: Any, owner_cls: type) -> Any:
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance: Any, value: Any) -> None:
instance.__dict__[self.name] = value
def __delete__(self, instance: Any) -> None:
del instance.__dict__[self.name]
I can just do this, why would I want a protocol that defines which methods should be present in a descriptor?
all my descriptors will inherit from this class
You would have the typehint as Descriptor
Yes
I just can't see what's the advantage of a protocol
I wasn't aware that all descriptors would be a subtype of a specific class
Also you can make the signature for __get__ more specific instead of returning Any (using overloads)
isn't protocol only really useful if you need to do something like Protocol[int] which is a type-variable that is shared across multiple methods
but I don't think I need something like that for descriptors
yeah, in my child descriptors, I do this: ```py
def get(self, instance: Any, owner_cls: type) -> float:
"""Implement Coordinate getter."""
ret = super().get(instance, owner_cls)
return cast(float, ret) # This will be float, since it's imposed in setter
but I don't see how overloads in the main descriptor class would make sense here
though I could do this py def __get__(self, instance: T, owner_cls: type[T]) -> Any: ... since the __get__ is invoked just by doing: __get__(a, type(a)) (which makes me wonder why is it even there but I suppose it's just for legacy reasons to ensure backwards compatibility)
Yeah overloads won't be necessary
hmm, now that I'm thinking about it it may actually make sense to make it into a generic though
from typing import Any, TypeVar, Generic
T = TypeVar("T")
class Descriptor(Generic[T]):
def __set_name__(self, owner_cls: type[T], name: str) -> None:
self.name = name
self.cls = owner_cls
def __get__(self, instance: T, owner_cls: type[T]) -> Any:
if instance is None:
return self
return instance.__dict__.get(self.name)
def __set__(self, instance: T, value: Any) -> None:
instance.__dict__[self.name] = value
def __delete__(self, instance: T) -> None:
del instance.__dict__[self.name]
since when a descriptor is created in a Foo class, I could then do this ```py
class Foo:
x: Descriptor["Foo"] = Descriptor()
If you aren't using it as a return type for __get__ it wouldn't have an effect
?
return type for get is any
it's what the user puts into the descriptor, I can't control that
Yes so having instance be Any and owner_cls be type has the same effect; it doesn't need to be generic
f = Foo()
f.x = "s" # now ret type for __get__ is str
f.x = 2 # now it's int
...
I suppose since the user isn't calling those it's true, but it's not a mistake and it kind of makes sense to still do it like this imo
it will mean that whenever the instance passed into something like __set__ will be of certain type, we can always know that the instance passed to __get__ will also be of that type, same for delete
and we can know that it's the same instance just by looking over the class immediately
if it were Any and type, it could mean that those differ from call to call, but that's not true since once it's initialized, these will always be the same
Not really, it's static type checking. Say you have this one protocol, that say that the types need a fetch() method.
class Fetchable(Protocol):
def fetch(self) -> None: ...
Now you can define these classes that implement the protocol without subclassing Fetchable.
class Monkey:
def fetch(self) -> None:
... # Do something
Instances of Monkey can now be passed to parameters and attributes annotated as Fetchable
All of those Sequence, Iterable, etc. type of types can be implemented as protocols (I am not 100% sure if they truly are).

hmm?
they are ABCs
because they provide some utility methods afaik
oh, that's interesting, so if I define a Fetchable protocol like you did, would something like issubclass(Moneky, Fetchable) or isinstance(Monkey(), Fetchable) return True or is it purely for type-hinting? Also, while this is really cool to know, I still don't see why protocols would be useful in my case, since I do actually want to be able to use or subclass that descriptor class
only if you use @runtime_checkable
Also a more general response to what you were talking about earlier: avoid Any if at all possible, because it basically turns off the type system, so if you use it, you don't get the benefits of static type checking
instead, look for some way to give a safe type, perhaps a Protocol or object
I really want a tool that replaces redundant Any with object
I only recently learned that I should be using object if I really don't care about the object type, not Any
it's not obvious
really? but what's the difference? when is Any not an object? That's super non-obvious
Any is a subtype of any other type, and any type is a subtype of object.
Any type is a subtype ofobject, but object is only a subtype of itself.
run that by me again?
So this passes @brazen jolt @little hare : py def f(x: Any) -> None: print(x + "!") and this fails: ```py
def f(x: object) -> None:
print(x + "!")
in other words, you can do anything with Any, but you can't do much with object.
oh even when that interaction on x would be invalid
Any disables the type checking around it, object only allows what's valid on a plain object
yep
that's interesting
!e print(str(object()))
@little hare :white_check_mark: Your eval job has completed with return code 0.
<object object at 0x7f322eeb4130>
You can still do type narrowing on object though: ```py
def f(x: object) -> None:
if isinstance(x, str):
print(x + "!")
interesting
can't you also do this with Any
typing.Any```
Special type indicating an unconstrained type.
β’ Every type is compatible with [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any").
β’ [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any") is compatible with every type.
You can.
But with Any, you are just allowed to cast it implicitly to any type
but you can also cast an object to any other type, can't you? ```py
def f(x: object) -> int:
return cast(int, x)
Yes, but this is an explicit cast
wow
by 'implicit cast' I meant just not checking anything
Lib/typing.py lines 412 to 424
@_SpecialForm
def Any(self, parameters):
"""Special type indicating an unconstrained type.
- Any is compatible with every type.
- Any assumed to have all methods.
- All values assumed to be instances of Any.
Note that all the above statements are true from the point of view of
static type checkers. At runtime, Any should not be used with instance
or class checks.
"""
raise TypeError(f"{self} is not subscriptable")```
ah, I see
all types are an Any, is the point. you can apply it to literally Anything
no no the point is the source for Any is identical to NoReturn and others
though I'm not sure I like object more than Any in that case, since with Any, I can directly cast it to something else when I know what will be returned and work with that, and when i don't know, it won't give me any errors, whereas with an object, it would be failing when I didn't cast it explicitly, making it a pain to work with for the user. If each time you'd do self.x when x is a descriptor, you'd also have to specify a type, I feel like that would just get way too distracting
the docstring is the same? π€
Lib/typing.py lines 426 to 439
@_SpecialForm
def NoReturn(self, parameters):
"""Special type indicating functions that never return.
Example::
from typing import NoReturn
def stop() -> NoReturn:
raise Exception('no way')
This type is invalid in other positions, e.g., `β`βList[NoReturn]`β`β
will fail in static type checkers.
"""
raise TypeError(f"{self} is not subscriptable")```
yeah, you can't subscript it
but how does it work, internally
although I can see why it could be useful
does it even have some implementation like this, isn't that just up to type checkers, such as mypy?
I don't think there's really anything Any would need to implement on it's own, the type-checking functionality isn't present in python directly, that's up to an external static type checker tool, there's pretty much no significance to type-hints in pure python, it will run even if all of those type-hints are completely wrong
at most the ability to do isinstance(x, Any), but python doesn't implement that, there are external tools that can check these type-hints on run-time with a decorator, but direct runtime checks aren't implemented with Any at all afaik
because everything is an Any
yeah, but I meant it more generally, Any is just one such type
true
Yeah, type checkers just special-case Any
It's annoying you can't do def get(self, v: V) -> K | NoReturn[KeyError]:
that would be cool though NoReturn wouldn't be the type-hint to use, since it can also be used in cases like these: py def foo() -> NoReturn: while True: pass so NoReturn taking exception as an argument is a bit weird, perhaps if there were something like Raises
But there it would just be un-annotated.
yes, that is a possibility, but it's still a bit weird imo
hinting exceptions is a bit weird as there are some that can be raised anywhere as normal behaviour and then there's also an API that'd allow you to do that for any exception
if we could pass exceptions into NoReturn there should also then be a way to pass in an infinite loop, since just doing NoReturn could then mean any exception or function hangs
that's a good point
You just add the | NoReturn[KeyboardInterrupt] to every def
Generators would need a throw type
maybe its time to work on var args for type annotations like generator and coroutine
It you could just use the SendType | NoReturn[CancelledError]
Does pyright have a setting to error if a type hint is not present? (Mandate them, basically)
Or another tool like flake8, perhaps
Thanks!
The strict option probably?
I shall try, thank you!
strict does a more than just enforce type hints (in fact, I'm not even sure if it does enforce them). flake8-annotations will most likely be a better option as it gives you the ability to ignore specific things, such as annotations for special variables, such as self, or cls and just gradually fine-tune everything, either with global flake8 exception ignores or with individual noqa comments. But yeah, certainly try out both (and if you don't mind, let me know whether strict mode does actually enforce type hints, since I don't really think it does, but I may be wrong)
the general way to think of strict is that it always imposes stricter types, if it can, for example: py lst = [1, 8, "foo"] will not be of type list[Any], but rather of type: list[Union[int, str]
@green gale ^^
I'll try out both and get back to you, thanks!
there is the reportMissingParameterType (strict does turn this on)
reportMissingParameterType [boolean or string, optional]: Generate or suppress diagnostics for input parameters for functions or methods that are missing a type annotation. The 'self' and 'cls' parameters used within methods are exempt from this check. The default value for this setting is 'none'.
ive been confused every time i read this example in the pep
def async_query(on_success: Callable[[int], None],
on_error: Callable[[int, Exception], None]) -> None:
does it mean Exception class and subtypes like?
>>> def foo(x: Exception): pass
...
>>> foo(Exception)
Exception and a subtype of Exception like IndexError are both the type metaclass so that doesnt make sense
it means an instance of Exception not the type Exception
Exception() (or subclasses) should type check Exception should not
if you wanted to check for a type directly, you would want to do Type[Exception]
What's incompatible with these signatures?
@t.overload
def value(
self,
categories: collections.abc.Iterable[str],
key: str,
*,
default: t.Any = _DEFAULT_SENTINEL,
sync_on_missing: bool = False,
) -> TOMLType:
...
@t.overload
def value(
self, key: str, *, default: t.Any = _DEFAULT_SENTINEL, sync_on_missing: bool = False
) -> TOMLType:
...
def value(
self,
categories_or_key: t.Union[collections.abc.Iterable[str], str, object] = _MISSING_SENTINEL,
key: t.Union[object, str] = _MISSING_SENTINEL,
*,
default: t.Any = _DEFAULT_SENTINEL,
sync_on_missing: bool = False,
categories: t.Union[collections.abc.Iterable[str], object] = _MISSING_SENTINEL,
) -> TOMLType:
Is it the names? I thought I had it working with differing names before as long as it was usable in the same manner but I can't get it now
the more succinct version would be
@t.overload
def f(a: int, b: str):
...
@t.overload
def f(b: str):
...
def f(a_or_b: t.Union[str, int], b: str = _MISSING_SENTINEL, *, a: int = _MISSING_SENTINEL):
...
The names are important yes, since you could do f(a="blah"). You could use the / positional-only marker, or fix the names.
shouldn't that be covered by the kwarg only?
No, the args on the right are keyword only, but the ones to the left are still positional or keyword.
You can call value(categories=x), and that should work by your sig but it doesn't.
should it? If only categories is passed in then key will be missing
the actual implementation would work with it, but that can be taken care of in the body
Ah yeah you need key too, but I mean it can be passed by keyword, but then categories_or_key isn't defined for your implementation?
Ah, misread slightly for the categories but.
But value(key=x) is valid for sig 2, but your implementation wouldn't take that.
It should work, as they all have defaults
but anyway, is there any way to get it working in some way like I had it, or do I either have to use nonsensical names (key when it's actually a category etc.), or positional/kwarg only ?
What I normally do is use the nonsensical names yeah, then on the first lines swap the values around to the right vars, or reassign them into say key_ and use that thereafter.
hmm, that sucks, as it also muddies it a bit for the user when they're in the overloaded signatures
@void panther I think the central issue is that categories will map to different parameters depending on whether it's is positional on keyword.
so the type checker thinks this is invalid
is there a better way to typehint the sentinels I used above than just ... | object or creating an enum for it ?
Not yet, PEP 661 is hoping to fix that
What Iβve done a few times is cast them to the non-sentinel type or Any, so theyβre just allowed. Though then you need to ensure you do the is-checks, since the type checker wonβt catch it anymore.
id just annotate it as Any as well, im assuming you dont want people to actually pass these as values
ah that'll probably be a bit better, thanks
I tend to use an Enum with only one item
Although sometimes you need a bunch of sentinels and they can all go in the same Enum
I usually do py class _Unset: pass _UNSET = _Unset() and then do Union[actual_type, _Unset] and then use isinstance to narrow
I used the enum approach previously but it feels a bit weird
You can use Literal[...] with Enums
I wrote something like this recently:
class _Missing:
pass
Missing = _Missing()
value: Union[SomeType, None, _Missing] = mapping.get(key, Missing)
didn't like it but did it anyway π€·
you are missing the Union part of that
thanks
it probably cant be None would be my guess?
the mapping can contain None as a valid value
I annotated it purely for developer convenience; we're not even using a type checker
I really enjoy being able to do if value is Missing:
really I think most of the weirdness in the annotation comes from None being special-cased
π€·
from typing import Union
class _Missing:
pass
Missing = _Missing()
mapping: dict[str, int | None] = {}
key = ""
value = mapping.get(key, Missing)
reveal_type(value)```this works for me with pyright
No pyproject.toml file found.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/home/runner/PyrightPlayground/pyright.py
/home/runner/PyrightPlayground/pyright.py:11:13 - info: Type of "value" is "int | _Missing | None"
0 errors, 0 warnings, 1 info
Completed in 7.447sec```
π
so did the old version
difflib.py:41: error: Module "typing_extensions" has no attribute "GenericAlias" 
Is this due to fact the name definition is guarded in typing_extensions's source by an indirect version check?
Can you add this hack to your message please? π
would you mind updating the pyright version?
last time i checked it wasnt very up to date
@soft matrix done
updated to 1.1.190
I also made it create random files so that two people can use it at the same time
:)
maybe I should make a proper service tomorrow
I asked if I can register a domain name with 'pyright' in it
if not, I'll just put it somewhere else
one think I would like is to see the errors marked in the file, like with the mypy playground
(the json output mode should help with that π https://github.com/microsoft/pyright/blob/main/docs/command-line.md#json-output)
a proper playground should be fun
I wonder if it is possible to do it without creating temp files
Maybe I could display mypy & pyright & something else side by side
why not?
right, it should not be to hard
pyright doesnt support passing text as input atm afaik
that would be super cool
Btw, is there a way to speed up pyright? it takes, like, 5-8 seconds to run.
maybe ask eric nicely π₯Ί
Maybe try in watch mode?
that sounds like a decent idea
its probably a case of replit machines not being very powerful
i was wondering whether there was something like mypy's playground for pyright, nice
why mypy gives no error?
!e print(isinstance(True, int))
@fierce ridge :white_check_mark: Your eval job has completed with return code 0.
True
interesting
booleans are subclasses of ints
pyright gives x possibly unbound
thats not the issue here
I thought mypy detected those too
its that x is not defined for the False case
apparently, it does not
but I don't see how True/False being int is relevant here, since it's not the bool parameter that gets returned it's the unbound variable x
i think they just misread the snippet
it's really surprising to me that mypy didn't find this though, I usually use pyright so I didn't even realize this wasn't a thing in mypy, but I guess it just isn't (perhaps there's some way to turn it on?) Apparently, it just sees that x was at some point set to an int, so it carries that type definition and just works with x as being int without any checks that ensure that it was even set before it's used
is it possible to create some object x, such that annotation var: x = x would be correct?
i want to create singleton for which you do not need to use two names for annotation
example:```py
from ... import sentinel
def f(x: int | sentinel = sentinel) -> int:
if x is sentinel:
return 0
return x
but now i must use two names: sentinel and SentinelType, which is annoying
no
I don't think you can, apart from maybe: x: type = type since type is of type type (type(type) is type)
but that's only because type has quite a unique implementation in python source itself I don't believe this is replicable with any new class with pure-python code
apparently x: None = None also works
oh maybe theoretically you could do something like
class sentinal(type):
def __new__(mcs, name) -> object: return super().__new__(mcs, name, (), {})()
@property
def __class__(cls): return cls
if TYPE_CHECKING:
__class__: sentinal
type checkers will likely complain anyway since they won't run that code
if you had active type checking, i.e. some implementation that checked types at runtime, perhaps this would be ok, but we usually implement add typing so that it can be checked statically
this can be statically type checker idk what you mean
there are no annotations, so type checker will not check this code
this would be what you'd actually need to pass mypy even on the definition of such class: ```py
from typing import TYPE_CHECKING
class sentinel(type):
def new(cls, name):
return super().new(cls, name, (), {})()
@property
def __class__(cls): # type: ignore # mypy doesn't like this being obscured later
return cls
# mypy doesn't like read-write __dict__ attribute being
# overridden by read-only property, so we need a setter
# to suppress that warning
@__class__.setter
def __class__(cls, _): # type: ignore # mypy doesn't like this being obscured later
pass
if TYPE_CHECKING:
__class__: "sentinel" # type: ignore # mypy doesn't like this already being defined
and even with all this py s = sentinel("s") x: s = s still fails mypy with: ```
error: Variable "file.s" is not valid as a type
so yeah, you can't really do it
pyright also fails with: ```
error: Expression of type "Type[__class_sentinel]" cannot be assigned to declared type "__class_sentinel"
Β Β "Type[sentinel]" is incompatible with "Type[__class_sentinel]" (reportGeneralTypeIssues)
class sentinel:
sentinel: ClassVar[sentinel]
sentinel.sentinel = sentinel # type: ignore[assignment]
def test1(x: sentinel = sentinel.sentinel) -> sentinel:
return x
def test2(x: int | sentinel = sentinel.sentinel) -> int:
if x is sentinel:
return 0
assert isinstance(x, int)
return x
sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel sentinel
it works at type-check time and runtime
then again, sentinel.sentinel isn't just sentinel this would still be failing: ```py
s: sentinel = sentinel
yea, but this solves my issue
please don't actually use this
.bm
saw this and was like, i will not do that π
it fails when compiled with mypyc (
well, problem with the above code is that even though it should logically work, it actually won't because type checkers will just not accept s as the type like this, but yeah even just attempting anything like this is kind of crazy
I'm not at all surprised, doing this is very hacky and weird
what's so wrong with SentinelType? lol
it is additional name to import
I mean, doing what you're doing is completely counter-intuitive and if it's a library, users will have no idea how to type-hint your sentinels at all
are you on python >=3.9?
hm
use the class directly then
class Sentinel:
pass
def foo(x: type[Sentinel] = Sentinel) -> type[Sentinel]:
return x
this one should work
!pep 661
!e
class _sentinel_meta(type):
def __repr__(cls) -> str:
return '<sentinel>'
class sentinel(metaclass=_sentinel_meta):
pass
def test1(x: type[sentinel] = sentinel) -> type[sentinel]:
return x
def test2(x: int | type[sentinel] = sentinel) -> int:
if x is sentinel:
return 0
assert isinstance(x, int)
return x
print(f'{sentinel = }')
print(f'{test1() = }')
print(f'{test2() = }')
print(f'{test2(1) = }')
it may be a bit neater with this syntax: ```py
class SentinelFactory(type):
def new(cls, name: str):
return super().new(cls, name, (), {})
def __repr__(cls) -> str:
return "<sentinel>"
MISSING = SentinelFactory("MISSING")
def foo(x: type[MISSING] = MISSING) -> type[MISSING]:
return x
until PEP 661 is addressed that is
hm, apparently mypy doesn't like that though, but pyright is fine with it
a singleton enum is one of the nicest ways to do sentinels, since you can use is checks, like you would with normal sentinels
what is wrong in the type hint here?
Show the whole function
You need to return a function
you're returning none and type-hinting that you're returning a callable
def fib_memoization() -> Callable:
"""
>>> fib_memoization()(5)
5
>>> fib_memoization()(100)
354224848179261915075
>>> fib_memoization()(25)
75025
>>> fib_memoization()(40)
102334155
"""
# Cache must be outside recursuive function
# other it will reset everytime it calls itself.
cache: dict[int, int] = {1: 1, 2: 1} # Prefilled cache
def recursive_fn(num: int) -> int:
if num < 1:
raise ValueError("Enter positive integer")
if num in cache:
return cache[num]
value = recursive_fn(num - 1) + recursive_fn(num - 2)
cache[num] = value
return value
oh. Alright
You're not returning anything from this function
you probably forgot return recursive_fn
yep
thnx. Its fixed
Also, please don't use generics like List or Dict or Callable without parameters (e.g. use Callable[[int], int] instead of Callable).
it makes the type gods very sad π¦
yeah, if your function is returning a callable that takes an int and returns an int, you can specify that directly rather than just specifying that it's any kind of callable
Callable[int] would mean that the inner function is taking an int right?
callable needs 2 arguments
first one is the list of arguments your fucntion takes and second one is what it returns
Callable[[int], int] is what you want
oh.
Callable[int] is not valid, yeah
Callable[[int], int] is a function taking an int and returning an int
ohh.
well, not necessarily a function
a callable
So CachedFibo() would be a callable as well
class CachedFibo:
def __init__(self):
self._cache = {}
def __call__(self, num: int) -> int:
if num < 1:
raise ValueError("Enter positive integer")
if num in self._cache:
return self._cache[num]
value = self(num - 1) + self(num - 2)
self._cache[num] = value
return value
fibo = CachedFibo()
c: Callable[[int], int] = fibo # OK
yeah, this would probably pass this type-hint```py
class Foo:
def new(cls, num: int) -> int:
return num
x: Callable[[int], int] = Foo
not sure all type-checkers would support it though since it's a bit weird
pyright is fine with this, mypy complains though
classic
btw, I asked Eric if I'm allowed to register pyright-play.net, he'll get back on Monday
well it violates the super()'s return type
looking forward to that playground!
i actually dont think pyright should allow that
well, sometimes you do want to do some werid stuff like singletons that could return an instance directly from __new__
yeah but the singleton should be an instance of the class
yeah, but I don't think it should strictly be frobidden, I agree that it's weird though
mypy does complain about it, though you could do type: ignore there
problem is that even if you ignore it, mypy still complains about the type-hint for x not being correct. But yeah, pyright could also probably detect it as a violation and allow you to ignore these cases specifically
makes sense
nice, do you plan to make it open source just wondering?
sure
also
can you ping me if you're replying?
sure
π
does pyright have a block on all domain names or some licensing thing?
I mean like a trademark
well I don't want to get into legal troubles with microsoft π
I wasn't able to find a trademark, but yeah, I would double-check that and be careful with using it, though I think it's fine
usually it's ok to use a trademark to refer to the thing it's trademarked for
eg if you put M&Ms in a cookie you can sell them as M&M cookies
there's some fancy stuff like complying with a specification eg Thunderbolt 4 tm
where to use the branding you have to comply to a specification
that's different because you can't just go and make some sugar coated chocolates and call them M&Ms, as there's no public specification or compliance process
Pyright is different again because it's available under the MIT license https://github.com/microsoft/pyright/blob/main/LICENSE.txt
that's just a license for that code, it has nothing to say about using the name itself
It forces you to use the name when you sublicense the software
In the "above copyright notice"
When you redistribute it (etc) you have to include:
Pyright - A static type checker for the Python language
Copyright (c) Microsoft Corporation. All rights reserved.
but there's no sublicensing happening afaik, isn't the playground just supposed to utilize pyright without actually editing it's source or anything like that? Just using an MIT licensed software in any other license only requires you to mention the usage of it
It would come under "use"?
well, you're installing the tool and making calls to it, then taking the output and utilizing it to nicely show where is an error, etc. it's essentially like having an MIT library in your dependencies
mentioning the usage of that tool may be required by the MIT license, but it's mentioned in the dependencies which is usually enough. It has nothing to say about you using the name of that software in your own name, that's a completely different issue and I really don't see how MIT license allows nor prohibits the usage of the name of the software licensed under it
okay, what is wrong here now?
this is returning a list not a Callable
it should return list[int]
Hi, I am doing a node graph where I procedurally generate nodes based on a user defined execute method's .__annotations__ dictionary. This works wonderfully with inputs, only one slight problem, I can't figure out a way of supporting multiple return types? Currently a return type is specified the default python with the -> type syntax, this works quite well for nodes that only require a single output, but when I want to return something more complex, it becomes a little annoying. My system currently names the function inputs based on the __annotations__ variable names, I would like this to persist and work similarly on the return type system.
Are there some type, that supports typeing, that lets the user define keys and types exactly as when specifying function parameters?
Example of current syntax:
class StringConstantNode(KSNodeItem):
_title: str = "Sting Constant"
def execute(self) -> str:
return "String1234"
class PrintString(KSNodeItem):
_title: str = "Print String"
def execute(self, string: str, boolean: bool) -> None:
print(string)
This would be the ideal syntax:
def execute(self, string: str, boolean: bool) -> (outputString: str, outputInt: int):
...
Or more realistically:
def execute(self, string: str, boolean: bool) -> MyDynamicNodeReturnType(outputString: str, outputInt: int):
...
Do you expect these functions to return dictionaries? Maybe you could use a TypedDict
I don't really mind the specific type since the execute function's return type will only be evaluated once, but I need some sort of key/value type where I can specify the key and value type in the return type.
My IDE sure doesn't like it.
I think it wants me to use typing.Dict[]
there was some stir about this kind of thing but i dont think anything is gonna happen with it
Can I do a TypeAlias inline?
from typing import TypedDict
class Foo(TypedDict):
foo: str
def bar() -> Foo: ...```this is how you are meant to do this atm
What versions does this support?
Welp, I am in 3.7.7 because of the host software (Autodesk Maya)
Can I extend the typing module myself?
no
Huh?
I can't really use non-standard modules.
ok then you need to use
def bar() -> typing.Dict[str, str]: ...```
you cant get any better than that
You can use:
if typing.TYPE_CHECKING:
from typing_extensions import TypedDict
class Foo(TypedDict):
foo: str
else:
Foo = dict
There is really no way of extending typing? This seems kinda wired?
mypy considers typing_extensions installed even when it's not
extending typing would be really hard for type checkers to implement
mypy supports custom plugins.
But yeah, arbitrarily extending the type system is not easy π
But possible? xD I just wonder were the return type is stored and if it can somehow be changed manually, python usually quite open with internal data...
Remember, I am only using this for syntax and not any sort of type checking or deep logic.
it can be manually changed yes, but only at runtime and typecheckers cant tell that happens
Type hints don't do anything on its own. They only matter to type checkers that statically analyze the code.
So if you're not using a type checker, just put the more complicated information into the docstring
or in a comment
The function annotations are not used anywhere at runtime?
not normally, some things like pydantic do use them at runtime
but you probably arent using that
How would I access a docstring at runtime?
but why?
but why do you need to access the docstring?
I just need some way of linking some return specification to the method statically that I can access later when creating the nodes in runtime.
Are you inspecting annotations at runtime?
Yeah, that's how i generate inputs.
!d typing.get_type_hints
typing.get_type_hints(obj, globalns=None, localns=None, include_extras=False)```
Return a dictionary containing type hints for a function, method, module or class object.
This is often the same as `obj.__annotations__`. In addition, forward references encoded as string literals are handled by evaluating them in `globals` and `locals` namespaces. If necessary, `Optional[t]` is added for function and method annotations if a default value equal to `None` is set. For a class `C`, return a dictionary constructed by merging all the `__annotations__` along `C.__mro__` in reverse order.
The function recursively replaces all `Annotated[T, ...]` with `T`, unless `include_extras` is set to `True` (see [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated "typing.Annotated") for more information). For example:
use this
yeah
cause you cant use the more lenient inspect.get_annotations
Right.
I still need to find a way to force the return key/type dict into the __annotations__['return'] value.
Could properly be done with a method decorator?..
Shouldn't this work?
import collections.abc
import typing
a: collections.abc.Iterable[str] = "abc"
typing.cast(str, a)
a.split()
or do I have to narrow it through an assert or something
ah
Hm... looks like mypy and pyright both reject this code. If you have typing_extensions or Python >=3.10, you can use ParamSpec.
import functools
from typing import Callable, TypeVar, ParamSpec
T = TypeVar("T")
P = ParamSpec("P")
def log(function: Callable[P, T]) -> Callable[P, T]:
@functools.wraps(function)
def wrapper(*args: P.args, **kwargs: P.kwargs):
try:
output = function(*args, **kwargs)
return output
except Exception as e:
raise e
return wrapper
``` Sadly mypy doesn't support `P.args` and `P.kwargs` yet, so I'd just put `# type: ignore`
It does on master afaik
no
hmm, maybe the playground has an old version
Not sure
Eric said that I can register a domain name with pyright in it as long as I say somewhere that the website isn't run by Microsoft or maintainers of pyright
My PyCharm seems to reject P as the first parameter of Callable. Any idea?
you need a list of arguments
But in the official doc, [] is not included
what pycharm version did you use? does it have support for paramspec yet?
I'm using 2021.2.3 professional ed. I have checking for 3.10 turned on. Maybe PyCharm doesn't have that support yet?
it's possible, paramspec is still a fairly new thing and not many people are using 3,10 yet, try running the type-checking tools manually aside from pycharm
Yeah I checked Pycharm issue checker. The support for ParamSpec seems to be poor
Thanks!
what is the story for annotating the names of a return tuple? e.g. "multiple return values"
is there a standard way? π¦
tuple[int, str]?
that works good for indicating the types
can anyone explain to me wat's generic and typevar ?
def f() -> tuple[x=int, s=str, l=list[int]]:
return 0, '', [0]
``` π€
?
That's not legal syntax. Use typing.NamedTuple instead
(or why not use a dataclass already)
yes, i know
but it is legal with
!pep 637
what is the comment to make mypy or pyright ignore something?
# type: ignore
can someone explain to me wat's TypeVar & Generics??
https://dev.to/decorator_factory/typevars-explained-hmo
(written by @trim tangle)
haha okayy thx
Generic is not explained there π
tat's quite a nice article tho actually
how bout generics?
ya i mean, is there any suggested articles?
ah
There's mypy docs:
https://mypy.readthedocs.io/en/stable/generics.html
Or this SO post:
https://stackoverflow.com/questions/6725868/generics-templates-in-python
ook thx
i would like to ask why doesnt python allow List[int, str] like Tuple does? i mean, why not?
Lists are generally homogenous. If you want to mix elements in a certain pattern, use a tuple
although I'm not sure about the "official" reasoning
oh okayy
also, for example asyncio.gather returns a list, but it's type-hinted to return a tuple
because of this
?
The type stub for asyncio.gather says that it returns a tuple, like py asyncio.gather(a, b, c) where a is Awaitable[int], b is Awaiatable[str] and c is Awaitable[bool] should return Awaitable[tuple[int, str, bool]] according to it.
At runtime, it returns a list. But because it's impossible to write an accurate type annotation for a list like that, the tuple hack was used.
haha okayy thx
why not return a tuple?
ya, just curious
that would be a breaking change, I suppose
you can't just change a stdlib function
lol
well yeah
but when a function returns "multiple things" its usually as a tuple anyway
internally, it probably uses a list of Nones where it sets indices as the awaitables complete
so converting to a tuple is extra overhead
hm makes sense
I do not understand if that is sarcasm or not π
oh sorry lol
Where's the mobile version :angerycat:
I actually mean that, I hate having to use the mypy playground
the monaco editor is a giant pain in the butt... doesn't seem possible to make responsive
huh so far, mypy, black, and now pyright, neat
i'm actually slightly surprised
that they don't share a similar framework or something
it also takes a few minutes (!!!) to build on my VPS
just copy black's playground and switch it to pyright 
Eric also mentioned that it's theoretically possible to run Pyright in the browser
Shameless plug
@trim tangle hey, follow the format of this π #type-hinting message
Now the frontend will invoke a lambda function instead of my crappy VPS
the response time seems to be twice as low
Just cause it's mypy and I've had to implement horrible workarounds in my library just to support mypy
Like if I add a certain field to a certain TypedDict then it causes mypy to OOM
And making a class generic makes mypy take ~30 minutes to run
ah, so you're generally disappointed in mypy, not specifically in mypy-play.net
yeah, sorry should've clarified that
I think the only advantages mypy has over pyright are:
- it's written in python, so you don't need to install node
- it has a plugin system
which might be pretty important
Yeah the plugin system is the biggest advantage
I've kind of solved 1 with https://github.com/RobertCraigie/pyright-python
it's a hack though, right?
yes
does pyright work without an internet connection? It hanged yesterday when I tried it once
It just downloads node if you don't already have it and then runs pyright through node
Are you using the wrapper I wrote?
If so, it doesn't have support for offline usage yet
no, normal pyright
Normal pyright works without the internet
normal pyright should work without an internet connection
I just checked
I wonder what the issue was then, it got stuck for over a minute without output so I just killed it
when I tried it now after disabling the network adapter I got a pyright.errors.VersionCheckFailed: Version check for pyright failed, see output above. after a bunch of npm errors
But yesterday I had a network connection, just not internet
Thats the wrapper I wrote
!pypi pyright
That's why it's not working
This project works by first checking if node is in the PATH and if it is not then we download node at runtime using nodeenv and then install the pyright npm package using npx.
We also automatically upgrade the pyright npm package to it's latest version on every run, see below for how to change this behaviour.
@void panther You can disable this behaviour by explicitly setting the version
e.g. set the evironment variable PYRIGHT_PYTHON_FORCE_VERSION to 1.1.190
gonna modify that and not submit it as-is, but would like to see a way to set update checking
Yeah I do want to support that in the future
A cli for the wrapper itself is a good idea, I hadn't thought about that before
made the issue then π
RobertCraigie/pyright-python#19
I have considered automatically releasing a new version whenever a new pyright version is released
huh, that would be neat too
basically a mirror
hmm
wouldn't want to change pyright tool itself since it already works right but a wrapper that will uses its own version to get the other version would be neat
idk I think it would be better to consolidate that into the existing wrapper as it would probably get confusing
people already get confused over the current python package
trueee
If I do end up implementing that I would change the current autoupdate behaviour to simply output a message that the current package is out of date instead of automatically upgrading
So I updated http://pyright-playground.decorator-factory.su/ a bit:
- switched from monaco to codemirror
- made the editor more responsible by switching to a lambda for pyright
TODO:
- clean up the code (it is a mess)
- add Pyright version selection
- add Python version selection
- fix squiggly line not showing up if the error is at the last char
- reset the tooltips somehow after a new set of diagnostics is received
- colour-code toolitps depending on the severity
here's the code if anyone is interested https://github.com/decorator-factory/pyright-playground
global TODO: rewrite the backend for Node
for some reason, the red highlight is off by 2 characters to left for me (on firefox), bit weird
what would be really cool is a single playground that lets you choose between mypy, pyright, and even other typecheckers
that would be really nice
sigh... I'll look at that tomorrow
you should probably add some license there
YAY
Yeah I have some off by one bug.
That's what you get for not writing tests
!rule 4 which is probably why it was deleted
4. Use English to the best of your ability. Be polite if someone speaks English imperfectly.
(if you were wondering)
i deleted it, I was wrong with the layout
lol
nice
so I pushed a few updates, should look nicer now
How to type MongoDB aggregation keywords an TypedDict?
E.g.:
[{
$match: {
name: {
$in: ['John', 'Smith']
}
}
}]
and
class MyQuery(TypedDict):
x: int
y: int
$in: str # neither this
'$in': str # nor this work
label: str
(both is sample)
probably this
Should work, thanks!
And another (probably with fast answer) question - I have a custom type AggQuery = TypedDict(...) and I pass it (a pure - but typed - dict!) to a function which takes dict.
Type checker (mypy) complains about type of dict which I pass to the function.
In TypeScript I will write obj1 as dict, but how to get rid of this issue in Python?
Do not get me wrong - all of benefits of typing the dict are consumed, no need to do it further
Sounds like a variance issue, can you use Mapping as the annotation instead?
yeah or typiing.Mapping
(depending on your Python version)
If you ever want to explicitly tell the type checker what a type is you can use cast
@deep pendant Can you show your code?
@deep pendant Here's the issue:
class Foo(TypedDict):
bar: int
baz: str
def fizz(foo: dict[str, Any]):
foo["new field"] = 42
foo["bar"] = "wrong type"
foo: Foo = {"bar": 1, "baz": "aaa"}
fizz(foo)
Argument 1 to "get_tracks_list" has incompatible type "AggQuery@189"; expected "Dict[Any, Any]"
Right, because if that was allowed, this code would type-check, which it shouldn't
dict is "invariant" relative to its key and value parameters. That means that even if A is a subtype of B, dict[A, V] is not a subtype of dict[B, V].
What you need to do is make that function accept a Mapping[your-key, your-value].
Mapping is "covariant". It means that if K1 is a subtype of K2 and V1 is a subtype of V2, then Mapping[K1, V1] is a subtype of Mapping[K2, V2].
Mapping is covariant only in its value type, the key type is still invariant. But usually it's the value type that causes problems, the key type should just be str for TypedDict
yea, simplified.
ah, alright
why is the key invariant though?
Nevertheless, I anyway have to modify the function argument's types, yea? Even if it doesn't matter if this would be Mapping (friendly to other callers) or my type
wdym?
https://github.com/python/typing/pull/273 and some other old linked discussion. We haven't revisited this in a while though
I meant that I have to change (function) get_tracks_list: (query: dict, offset: int) -> list to either (query: Mapping[...], offset: int) or (query: AggQuery, offset: int)
I've loss the point π
Thank you a lot!
How should I type-hint a traceback object (I need this for __exit__ method in my context mgr class), I know there's traceback module, but i wasn't able to find the traceback class in it
types.TracebackType
!d types.TracebackType
class types.TracebackType(tb_next, tb_frame, tb_lasti, tb_lineno)```
The type of traceback objects such as found in `sys.exc_info()[2]`.
See [the language reference](https://docs.python.org/3/reference/datamodel.html#traceback-objects) for details of the available attributes and operations, and guidance on creating tracebacks dynamically.
oh, I see, thanks
Most of the internal types are found in types.
not even close
most of the public internal types are found in types
>>> iter(()).__class__
<class 'tuple_iterator'>
>>> iter([]).__class__
<class 'list_iterator'>
>>> iter({}).__class__
<class 'dict_keyiterator'>
>>> iter('').__class__
<class 'str_iterator'>
>>> iter(b'').__class__
<class 'bytes_iterator'>
>>> iter({0,}).__class__
<class 'set_iterator'>
>>> iter(bytearray()).__class__
<class 'bytearray_iterator'>
Indeed yes, those are a notable exception since all you should care about is that they're an Iterator[X].
!e
print(object.__subclasses__()[52])
@tranquil turtle :white_check_mark: Your eval job has completed with return code 0.
<class 'symtable entry'>
oh no, space in class name
Spaces and other rules for names are just a syntactic restriction - manually constructing things let you use any string for class names, globals, attributes etc.
!eval
Printer = type(
"my-basic-printer", (), {"hello-world": lambda self, x: print(x)}
)
p = Printer()
print(p.__class__)
getattr(p, "hello-world")("hello, world!")
@leaden oak :white_check_mark: Your eval job has completed with return code 0.
001 | <class '__main__.my-basic-printer'>
002 | hello, world!

from typing import Callable, Generic, TypeVar
T = TypeVar('T')
class X(Generic[T]):
f: Callable[..., T]
def __init__(self, f: Callable[..., T]) -> None:
self.f = f # mypy: error assignment - Cannot assign to a method
is it bug in mypy?
That doesn't look right, yeah.
Yep. It's been an issue for 6 years https://github.com/python/mypy/issues/708
The best workaround is with a protocol ```py
class Function(Protocol[T]):
def call(self, *args, **kwargs) -> T:
...
whoa...an issue tat hasnt been fixed in 6 years? gg
@overload
def foo(t: T) -> List[T]:
...
@overload
def foo(t: List[T]) -> List[T]:
...
def foo(t: Union[List[T], T]) -> List[T]:
if isinstance(t, list):
return t
return [t]
does anyone know how to make pyright happy here?
this one has been up for 8 years https://github.com/python/mypy/issues/230
Lmfaoooooo
If you call foo(x) where x is List[int], should it return List[int] or List[List[int]]?
From the first overload, it should return List[List[int]].
Right now you can't really specify "any-type-except-list"
Actually, why do you even have these overloads? just do ```py
def foo(t: Union[List[T], T]) -> List[T]:
if isinstance(t, list):
return t
return [t]
Although that still has the same issue.
Actually, isn't this a bug? π€
Probably related to https://github.com/microsoft/pyright/issues/2568?
I made this https://github.com/microsoft/pyright/issues/2642
I think this applies too https://github.com/python/typing/issues/926#
Oh that might be specific for TypeGuard nevermind
thanks for the link!
Isn't Union[List[T], T] just T?
No?
well, depends on the context
if the type variable is already decided by some outer context, like ```py
class Foo(Generic[T]):
def init(self):
self._items: list[T] = []
def insert(self, items: Union[list[T], T]) -> None:
...
ints = Fooint
ints.insert(42) # ok
ints.insert([1, 2, 3]) # ok
(of course this will break if the type variable is assigned list[something]... but that's another story)
so the issue here is how T is inferred
If a TypeVar can represent any type, that would also include List[T]
in the context of that function - yes, I think this signature is broken
which is why I opened the pyright issue
trying to figure out how properly should I use Literal type with Union π€ List[Literal["a", "b"]] works fine as a type argument but if I add a Union to it then mypy starts to get confused and wraps it in a wrong way, is this intended? what I would like is an argument that can take Union[List[Literal["a", "b"]], List[Literal[1, 2]]]
That should be fine. Are you running into variance issues? For arguments I'd generally recommend using Sequence instead of List
def foo(arg: Union[Sequence[Literal["a", "b"]], Sequence[Literal[1, 2]]]):
return arg
foo(["a", "b"]) # has incompatible type "List[str]"; expected "Union[Sequence[Union[Literal['a'], Literal['b']]], Sequence[Union[Literal[1], Literal[2]]]]"
foo([1, 2]) # has incompatible type "List[int]"; expected "Union[Sequence[Union[Literal['a'], Literal['b']]], Sequence[Union[Literal[1], Literal[2]]]]"
looks like a bug to me
thanks. thought so π¦ will try with different mypy versions and if that will not help then I'll create a bug issue.
How do I typehint this? RecursiveDefaultDict is RecursiveDefaultDict(dict[_KT, _VT], t.Generic[_KT, _VT])
hint = RecursiveDefaultDict[str, t.Union["TOMLType", "hint"]]
self._settings_dict: hint = RecursiveDefaultDict()
that's another 6 year old issue https://github.com/python/mypy/issues/731
works on pyright tho
did you also discover this trick? ```py
def Y():
return defaultdict(Y)
Yeah, but unfortunately when used like that it's not easy to control the default factory of all the dicts it creates
so the above typehint is correct?
IIRC the spec says that type checkers are not strictly required to implement recursive aliases
so the type hint is correct, but mypy doesn't support it
hmm with pyright on this code it detects it as Type[RecursiveDefaultDict[str, ...]] for some reason
a : TypeAlias=RecursiveDefaultDict[str, t.Union["union_arg", "a"]]
b:a = RecursiveDefaultDict()
reveal_type(b)
reveal_type(b["a"])
\scratch.py:115:13 - info: Type of "b" is "RecursiveDefaultDict[str, int | str | Type[RecursiveDefaultDict[str, ...]]]"
\scratch.py:116:13 - info: Type of "b["a"]" is "int | str | Type[RecursiveDefaultDict[str, ...]]"
pyre gets it right but then complains about the b assignment
can you show the code for RecursiveDefaultDict?
the type inference seems right to me
what's wrong?
Since it's a type alias, it should be seeing the RecursiveDefaultDict as a hint for an instance of it no?
so b["a"]["b"] would work as it's a RecursiveDefaultDict[str, t.Union["union_arg", "a"]]
What if b["a"] is a str?
What is union_arg in your case?
Just union_arg = t.Union[int, str]
I'm not sure I understand, since it's an union it should matter? At least I'd expect it to allow both of the accesses
right now b["a"]["b"] fails as it think I'm indexing the string from the union:arg
Or well, if I'm not understanding it correctly, how would I typehint the case where the dict's values can be anything from the union_arg, or its own type with the same generic arguments
If you have an Optional[int] (which is Union[int, None]), you can't just add it to an int
in general, an operation is only allowed on a union if all variants support it
maybe I don't understand how your class works?
so should I just keep it at [str, t.Any] if any key can be either a value or an another recursive dict?
if you want to basically turn off type checking on indexing, then yes
if you want to do it in a "type-safe" way, you could make a method for RecursiveDict that takes a list of keys and traverses the dict, checking each step
So there'd be no way of making an arbitrary amount of direct accesses work?
no
because operations on unions are strict
as in, you can't do an operation if just one of the variants allows it
which makes sense from a type safety perspective
Would be hacky but couldn't you overload __getitem__?
I usually tend to avoid hackyways since typecheckers go brrr with them
I don't think that would work as the keys will always be strings
Hello fellow typing friends. What's the correct way of distinguishing an async generator from an async function that eventually returns an async iterator? Those two things are very different, but they look absolutely the same in terms of hinting :/
async def v1() -> AsyncIterator[int]:
yield 0
async def v2() -> AsyncIterator[int]:
return v1()
Why are they very different? Don't they work the same from a user's perspective?
No, calling v1() gives you an async iterator. Calling v2() gives you an awaitable that gives you an async iterator.
I.e. you'd do [x async for x in v1()] , but you'd have to do [x async for x in await v2()]
Thats synonymous to AsyncIterator, and most importantly, that's the thing we're supposed to use for typing an async generator... but I'm afraid we can't declare an async function returning said thing.
Then can you wrap v2 in Awaitable[], is that valid?
This seems like an unfortunate edge case
Doesn't feel that way.. Awaitable[] should be implied by the function being async, else you couldn't distinguish between Awaitable[int] and Awaitable[Awaitable[int]]
(of the implicit async typing of functions)
So sad ^^
Maybe just use AsyncIterable[T] because that one never gets picked for annotating generators ^^
Hm, it's kind of an unfortunate effect of python not declaring the nature of function-vs-generator in the def-line..
The type is not determined by the signature
Though that's just how generator functions generally exist so
That's what I meant. Those two things are very different API-wise, but the 'header' is exactly the same.
That's just how it is I don't see how else it could be
That's the correct answer I suppose.. without looking at the body, one cannot truly know the type of a function for sure :/
could someone explain the error pyright is giving? py def a(iterable: Iterable[T]): # type: ignore itertools.pairwise(iterable)``````Argument of type "Iterable[T@a]" cannot be assigned to parameter "__iterable" of type "Iterable[_T@__new__]" in function "__new__" Β Β TypeVar "_T_co@Iterable" is covariant Β Β Β Β Type "T@a" cannot be assigned to type "_T@__new__"
What type is T?
Regular TypeVar (regular meaning not covariant or contravariant - is invariant the term?)
Tried with a covariant one too
Also you need more than one use of a type variable for it to do anything
Yeah in my actual implementation I did
Ok show your actual code?
import itertools
from typing import TypeVar
from collections.abc import Iterable, Iterator
T = TypeVar('T')
def triplewise(iterable: Iterable[T]) -> Iterator[tuple[T, T, T]]:
"Return overlapping triplets from an iterable"
# triplewise('ABCDEFG') -> ABC BCD CDE DEF EFG
for (a, _), (b, c) in itertools.pairwise(itertools.pairwise(iterable)):
yield a, b, c```
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
Mypy likes it
There's also more_itertools.triplewise
yeah this code is copied directly from that
though i should probably just use that instead, since there's no reason not to
might be a pyright bug then
Seems like it, make sure you have the latest version?
here is the relevant typeshed stub, maybe an issue with how it understands typevars in __new__? https://github.com/python/typeshed/blob/master/stdlib/itertools.pyi#L206
stdlib/itertools.pyi line 206
class pairwise(Iterator[_T_co], Generic[_T_co]):```
Woah it's a class
yeah seems like that's the case
not at runtime
Curious if itertools.combinations has similar issues, it has an even more complicated __new__
I feel like way too much stuff is secretly a class
can't write a generator function in C
def foo(iterable: Iterable[T]) -> Iterator[tuple[T, T]]:
return itertools.combinations(iterable, 2)```successfully type checks, so not sure
.topic
AyncIterator i think is wrong and AsyncIterable is what you should use anyway π the docs for example recommend Iterable instead of Generator, not Iterable
doesn't fix your problem though
that's actually really weird situation
the first return type is correct because that's literally what it is
the second return type is correct because implicitly it gets wrapped in Awaitable
however the revealed types are in fact correct
main.py:9: note: Revealed type is "def () -> typing.AsyncIterable[builtins.int]"
main.py:10: note: Revealed type is "def () -> typing.Coroutine[Any, Any, typing.AsyncIterable[builtins.int]]"
i don't know that there's a solution
unfortunate
would be worth a post on the typing-sig mailing list
cursed idea: Annotated[AsyncIterable[int], (yield)] so the generator flag is visible in the signature
:>
Eric said that he's going to change something in the next version
anyone know why https://mypy-play.net/?mypy=latest&python=3.6&gist=676c8066f624302d49a6b8cfbb8a849e fails but https://mypy-play.net/?mypy=latest&python=3.6&gist=1b6547d3f57f5b86cde4db977ff4de86&flags=strict passes?
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
@grave fjord the type hint of the actual implementation is not taken into account
So your first function's type is just ```py
@overload
def bar(name: str, *, return_length: Literal[True] = ...) -> Tuple[str, int]:
...
@overload
def bar(name: str, *, return_length: Literal[False]) -> str:
...
ah except that it has to be compatible with the overrides
yep
ah of course so you can do stuff like:
@overload
def ham(name: Literal["ham"]) -> Ham: ...
@overload
def ham(name: Literal["spam"]) -> Spam: ...
def ham(name: object) -> Ham | Spam:
if name == "ham": return Ham()
if name == "spam": return Spam()
raise ValueError
yep
yeah there's been some talk about making bool equivalent to Literal[True, False]. might even be a mypy PR for it already
That would be cool indeed 
also is:
class Foo(enum.Enum):
ham = auto()
spam = auto()
is Foo equivalent to Literal[Foo.ham, Foo.spam] ?
it's not currently - but if you have a PR it would be interesting to see
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
π€ can you subclass enums?
no
then yeah, they're the same
you cannot extend a subclass of enum that defines enum members
right, that makes sense
thanks to some dark metaclass magic
but you can extend one that doesn't define enum members?
wait ik you can
yes
I have a place in my code that does that
making a BitwiseAutoEnum for a bitmask enum
class enum.IntFlag```
Base class for creating enumerated constants that can be combined using the bitwise operators without losing their [`IntFlag`](https://docs.python.org/3/library/enum.html#enum.IntFlag "enum.IntFlag") membership. [`IntFlag`](https://docs.python.org/3/library/enum.html#enum.IntFlag "enum.IntFlag") members are also subclasses of [`int`](https://docs.python.org/3/library/functions.html#int "int").
?
!e
import enum
class Ham(enum.Enum):
pass
class Spam(Ham):
umad = enum.auto()
brohiem = enum.auto()
print(Spam.umad)
urrgh
@grave fjord :white_check_mark: Your eval job has completed with return code 0.
Spam.umad
ah I see it's special cased for empty enums
it could probably be done with __init_subclass__ now right?
https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=91ffdba2f473f4644f1878129a797fc4 mypy doesn't/won't care it seems
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
oh it got fixed back in october https://github.com/python/mypy/issues/10857
right but I'm using 0.910
how would i typehint that a variable is storing any type, such as a = int a = str
i tried just using Type since that seemed most intuitive to me, but no luck
https://img.laundmo.com/u/C2YqVq.png
obviously Literal would work, but it would be quite hard to collect all the possible values
that's weird, is this pyright? maybe try Type[Any] or lowercase type
it is Pyright (or Pylance to be more accurate)
type should work
yeah it does. wierd that the typing.Type without any arguments in [] doesnt have the same effect
https://img.laundmo.com/u/xL40rc.png
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
@dim trail what are you using this for?
often it's better to use a type annotation that more reflects how you'll use it, eg Callable[[object], object]
i made a class that represents a mapping from a incoming type to a outgoing type
