#type-hinting
1 messages · Page 68 of 1
async def _run_event(self, async_fn: Callable[P, Coroutine[Any, Any, object], /, *args: P.args, **kwargs: P.kwargs) -> None: can you use ParamSpec like this? I think it's forbidden
Sometimes the terms "coroutine" and "coroutine function/async function" are used interchangeably, like "generator" is sometimes used for "generator function". But it's confusing IMO
!e
async def foo(): ...
print(type(foo))
print(type(foo()))
@hearty shell :white_check_mark: Your eval job has completed with return code 0.
001 | <class 'function'>
002 | <string>:4: RuntimeWarning: coroutine 'foo' was never awaited
003 | RuntimeWarning: Enable tracemalloc to get the object allocation traceback
004 | <class 'coroutine'>
RuntimeWarning: coroutine 'foo' was never awaited 🤔
Didnt know it gave a warning x)
I think it's fine, but remove \
oh hm maybe not, it should be there
otherwise kwargs will not work
it has to be a / tho
I always get them mixed up
Async generators don't warn
How are async generators even cleaned up? 🤔
wait... how are normal generators cleaned up?
You have to use async with contextlib.aclosing(agen_fn()) as agen:
You can use yield from or with contextlib.closing(gen_fn()) as gen:
It's particularly bad if you do something like:
def whatever():
with open(filename) as f:
for line in f:
if not line.startswith('#'):
yield line
every time an async generator is used?
It's quite easy to build a thing and do whatever().__next__()
Every time
There's some garbage collector hooks that run aclose()
But if your async_gen function is GC'd in a cancelled task group it can continue executing
yeah that's understandable
I'm starting to think that async introduces a lot of accidental complexity that most people don't really need...
anyway, we're in #type-hinting btw
not sure if this counts as typehinting
from typing import Any, Dict, TYPE_CHECKING
if TYPE_CHECKING:
from .message import Message
from .state import ClientState
def _event_to_object(name: str, data: Dict[Any, Any], _state: "ClientState") -> Any:
_event_converters: Dict[str, Any] = {"READY": None, "MESSAGE": Message}
``` how can I import `Message`? it just says `Message` is not defined because `TYPE_CHECKING` is set to false during runtime
if you need to use it at runtime, don't import it inside if TYPE_CHECKING
okay, but then i'd get circular import
then you'll need to redesign your program 🙂
w h a t
or use absolute references, I guess
use what?
if your top-level package is called top_level, put import top_level in the file and use top_level.message.Message
nvm i realisd i dont get circular imports
what
Assuming there is some object named “thing”, That’s an example of a circular import
It’s when a module imports something from another module, but that one imports something from the same module. Then the initialization of both modules can’t complete
dude i know what a circular import is lol
i just thought my code was going importing something which wouldve raised circular import, but it didnt
ohh
i said i dont get them
i meant my code wasnt raising them
i don't think so...
🗿
is it sinful to import something in a function
this time i am getting circular import
and its pretty hard to avoid
nvm avoided it
Hey I am trying to make this decorator's hinting say "Takes a function which takes any amount of arguments (first one is a typevar) and keyword arguments, returns a function (callable) which takes the same first arg, joins (concatenate) type of (type) first variables, rest of the things as is (paramspec) and the return will be the same (also a typevar)"
This is my implementation of it :
R = TypeVar("R")
ARG = TypeVar("ARG")
P = ParamSpec("P")
class Thingy:
@staticmethod
def instance_and_class_method(
func: Callable[[Concatenate[ARG, Type[ARG], P]], R]
) -> Callable[[ARG, Type[ARG], P], R]:
@wraps(func)
def wrapper(self, *args, **kwargs):
cls: Type[self] = self.__class__
return func(self, cls, *args, **kwargs)
return wrapper
This is how I tried using it :
class Foo:
def __init__(self, x: int):
self.x = x
@Thingy.instance_and_class_method
def bar(self, cls, new_x: int) -> Thingy:
print(f"{self.x = }")
return cls(new_x)
f = Foo(1)
b = f.bar(2)
print(b.x)
Works perfectly and as expected but decorating (@Thingy.instance_and_class_method) causes a comically large error :
(method) instance_and_class_method: (func: (Concatenate[ARG@instance_and_class_method, Type[ARG@instance_and_class_method], P@instance_and_class_method]) -> R@instance_and_class_method) -> ((ARG@instance_and_class_method, Type[ARG@instance_and_class_method], Unknown) -> R@instance_and_class_method)
Argument of type "(self: Self@Foo, cls: Unknown, new_x: int) -> Thingy" cannot be assigned to parameter "func" of type "(Concatenate[ARG@instance_and_class_method, Type[ARG@instance_and_class_method], P@instance_and_class_method]) -> R@instance_and_class_method" in function "instance_and_class_method"
Type "(self: Self@Foo, cls: Unknown, new_x: int) -> Thingy" cannot be assigned to type "(Concatenate[ARG@instance_and_class_method, Type[ARG@instance_and_class_method], P@instance_and_class_method]) -> R@instance_and_class_method"
Parameter 1: type "Concatenate[ARG@instance_and_class_method, Type[ARG@instance_and_class_method], P@instance_and_class_method]" cannot be assigned to type "Self@Foo"
"Concatenate[ARG@instance_and_class_method, Type[ARG@instance_and_class_method], P@instance_and_class_method]" is incompatible with "Foo"
Function accepts too few positional parameters; expected 3 but received 1

if I have a function that returns Optional[TextChannel], and i am calling it in a spot where it will always return TextChannel and never None how can I assign it to always be a textchannel?
/home/Caeden/Github/discii/discii/message.py:57:16 - error: Expression of type "TextChannel | None" cannot be assigned to return type "TextChannel"
when im doing ```py
@property
def channel(self) -> TextChannel:
"""Returns the channel that the message was sent in."""
return self._channel
basically i just need to make it so that my `self._channel` instance will always be `TextChannel`
Callable[[Concatenate[ARG, Type[ARG], P]], R] when using Concatenate you dont put it inside a list
And because you are returning a functionw with ParamSpec form instance_and_class_method, you also need Concatenate
class Thingy:
@staticmethod
def instance_and_class_method(
func: Callable[Concatenate[ARG, type[ARG], P], R]
) -> Callable[Concatenate[ARG, P], R]:
@wraps(func)
def wrapper(self, *args, **kwargs):
cls: type[self] = self.__class__
return func(self, cls, *args, **kwargs)
return wrapper
The first argument to Callable must be a list of types or "..."mypy(error)

mypy doesn't support Concatenate yet
Yeah I dont think Mypy supports
print(b.x) should still error though because you are returning a Thingy
what is mypy
And Thingy has no x
Oh welp, I meant Foo, 
Ah then 'Foo', should leave your code error-less in vscode
Python's "defacto" type checker
Invalid location for ParamSpec "P"mypy(error)
and then proceeds to point atARG, mypy wrong here again it seems
How do you know it is always not None?
You might have better luck with pyright
Yes, whatever you are trying to do right now, forget about mypy working with it
x)
Yeah lol, should switch to pyright :p
Support for it should come soon I think? But yeah Pyright does move way faster
Pyright supports features even before they are actually finalised and accepted as a Pep xD
in my situation it is always not None because the channel that the message came from will always be registered
there are 2 situations; one where the user inputs an id where it could potentially be a channel, but sometimes not and this situation where it is always a channel
Strangely enough it doesn't support stuff from typing_extensions right?
Self hint in it didn't work for me
do i make a second function or?
Can you show what you tried to do?
I mean, there might be a clean way to fix this upstream somewhere in your code
but you can just assert self._channel
before returning
oo true
although you can equally just # type: ignore xD
Nevermind, it was mypy that did it, my bad
Variable "typing_extensions.Self" is not valid as a typemypy(error)
This was my test
from typing_extensions import Self
from typing import Any
class Test:
def __call__(self, *args: Any, **kwds: Any) -> Self:
print(args or "", kwds or "", sep="")
return self
t = Test()
t(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)
which would be better?
assert or type: ignore
Yeah, and then it proceeded to say Self? Not callablemypy(error) 
Yeah, I said pyright supports...
Type ignore will cause your linter to ignore a existing possible error
assert will cause it to raise an AssertionError if a certain condition is not met
Not sure to be honest, depends on the case, here I think there are probably other ways to fix this somewhere else
But if forced I think assert might be better
Mypy still doesn't support it
since None might make sense somewhere else in your code
Sorry ;)
so making that more difficult to see where a bug is coming from
i know what they do, i asked which would be better for the context.
what would those other ways be? theres just 2 situations
what should I change about this code? Want a TypeVarTuple of (int, str) to require a (Body[int], Body[str]) as args
i might just stick the assert
from dataclasses import dataclass
from typing import Generic, TypeVar
from typing_extensions import TypeVarTuple, Unpack
P = TypeVarTuple("P")
T = TypeVar("T")
@dataclass(frozen=True)
class Body(Generic[T]):
x: T
class Call(Generic[Unpack[P]]):
def __init__(self, *args: Body[Unpack[P]]) -> None:
self.args = args
Call[int, str](Body(1), Body("abc"))
I am saying that if possible, I would try to fix this somewhere else
assert might be better because it seems like you are building a discord bot, you can stick a logger which'd log such events of error
if possible, that is why I said it depends
gives this:
error: TypeVarTuple not allowed in this context
error: Argument of type "Body[int]" cannot be assigned to parameter "args" of type "Body[*tuple[int, str]]" in function "__init__"
TypeVar "T@Body" is invariant
"int" is incompatible with "*tuple[int, str]" (reportGeneralTypeIssues)
error: Argument of type "Body[str]" cannot be assigned to parameter "args" of type "Body[*tuple[int, str]]" in function "__init__"
TypeVar "T@Body" is invariant
"str" is incompatible with "*tuple[int, str]" (reportGeneralTypeIssues)
i was thinking assert would be better because if the assert fails (which it shouldnt), its all fucked
I dont think this is possible?
I am very curious 🤔
I think this needs the hypothetical typing.Map operator
Yeah I was thinking this looks like functor behaviour
I recently started using Pyright with a codebase that I'd already been using mypy with for a while. This revealed a number of places where mypy and pyright disagree (ref: https://github.com/jab/bidict/pull/242), and uncovered a number of previously-unknown bugs in Pyright and mypy in the process.
In case it's not too late, should there be a common repository of acceptance test cases that all type checkers could use? That way, when a user is using only one type checker and finds a new bug in it, other type checkers can benefit from the corresponding test case at the same time. Ultimately, all participating type checkers should get less buggy more quickly, and agree with one another more often.
(Has this been discussed before? I searched a bit (e.g. https://mail.python.org/archives/search?mlist=typing-sig%40python.org&q=sharing+test+cases ) but didn't find prior discussion. If this isn't the right place to ask, please let me know where else I should post this.)
There isn't such a thing yet. I'm thinking of building it once typing.assert_type becomes available
Neat, thanks @oblique urchin, great to hear it!
(And thanks so much again for looking at that PR! Btw, did you happen to see my proposal in https://github.com/jab/bidict/pull/242#discussion_r825314402 (for # mypy: ignore plus "warn-but-not-error on unused suppression comment")? I know some of those threads are getting long but just wanted to make sure you saw that proposal before you maybe run out of time on that PR:)
uh yeah there's a lot going on in that PR 🙂 It's on my list to look at it again
Amazing, thank you so much! Whenever you have time, just wanted to check if you were still interested + point out that comment in case you only have time for one more)
would be neat, is there a draft PEP / previous discussion for this? or is it very hypothetical
nothing remotely concrete
for pep 646, only one type var tuple can exist in a "type parameter list"... what is a type parameter list?
more specifically, my question is: the pep doesn't seem to mention that only one typevartuple can be in an alias at a time, but that seems like a hard requirement. is this just an oversight, or me missing vocabulary?
"type parameter list" isn't a well-defined term, but I'd think it includes params to a type alias
alright
(i was going to complain about missing the explicitness but then i reread it and wondered whether i had misinterpreted ... i had :D )
Is there any good way to have an iterable type that doesn't accept strings?
For context I want to expand List[str] to Iterable[str] but explicitly disallow str to be passed
I forgot the name for it but you cant exclude types from an accepted type
Can you go the other way around? What kind of types do you want to support?
Yes but I was hoping someone knew of a workaround
What do you mean the other way around?
there was talk on typing-sig about adding a Not type to support this
Ah, that was the name
Yeah I'd heard about that, was there any sort of consensus?
Not really, Eric Traut didn't like it
Also looking for it x)
"thoughts on a nottype" is the thread title. couldn't find it online
that's a reply, not the main thread
That is an understatement
unrelatedly, TypeScript supports string uppercasing at the type level :S https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html#uppercasestringtype
Generating mapping types which change properties via template literal strings.
He also mentions intersection but I thought I heard that it would have better prospects
yes, although Eric is skeptical about that too. I think there are more use cases
idk but def fn(x: str & ~Literal[""]): doesnt seem like a check that should be done at the type level
also, a NotType breaks lsp doesnt it
a supertype of T would be compatible with ~T but T wouldnt
(not that there arent any lsp violations in the stdlib)
Not necessarily, you could have an abstract set of all possible strings not including "", and that would be your "NotEmpyStringType" x)
but it wouldnt be a subtype of str
I think it is the same for how you cant pass a str to a literal
(edited to clarify)
Humm, would it?
The more I think about it the more I see how this could be very difficult to implement x)
But I think the idea should be that Not is contravariant
yeah i mean i would think you can do x: ~str = object()
so that Not[Super] is a subtype of Not[Sub] so it isnt allowed, which means if you wanted Not[str] you couldnt pass a Iterable but you could pass a List
Well, str is an object, so I would think you cant do that
hmm but wouldnt it have to be covariant too
wait nvm variance still breaks my head
I feel like TypeScript's Exclude is more obviously useful than Not
You mentioned that as the Difference no?
But yeah I mean Not would only be useful when paired up with a intersection
is there any pep for intersection types?
hmm
another long one about str vs Sequence[str] https://github.com/python/typing/issues/256
pytype "solved" it by special casing strings to not match Sequence[str]
that makes me a bit uncomfortable
I think type should never lie, even if it is a white lie.
i agree with this
this is how i understand it
def f(x: ~T):
assert not isinstance(x, T)
then if i have
def f_str(x: ~str):
assert not isinstance(x, str)
f_str(object()) will work, but f_str("") will fail which i think violates lsp
Well, I was speaking conceptually. If you wanted to check it a runtime I think you would have to do
def f_str(x: ~str):
assert type(x) not in str.mro()
But yeah if you interpret Not to just not be exactly the type passed, then it would violate LSP
not just exactly the type, the type and its subclasses
actually, you are going a step further since a subclass of str should still work
I think you are right 🤔
an object() is not a string, so it should work with ~str
Well, as I said, if you just take that interpretation, then it would definitely break lsp
Yeah actually when I said it just had to contravariant I forgot that it would lose semantical meaning.
Going back to a useful example
str
class MyStr(str): ...
def foo(i: Iterable & Not[str]): ...
What would make sense here? Should this accept a MyStr?
no
Not[T] should mean all types not compatible with T
Humm, contravaraint so it doesnt break LSP, and covaraint so it makes seance semantically
And then if you really wanted MyStr you could apply an Union afterwords
Gonna quote that again xD
def f(it: Iterable[str]):
g(it)
...
def g(it: Iterable[str] & Not[str]): ...
should this be disallowed 🤔
I guess it should
Why should it be disallowed? That seems like the one use-case for it
i think it should be disallowed too
inside f you don't know whether it is a str or not, so you cant check whether its compatible with the Not[str]
if not isinstance(it, str): ...
would you describe```py
class Foo(Generic[T]):
def bar(self, arg: T): ...
vs what i think of as a generic function ```py
def noop(x: T) -> T: ...
class Foo(Generic[T]):
def baz(self, arg: T, something: U) -> Spam[Self, U]: ...
also what is baz also called here?
Hold on, is the ~ a typing operator?
No, we were talking about a hypothetical Not type
Although it does already have a different meaning in typing
!e
from typing import TypeVar
print(TypeVar("T"), TypeVar("T", covariant=True), TypeVar("T", contravariant=True))
@hearty shell :white_check_mark: Your eval job has completed with return code 0.
~T +T -T
the invariance repr would probably have to be removed if NotType was added
True, I dont think anyone would care about that
Oooh, I shouldn’t just click on a channel and ask immediately should I
Doenst hurt me to respond ¯_(ツ)_/¯
x)
🙂
TIL
Is there a way to annotate a mixin class with a union of two classes, so that self assignments and method access wouldn't throw errors? Both classes have very similar interface, and the mixing touches only on the common part of them (hopefully)
Specifically, it's tkinter.ttk.Entry and tkinter.ttk.Combobox, where Combobox's documentation says the methods are the exact same as for the Entry. I had a class that subclassed Entry to add placeholder functionality, but now I need the same for the Combobox, and it works as a mixin class, but throws one hell of a lot of typing errors otherwise.
Now I have smth like this:
class PlaceholderMixin:
# all placeholder methods and __init__ are defined here
class PlaceholderEntry(PlaceholderMixin, ttk.Entry):
pass
class PlaceholderCombobox(PlaceholderMixin, ttk.Combobox):
pass
Use a protocol?
Although Combobox is a subclass of Entry, so def whatever(self: Entry, ...) -> ...: should work
Can you show a demo of what doesn't work?
Yeah, I tried a protocol, but then super() complains that the 2nd parameter isn't an instance of ... something. Also, a protocol requires me to define every single method that's already defined in the mixin, as well as all methods that are used within those methods too, which is kinda not ideal. I'll try to grab some pics in a moment
Just paste the code no need to screenshot - I'd recommend making a https://mypy-play.net
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
honestly, that's the only issue it shows now
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
although having to type everything twice like this is... not ideal
Hmm, if Combobox is a subclass of Entry
I could do like a bound type variable, somehow?
hmmmm
the error message makes sense because a protocol isn't an instance of PlaceholderMixin
This is something MyPy should take into account, but it doesn't do so right now
also double-typing with a bunch of Any's to not have to deal with incompatible signatues is quite a meh solution, to the point where I could just use no_type_check on the entire class and call it a day
Your mixin extends Widget?
that base doesn't need to be there with the protocol in place, but normally mixins are given a common base with whatever they'll be used in
that just lets you access the underlaying base methods
so, since both Entry and Combobox use Widget at base, subclassing it in the mixin prevents "no configure method" errors in the mixin, where it comes from the base Widget class
but as I said, with the protocol in place, that's not really needed
I don't think Entry extends Widget
oh?
I thought everything extends widget
ttk.Widget extends tk.Widget
then ttk.Entry extends ttk.Widget
https://docs.python.org/3/library/tkinter.ttk.html#combobox says "The ttk.Combobox widget combines a text field with a pop-down list of values. This widget is a subclass of Entry."
Sort of implies that not all Widgets are Entrys
Yes, not all widgets are entries
but entries are widgets, and widgets come with a common configure method for example
Ah right
So normally for a mixin class without base, MyPy would throw an error if you tried to use it inside the class:
class Mixin:
def __init__(...):
self.configure(...) # Mixin has no configure method
Yeah but you can put the base in the self with a protocol
but if you're sure that the mixin will be used with widgets and widgets only, you can give it a common base
oh
wait, I thought that Protocol can be the only base

that'd simplify things, lemme try
class Configurable(Protocol):
def configure(self, option: SomeTypeHere) -> object:
...
class Mixin:
def __init__(self: Configurable):
self.configure(...)
But mixins don't usually have constructors
this one does since it, well, does the exact same thing
But you don't need a self annotation because you already subclass widget
the widget doesn't differ a lot
protocols can't take other bases btw: main.py:8: error: All bases of a protocol must be protocols
Yeah I know
just like I remembered it
You just make a protocol with the methods that you use
Yeah, as I said, that base class is optional with a protocol usage like this. Regardless if it's there or not, super() is gonna complain though.
It looks like MyPy has no issue open with this, which is strange. Anyone who'd try to use protocols like this would run into it easily. Guess I'll open it then.
It's like my second time running into it too
does this work? https://mypy-play.net/?mypy=latest&python=3.10&gist=c140f923a33d8b20fe0761f4e03f7687 @minor nimbus
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 that sounds about right
Huh
Your gist looks promising too though
why I didn't think of that
lemme try
wouldn't that resolve Combobox methods to Entry first?
Also try to use object before using Any
PlaceholderCombobox(PlaceholderEntry, ttk.Combobox) means MRO goes like: PlaceholderCombobox -> PlaceholderEntry -> ttk.Entry -> ttk.Combobox
but I think that's just a matter of reversing the order of base classes there
I'm not actually 100% sure it's like that, but as far as I remember, it's a depth-first search
lemme check
[<class '__main__.PlaceholderCombobox'>, <class '__main__.PlaceholderEntry'>, <class 'tkinter.ttk.Combobox'>, <class 'tkinter.ttk.Entry'>, <class 'tkinter.ttk.Widget'>, <class 'tkinter.Entry'>, <class 'tkinter.Widget'>, <class 'tkinter.BaseWidget'>, <class 'tkinter.Misc'>, <class 'tkinter.Pack'>, <class 'tkinter.Place'>, <class 'tkinter.Grid'>, <class 'tkinter.XView'>, <class 'object'>]
Huh
interesting
TIL it's not strictly a depth-first search
C3 superclass linearization, with preservation of local precedence order
Also RE this, I kinda never understood it
what's the difference?
I'd assume they're almost the same thing
Any disables the type checker for the annotated thing
object is the type that everything inherits
Right, so it works just like Any but doesn't disable type checking altogether?

I guess?
In a few places it does have the same meaning
I see
I just try using object first unless forced to use Any as a last resort
Any is subtype and supertype of any type
object is supertype of any type
x: object
x.a # error
x() # error
``````py
x: Any
x.a # ok
x() # ok
``````py
x: object
y: str
x = y # ok
y = x # error
``````py
x: Any
y: str
x = y # ok
y = x # ok
It's sort of like any vs unknown in ts if you're familiar with that
do type checkers care about the difference between x/2 vs x/2.0?
Yes, int is a subclass of float in the eyes of a type checker
Eg x: float = 5 is fine, but x: int = 3.14 is not
does it matter when deducing the type of an expression? I saw someone write this:
return (left + right) / 2.0
but it seems to pass mypy without the .0 (when returning float)
/ 2 is gonna return a float anyways
It doesn't matter if it's even or not
Well unless you're in python 2
inspect.signature has str values for each annotation, what should I do to actually have them resolved?
even though my function is declared like this:
async def handle_pre_req(
read_administrator: AsyncDependency[Administrator, DependencyDescription],
user_id: Parameter[int, DefaultDescription] = Parameter(1)
) -> ReqHandler:
...
typing.get_type_hints
You're probably using from __future__ import annotations
which turns all annotations into strings
in which case you can still use that I think?
I think that is what the globalns=None, localns=None are for
Nvm, it is right there
I think those are chosen automatically
that is literally their purpose x)
also specify include_extras=True to include Annotated
Is there a nice way to get both defaults and resolved annotations?
Or do I have to join typing.get_type_hints and inspect.signature, taking the annotations from former and defaults from latter?
Not sure what you mean here
I ended up using both typing.get_type_hints and inspect.signature and joining on parameter name, in a: int = 1 I wanted both the int and 1.
But now something else came up:
Oh that is what you meant
from __future__ import annotations
import typing
def a():
class Custom:
pass
def b(custom: Custom):
pass
return b
typing.get_type_hints(a())
This expects Custom to be in global scope instead of a's scope. If I remove from __future__ import annotations then it works but then all annotations are resolved immediately, and forward reference annotations break
So is there a way to get a compromise, where Custom is immediately resolved in above example but forward reference annotations are deferred?
PEP 563 mentions exactly this use case as a use case that would no longer work, so I am guessing there is no mechanism to get around that
I suppose you could do something hacky like
def force(ns: dict[str, Any]) -> Callable[[T], T]:
def _force(f: T) -> T:
f.__annotations__ = get_type_hints(f, localns=ns)
return f
return _force
def a() -> Callable:
class Custom: ...
@force(locals())
def b(custom: Custom): ...
return b
I think there is a way so you dont have to pass locals to force, but I dont know
1.0 / 2.0 is faster than 1 / 2
use float if you can, in this case there will be no conversion from int to float
only a few nanoseconds faster
I probably spent more time typing this message than it would save in total
In [1]: %timeit 1 / 2
14.4 ns ± 0.39 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
In [2]: %timeit 1.0 / 2.0
14 ns ± 0.113 ns per loop (mean ± std. dev. of 7 runs, 100,000,000 loops each)
not even one nanosecond on my machine
if you use constant computations like this, they are computed at compile time, so you're benchmarking fetching a constant
try this:
x = 1
y = 1.0
%timeit x / 2
%timeit y / 2.0
oh right, bad benchmark
it is 10 ns difference with variables
is there good place other than the docs which explains typing.Generic?
mypy docs have a page about Generics https://mypy.readthedocs.io/en/stable/generics.html
and pyright docs have a small section https://github.com/microsoft/pyright/blob/main/docs/type-concepts.md#generic-types
and there is PEP 484 and PEP 483 https://peps.python.org/pep-0483/#generic-types and https://peps.python.org/pep-0484/#generics
and the typing tips articles https://decorator-factory.github.io/typing-tips/tutorials/generics/
Thank you very much : D
hey, I don't even have to plug it myself anymore 😎
https://paste.pythondiscord.com/imiduforon someone help trying to split image into 100 vertical sections and save each section as separate image but image saved at end is same image as inputted
already tried noone answered
You are free to ask again, this is however the inappropriate channel for that :P
Is there some reason I can't inherit from a Generic class and a slotted class? Trying gives me a TypeError with the message multiple bases have instance lay-out conflict, however removing the slots in the non-Generic base makes the error go away, but of course I lose the slotted nature.
Generic has __slots__ = (), maybe that's why? I don't know too well how the interaction between slots and subclassing works
Far as I'm aware, Python just allocates slots for each base, or if the base has no slots a __dict__
Generic has it, and the generic class has it
But inheriting from the generic class and another one breaks somewhere
Do both classes share slots?
Is Never the new <nothing>?
Never is the new NoReturn
But can't it be used where before mypy would say "<nothing>"?
I'm not sure how mypy determines something to be "<nothing>"
im asking this question again because its really annoying me having to add multiple type: ignore's. I have a function with has two use cases;
scenario a:
- user inputs a channel id that may or may not be a valid channel id.
-> my function checks to see if it is a channel, if it is, it returns it, if it isnt, it returns None, hence returning-> Optional[TextChannel]
scenario b: - the bot receives a message with a channel id that will always be a valid channel id.
-> this means that it will always returnTextChannel
self.channel = self._state.cache.get_channel(payload["channel_id"]) when doing this i am running scenario b. this means it always is a TextChannel object.
However, because of the Optional, everytime i do self.channel.id my flake8 says id is not an attribute of TextChannel | None.
sounds like you should have two functions
is there a way to tell mypy to ignore type errors in other modules when invoking mypy foo.py? i don't care about errors in other modules (e.g. bar.py) even though foo.py imports them
def get_maybe_channel(self, _id: int) -> TextChannel | None:
return self._state.cache.get_channel(_id)
def get_channel(self, _id: int) -> TextChannel:
channel = self._state.cache.get_channel(_id)
assert isinstance(channel, TextChannel)
return channel
although this is a little bit of a lie
I think it is follow imports silent
def get_channel(self, _id: int) -> TextChannel:
channel = self._state.cache.get_channel(_id)
if channel is None:
raise RuntimeError('This method was called from an invalid context. The channel is not set!')
return channel
thanks, it was --follow imports but i don't know the difference between silent and skip 🤔
--follow-imports=skip worked for me iirc
ah ty, you are faster than me
x)
silent is what i wanted
I meant to say it was a lie because get_channel can return any channel, not just TextChannel
issue is
def get_channel(self, channel_id: int) -> Optional[TextChannel]:
"""
Searches the internal cache for a channel.
Parameters
----------
channel_id: :class:`int`
The channel id to find.
Returns
-------
channel: :class:`TextChannel`
The channel if found, else `None`
"""
for guild in self._guilds:
channel = guild.get_channel(channel_id)
if channel is not None:
return channel
return None
if channel is not None:
return channel
Ah, in that case
Valid = NewType("Valid", int)
@overload
def get_channel(self, channel_id: Valid) -> TextChannel: ...
@overload
def get_channel(self, channel_id: int) -> Optional[TextChannel]: ...
def get_channel(self, channel_id):
"""
Searches the internal cache for a channel.
Parameters
----------
channel_id: :class:`int`
The channel id to find.
Returns
-------
channel: :class:`TextChannel`
The channel if found, else `None`
"""
for guild in self._guilds:
channel = guild.get_channel(channel_id)
if channel is not None:
return channel
return None
Then when you are passing the id from the bot, you call the function with self.get_channel(Valid(...))
or you can use exceptions to signal an error
Yes. I've been working on making our code base None-safe, and there are so many functions where people return None on invalid input, but then the call sites don't do anything useful with None. Just throw an exception already.
What does None-safe mean?
make the type checker tell you if you use None incorrectly, basically like mypy's strict optional mode
Oh I see
seems complicated… if I made it so that there was a user_inputted arg which asserts the channel when set to false?
if not user_inputted: assert channel #wont assert
@overload
def get_channel(self, channel_id: int, user_inputted: Literal[False]) -> TextChannel: ...
@overload
def get_channel(self, channel_id: int, user_inputted: Literal[True]) -> Optional[TextChannel]: ...
def get_channel(self, channel_id):
"""
Searches the internal cache for a channel.
Parameters
----------
channel_id: :class:`int`
The channel id to find.
Returns
-------
channel: :class:`TextChannel`
The channel if found, else `None`
"""
for guild in self._guilds:
channel = guild.get_channel(channel_id)
if channel is not None:
return channel
return None
But yeah exception method is probably better
hello can someone help me? im new to python and i dont know how to install this module
thanks
what method?
class ChannelNotFound(DiscordException):
"""Exception that is thrown when a get_channel operation fails"""
def get_channel(self, channel_id: int) -> TextChannel:
"""
Searches the internal cache for a channel.
Parameters
----------
channel_id: :class:`int`
The channel id to find.
Returns
-------
channel: :class:`TextChannel`
The channel if found, else `None`
Raises
------
ChannelNotFound
When channel isn't found
"""
for guild in self._guilds:
channel = guild.get_channel(channel_id)
if channel is not None:
return channel
raise ChannelNotFound(f"Could not find channel with id: {channel_id}")
nah don’t want my get_channel to raise an error like that tho
cus then i have to catch it
What is the difference between doing:
if something is None:
...
else:
...
and
try:
something
except Exception:
...
You will have to check for it being invalid either way
you can use this function for the case where you "know" the id is valid, and the existing one for the case where you don't
they are completely different things, one checks if something is None and the other checks if an exception is raised
as for which one you should use, i prefer using returning some kind of failure/sentinel value "internally" and raising exceptions at your api boundaries
You will have to do one either way
so it depends on how the method is meant to be used
the general principle of "don't use exceptions for control flow" is relevant here
if the channel being None is a normal thing that can happen, then use a sentinel value
if it really shouldn't happen, then it's an exceptional circumstance and you should use an exception
TypeScript has the postscript ! operator
const rv = x.y!.z
``` basically means ```ts
const _x_y = x.y
// assume _x_y is not null
const rv = _x_y.z
Not sure what the sentinel method would look like here
Haskell has it, rust has it, doesnt javascript have that now too?
None is fine
everyone but python sadge
Then you are back here
If you use None
yeah i see what you're saying. meh, go with the exception then
it sounds like the channel not-existing is an exceptional circumstance, basically user error
it's analogous to KeyError in dicts
it's really either/or though. i find it slightly weird that discord.py returns None instead of raising an exception on guild.get_channel, but it's possible that they did this for performance reasons internally (catching and handling exceptions is slow)
The nice thing about an exception then is that you can put in more information (e.g. the bad channel id) that will end up in your log. With ! all you'd get is an error
! in TS doesn't throw an error, it just assumes it's not null
it's pretty hard to make exception classes with useful structure
and what if it is null?
depends 🙂
or is it effectively an assertion?
it's javascript, so demons could fly out of your nose
you probably get something like "null has no attribute x" at runtime, right?
ah ok, so it's equivalent to assert x.y is not Null before taking x.y.z
no it's not
if you're accessing an attribute, yes. but if you have console.log(foo!) it may silently propagate
e.g. if you have key: 'foo' | 'bar' | null and you do myObj[key!], then with key being null you will get myObj[null] === undefined
is there a name for that operator
bang?
this person says it is a "non-null assertion" operator
i know that it isn't a runtime assertion
it's an assertion to the type checker though, not a runtime assertion
so javascript doesnt have it?
kotlin has that operator as well, or I guess it's spelled !!
I don't think so
Isnt that what optional chaining is?
that's a different thing
I guess it's different since in JS it seems like no code is being run at runtime, in kotlin you'll always get an exception immediately
optional chaining doesn't throw, you get the expression or None
foo?.bar?.baz
if foo is None, it's just None, otherwise if foo.bar is None, it's just None, otherwise it's foo.bar.baz
Oh that is what I thought was being talked about here
In TypeScript, optional chaining is done with ? (I think it is in JS now)
it's really handy and proposed for python though probably not goign to be accepted
I find the absence of ?. comes up a lot for me in python
Ahh so all ! does is literally take an opntional foo!.bar() and make it concrete without doing if checks
Yeah, I don't personally like that
when your assumption is wrong you're potentially going to get an error late
i'd rather get an error early
it also makes it really ergonomic to forgo null safety
Yeah. That said I'm guessing most languages that have ! also have other convenient operators
Does Rust have that?
Like Kotlin has !! but also has ?: and ?.
I think Haskell has that via unsafeCoarce
just put an extra character, doesn't ask for a comment - why is it not null? (in fact, where do you put a comment around it?)
so !! is convenient but doing the right thing is just as convenient
Rust has unwrap, I think, it would be the closest thing
I think unsafe functions in Haskell are the cursed things... like unsafe in Rust
this is just a partial function
Rust's ? is for propagating errors
Yeah I like unwrap() better, it is... louder?
yeah that's basically the intent I think
src/umd/ReactUMDEntry.js lines 21 to 22
__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOM,
__SECRET_DOM_SERVER_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactDOMServer,```
fromJust should be unsafeFromJust though
It is an unsafe function x)
it is partial, not unsafe 🙂
hmm actually
- Evaluation can lead to an exception or infinite loop. This is a relatively mild sort of unsafety ... except when it's not.
I guess
I mean, if you just hoogle Maybe a -> a you will see that some libraries named that unsafe, so it seems a not fully defined term by convention?
yeah I guess that's true, I thought it was mainly in the context of https://wiki.haskell.org/Safe_Haskell
I mean to be fair, going by that definition head would aslo be unsafe
anyway, this is mildly off-topic here 👀 maybe let's move to #otN if we want to continue this
"mildly" xD
I did have a python question, how do static analyser like pyright and mypy work? Does pyright for example have to implement its own parser? And how do they go about figuring the types in Python, I tried looking for any writing on that but I guess if someone has written about it, it is hard to find
Pyright has a completely separate parser, mypy uses ast (or typed-ast in older versions)
I think beyond that it's mostly standard compiler techniques. For each AST node you infer a type, and from that you build inferred types for more nodes. You may also use a different representation instead of the raw AST
Humm I see, I will lookup about that instead, I think that should yield better results
Eric recently gave a talk about Pyright's architecture at the typing meetup, I think the recording for that should be available
ooh
Oh thanks! I will definitely search for that
the mastermind himself
That means that when Python changed its parser in 3.8, pyright had to also do the same thing over
Right?
the parser change is a CPython implementation detail
Typing Meetup Notes February 16, 2022 Recording Chat transcript Pyright Overview and Architecture, Eric Traut Slides January 13, 2022 Recording Chat transcript dataclasses-json, Charles Li Slides Charles: I used .from_dict in my REST API example, but it should be Person.schema.load (n.b....
yes, but pyright already used a different parser architecture
pyright's parser is designed with error recovery in mind, so it works ok-ish if you are in the middle of writing something
but in general, almost every new language feature requires some work in type checkers
Ah okay, interesting, thanks for the insight and the notes 🙂
def get_channel(self, channel_id: int) -> Optional[TextChannel]:
"""
Searches the internal cache for a channel.
Parameters
----------
channel_id: :class:`int`
The channel id to find.
Returns
-------
channel: :class:`TextChannel`
The channel if found, else `None`
"""
for guild in self._guilds:
channel = guild.get_channel(channel_id)
if channel is not None:
return channel
raise ChannelNotFound("Channel with id ``{}`` not found".format(channel_id))
``` i did that and its still giving me the crappy `error: "id" is not a known member of "None" (reportOptionalMemberAccess)`
you need to change the return annotation
self.thumbnail: Dict[str, str] = None
self.video: Dict[str, str] = None
self.image: Dict[str, str] = None
self.author: Dict[str, str] = None
self.footer: Dict[str, str] = None
self.fields: List[Dict[str, str]] = []
whats wrong with this? Cannot assign member "thumbnail" for type "Embed" got an error like this for every line
None is not a Dict[str, str]
Not really a typing question, the reason pyright is telling you that is because those members dont exist
is self and Embed?
i think optional will do the trick
I think you're not supposed to add new attributes outside of __init__
__slots__ = (
"title",
"url",
"type",
"_timestamp",
"_colour",
"_footer",
"_image",
"_thumbnail",
"_video",
"_provider",
"_author",
"_fields",
"description",
)
thats in the init
it wouldnt be the slots
You are supposed to use the setters
That was a type check error
i added optional and im getting 2 different errors now
well
/home/Caeden/Github/discii/discii/embed.py:61:14 - error: Cannot assign member "footer" for type "Embed"
Expression of type "dict[str, str | None]" cannot be assigned to member "footer" of class "Embed"
Type "dict[str, str | None]" cannot be assigned to type "Dict[str, str] | None"
TypeVar "_VT@dict" is invariant
Type "str | None" cannot be assigned to type "str"
Type "None" cannot be assigned to type "str"
Type cannot be assigned to type "None" (reportGeneralTypeIssues)
You need to use set_thumbnail
and stuff like that
def __init__(
self,
*,
colour: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
color: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
title: MaybeEmpty[Any] = EmptyEmbed,
type: EmbedType = "rich",
url: MaybeEmpty[Any] = EmptyEmbed,
description: MaybeEmpty[Any] = EmptyEmbed,
timestamp: datetime.datetime = None,
):
self.footer: Optional[Dict[str, str]] = None
and on L61:
self.footer = {"text": text, "icon_url": icon_url}
Can you show your code after the change?
uh
Ahhh alright
what part
the part that gives you the error
this is the part that raises ```bash
/home/Caeden/Github/discii/discii/embed.py:61:14 - error: Cannot assign member "footer" for type "Embed"
Expression of type "dict[str, str | None]" cannot be assigned to member "footer" of class "Embed"
Type "dict[str, str | None]" cannot be assigned to type "Dict[str, str] | None"
TypeVar "_VT@dict" is invariant
Type "str | None" cannot be assigned to type "str"
Type "None" cannot be assigned to type "str"
Type cannot be assigned to type "None" (reportGeneralTypeIssues)
def set_author(self, name: str, icon_url: str = None) -> None:
"""
Sets the author field.
Parameters
----------
name: :class:`str`
The author name.
icon_url: :class:`str`
The icon url to assign.
"""
self.author = {"name": name, "icon_url": icon_url}
this is where i set self.author which is defined as self.author: Optional[Dict[str, Union[str, bool]]] = None. The current error is ```bash
/home/Caeden/Github/discii/discii/embed.py:58:14 - error: Cannot assign member "author" for type "Embed"
Expression of type "dict[str, str | None]" cannot be assigned to member "author" of class "Embed"
Type "dict[str, str | None]" cannot be assigned to type "Dict[str, str | bool] | None"
TypeVar "_VT@dict" is invariant
Type "str | None" cannot be assigned to type "str | bool"
Type "str" cannot be assigned to type "str | bool"
Type "None" cannot be assigned to type "str | bool"
Type cannot be assigned to type "None" (reportGeneralTypeIssues)
shit
it should be optional not union
icon_urlshould beOptional[str]- consider using a
TypedDictfor this
{"name": name, "icon_url": icon_url} has type dict[str, str | None], hence the error
a what?
(also None isn't bool)
i realised
yeah it worked as Optional not Union
thanks guys
a TypedDict would allow you to specify that name should be a str, but icon_url can be str | None for example
also yes, if you have a fixed set of keys, you should use TypedDict, or better a proper object (like a dataclass or NamedTuple)
but that would require learning what a NamedTuple is
It is just a tuple that you can access the members via the names, it is still however just a tuple
The interface is very simple
!e
from typing import NamedTuple
class A(NamedTuple):
x: int
y: float
z: str
a = A(1, 3.14, "123")
b = A(z="sdf", x=1.4, y=5)
c = (1, 3.14, "123")
print(a)
print(b)
print(c)
print(a.y, a[1])
print(a == c)
@hearty shell :white_check_mark: Your eval job has completed with return code 0.
001 | A(x=1, y=3.14, z='123')
002 | A(x=1.4, y=5, z='sdf')
003 | (1, 3.14, '123')
004 | 3.14 3.14
005 | True
hmm
i might bring myself around to using a NamedTuple
btw should i use slots to caceh the embed attr's?
You should really just use a dataclass though
Humm, that one you might want to look at the module page for it, it has many components to it. But it is worth learning since they are usable almost everywhere
That has even more to it, but yeah attrs > dataclass 100%
Overlapping slots? Nope.
They’ve got unique sets of slots
Not sure then, from a little searching it seems that you strictly cannot inherit from two classes directly where both have nonempty slots

God save me
How would you do it then?
My structure basically requires me to subclass a provided base class (the generic one) and then use mixins to add on to it
Given that my mixin is used in three different classes, I don’t want to dupe it so just used multi inheritance
What does your hierarchy look like exactly? I tried some variations on 3.9 and didn't get an error
...: __slots__ = ("x",)
...:
In [10]: from typing import *
In [11]: class Y(X, Generic[TypeVar("T")]): pass
In [12]: class Y(X, Generic[TypeVar("T")]): __slots__ = ("y",)
It is strictly when you import from two or more slottedclasses, I found this very old mailing thing where I think it somewhat says why they can't, although not in depth https://mail.python.org/pipermail/python-list/2002-December/150800.html
I see, but then Generic shouldn't make a difference because its __slots__ is empty
As for using slotted mixins, I think you just arent supposed to
Yeah, I think this issue is just from that fact that they are using slotted mixins
Or a Generic parents class that defines its own slots and then inheriting from it with another slotted mixin
You can't multi-inherit because the way slots work is that Python appends the pointers to the end of the object. If you single inherit that's fine since they just go after the existing ones, but if you multi inherit the two sets of base class slots are going to collide.
Is there any reason why pyright infers Literal type?
Even when there is a redefinition of the variable in the same scope
from typing import Literal
a = 5
reveal_type(a) # Type of "a" is "Literal[5]"
a = 2
reveal_type(a) # Type of "a" is "Literal[2]"
from typing import Literal
a: Literal[5] = 5
reveal_type(a) # Type of "a" is "Literal[5]"
a = 2 # "Literal[2]" cannot be assigned to type "Literal[5]"
reveal_type(a) # Type of "a" is "Literal[5]"
it's just super aggressive type narrowing I think
x: bool = True
a: int = 0
if x:
a = 5
reveal_type(a) # Type of "a" is "Literal[5]"
else:
a = 2
reveal_type(a) # Type of "a" is "Literal[2]"
reveal_type(a) # Type of "a" is "Literal[5, 2]"
from collections import defaultdict, deque
from typing import *
class _:
def numBusesToDestination(
self, routes: List[List[int]], source: int, target: int
) -> int:
if source == target:
return 0
mp: Dict[int, Set[int]] = defaultdict(set) # {stop:set(bus#)}
for bus, stops in enumerate(routes):
for stop in stops:
mp[stop].add(bus)
queue = deque([(source, 0)]) # deque([(stop,bus_count)])
visited_stops = set()
visited_buses = set()
while queue:
stop, count = queue.popleft()
if stop == target:
return count
for bus in mp[stop]:
if bus not in visited_buses: # prevent loop
visited_buses.add(bus)
for stop in routes[bus]:
if stop not in visited_stops: # prevent loop
visited_stops.add(stop)
queue.append((stop, count + 1))
return -1
why isnt mypy telling me visited_stops = set() visited_buses = set() dont have annotations? if I delete the entire while queue loop it then complains. is it inferring the types off of visited_buses.add(bus) and visited_stops.add(stop)?
yes mypy has that convenience feature, if you put a reveal_type after the loop, it would show set[int]
Yup, it infers bus is int from the enumerate and then it narrows from set[Any] to set[int]
pyright doesn't do it, and complains
Isn't that weird though? Literal[5] is not compatible with Literal[0] but it still shows it as being Literal
Also on top of that, if you do this
from typing import Literal, Final
x: bool = True
a: int = 0
reveal_type(x) # Type of "x" is "Literal[True]"
if x:
a = 5
reveal_type(a) # Type of "a" is "Literal[5]"
else:
a = 2
reveal_type(a) # Type of "a" is "Literal[2]"
reveal_type(a) # Type of "a" is "Literal[5, 2]"
So x it literal True
then shouldn't a = 2; reveal_type(a) be Type of "a" is Never
Maybe, but I'm not sure that behavior would be too helpful in practice
it became more aggressive with narrowing in newer versions I think, and the docs kinda contradict the current behavior https://github.com/microsoft/pyright/blob/main/docs/type-concepts.md#type-narrowing
all those int inferred in that example are Literal if you try now
from typing import Literal, Final
a: int | str = "3"
reveal_type(a) # Type of "a" is "Literal['3']"
if isinstance(a, int):
reveal_type(a) # Type of "a" is "<subclass of str and int>"
elif isinstance(a, str):
reveal_type(a) # Type of "a" is "Literal['3']"
else:
reveal_type(a) # Type of "a" is "Never"
I mean I agree, tbh to me the really weird one is the Literal inference
you don't get a "Never" in the int case here also
when doing the "Never" analysis it respects the actual annotation I guess
but it is confused about the Literal[3] and int together at the same time, and gives <subclass of str and int>
which is a bit funny looking
Yeah, it is a little bit all over the place
from typing import Literal, Final
a: int | str = "3"
reveal_type(a) # Union[builtins.int, builtins.str]
if isinstance(a, int):
reveal_type(a) # Revealed type is "builtins.int"
elif isinstance(a, str):
reveal_type(a) # Revealed type is "builtins.str"
else:
reveal_type(a) # ...
Literal[5] is compatible with int though, and that is the annotation for the variable
Literal[0] is just the narrowed type wherever it is sure that the value will only be 0
and if you don't annotate, then it infers the type to be the union of all assignments
so again assigning a new literal value is fine because it just gets added to the union.
I agree that the underlying type is fine, just the way it reports the types and the way the types are actually being inferred is at least not correct, this is a rather pointless issue as you would only see it when under a magnifying glass
still weird though x)
yeah it is a bit inconsistent in its confidence, it a bit extra confident about the narrowing only during reveal_type, but not in the isinstance checks
I think it is just lying to you. It knows it is not a Literal[0], if it actually was one it would report an error
a: Literal[0] = 0
reveal_type(a) # Type of "a" is "Literal[0]"
a = 1 # "Literal[1]" cannot be assigned to type "Literal[0]"
reveal_type(a) # Type of "a" is "Literal[0]"
a: int = 0
reveal_type(a) # Type of "a" is "Literal[0]"
a = 1
reveal_type(a) # Type of "a" is "Literal[1]"
there's a difference between "current narrowed type" and "type assignable to variable"
def f(x: int | str):
if isinstance(x, str):
reveal_type(x)
x = 5
reveal_type(x)
yeah it knows it is not a Literal[0] but it's not lying, it is just narrowing the type to Literal[0] which is not wrong if you just assigned 0 in the previous line
same thing is done by mypy also for other types, but it doesn't do Literal
Really?
I find that mypy always shows me the upper bound of the type when I ask it to reveal it
And yeah I just noticed this is not special to Literal
pyright
class A: ...
class B(A): ...
a: A = B()
reveal_type(a) # Type of "a" is "B"
a = A()
reveal_type(a) # Type of "a" is "A"
mypy
a: A = B()
reveal_type(a) # Revealed type is "__main__.A"
a = A()
reveal_type(a) # Revealed type is "__main__.A"
ugh I mean mypy doesn't narrow it down based on assignment, but it does narrow down based on other checks like isinstance, but lets you assign a different type than the one narrowed to, like @hallow flint's example shows
pyright
a = B()
reveal_type(a) # Type of "a" is "B"
a = A()
reveal_type(a) # Type of "a" is "A"
mypy
a = B()
reveal_type(a) # Revealed type is "__main__.B"
a = A() # Incompatible types in assignment (expression has type "A", variable has type "B")
reveal_type(a) # Revealed type is "__main__.B"
I guess that is true, it would narrow down to str | int to str but then I could still assign it a int
yup, with mypy
def f(x: int | str):
if isinstance(x, str):
reveal_type(x) # builtins.str
x = 5 # allowed
reveal_type(x) # builtins.int
Ah wait, nvm, that is the difference thought
Mypy narrows down, not up
Actually, thinking about it again, if you take the space between declarations as a space for narrowing, then it narrows down to a Literal and then when reassigning that is the same as leaving that function scope
And in this example, it takes the upper bound to be Any, then shows B as it narrowed it to B, and then A when it narrows it down to A
While Mypy infers it to be B, and then it doesnt allow A
I never realised how different the two were
yeah mypy locks in to the type on the first assignment, with some exceptions for convenience
pyright locks the type only if you annotate
otherwise it keeps taking union of the stuff you assign to the variable
yeah it's often annoying
def foo() -> int: ...
# ok
if foo() > 5:
x = None
else:
x = "smol" # ok
if not (foo() > 5):
y = "smol"
else:
y = None # error: Incompatible types in assignment (expression has type "None", variable has type "str")
like... these are literally the same
Can someone explain what's wrong with type hint for Generator object yielded by establishConnection()?
class TransactionRepo:
"""Class cointaining temporary transaction data and handling connection and queries to the DB during session"""
@classmethod
@contextmanager
def establishConnection(cls): #-> Generator[TransactionRepo, None, None]:
"""Read .ini file and load database config parameters and database column name mappings.
Create ContextManager handling connection with the database.
Raises:
Exception: raised in case of unexpected config file formatting
Yields:
Generator[TransactionRepo, None, None]: ????
"""
# *snipped*
with psycopg2.connect(**postgresConfig) as connection:
with connection.cursor() as cursor:
yield cls(connection, cursor)
connection.close()
I get TransactionRepo" is not defined, so it seems my TransactionRepo is not yet defined, therefore how can I type hint it?
either do from __future__ import annotations at the top, or Generator["TransactionRepo", None, None]
Yup, that works, but could you explain what does this import do or what " " do in type hint?
when the def establishConnection(cls) -> Generator[TransactionRepo, None, None] line is run, the class TransactionRepo isnt defined yet so it errors when it tries to evaluate the annotation
with that import, annotations arent evaluated and treated as strings by default
"" isnt special in a typehint, it makes a normal string. but typecheckers know that they should treat these as types and not normal strings
But if we pass a string instead of type object, do I understand that we also postpone type hint evaluation after every class/function/method is defined?
yes
Perfect, thank you very much!
Aaaaah, thank you all for the help!
can also do Generator[Self, None, None] (with typing_extensions.Self)
Also, your connection.close() call should ideally be in a try-finally block
with pysocpg2.connect( should be enough - it doesn't need closing twice @onyx kayak
No @onyx kayak , as per the psycopg docs, with on the connection only ends the transaction
wut
wow that is unexpected
tbh all the dbapi-s seems to have all sorts of weirdness. Use them via sqlalchemy
with (
contextlib.closing(psycopg2.connect(**postgresConfig)) as connection,
connection,
connection.cursor() as cursor,
):
yield cls(connection, cursor)
This will drop the transaction though, no?
The connection will be closed without starting or ending a transaction
will it?
shouldn't do
there's a yield there
let's say I have a Generator[int, int, int] I can't g.send(None) it to start it: https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=4e69e85942b1a9945b309e0d42ae2369
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 wait I didn't see, the connection is withed as well
sorry
And why is that? Due to psycopg2 connection.close() funkiness mentioned above? I assume with normal with ... as functionality, this should not be necessary?
Like this?
try:
with psycopg2.connect(**postgresConfig) as connection:
with connection.cursor() as cursor:
yield cls(connection, cursor)
finally:
connection.close()
yes, like that. you need it because otherwise the connection.close() call won't get executed if the body of the with throws an exception
Understood, thanks!
with psycopg2.connect(**postgresConfig) as connection:
try:
with connection.cursor() as cursor:
yield cls(connection, cursor)
finally:
connection.close()
otherwise it will fail if psycopg2.connect fails with an exception
hm wouldn't that close the connection before the transaction?
hmmmmmmmmmmmmmmmmmmm
it would
connection = psycopg2.connect(**postgresConfig)
try:
with connection:
with connection.cursor() as cursor:
yield cls(connection, cursor)
finally:
connection.close()
this^ seems correct?
does connection.__enter__ return self? if not might need this ``` connection = psycopg2.connect(**postgresConfig)
try:
with connection as transaction:
with transaction.cursor() as cursor:
yield cls(transaction, cursor)
finally:
connection.close()
psycopg/connection_type.c lines 422 to 425
rv = (PyObject *)self;
exit:
return rv;```
so like this imho:
with (
contextlib.closing(psycopg2.connect(**postgresConfig)) as connection,
connection,
connection.cursor() as cursor,
):
yield cls(connection, cursor)
I don't like finally: foo.close() - I prefer contextlib.closing
But does contextlib.closing() allow you to handle possible Exception in psycopg2.connect()? It abstracts away except: block
Kind of a midly strange idea,
Wouldn't it be great if we could annotate ParamSpec args by specifying an slice?
Like suppose
P = ParamSpec("P")
And then,
we could for example modifying like
[P.args[-1], int, P.args[0], P.kwargs]
To mention that last positional arg comes first, first comes last, and there'll be an instance of int in the middle.
Analogically to what @grave fjord proposed: to handle another Exception threw by psycopg2.connect(), I'd have to enclose it in additional try block?
try:
connection = psycopg2.connect(**postgresConfig)
except:
stuff()
try:
with connection as transaction:
with transaction.cursor() as cursor:
yield cls(transaction, cursor)
finally:
connection.close()
type checker implementors are already crying for help 😄

No really, this seems like a very good idea to me
oh I meant that it might be a bit complex to implement
hmm although, TypeScript does have it, kind of
Not really, I would just let that exception bubble up
Ok, i guess this is the ultimate beginner question. If it bubbles up, how can I later address this raised Exception for it to not stop my app from running?
I mean the main issue here is whether there is a way for your app to address that exception without failing
and what that way is
wherever you can address it, that's the point that you catch it
the more context you need to address the error/exception, the earlier you would typically catch it, so that more stuff is still around.
conversely if you need almost no context (e.g. print a backtrace and exit) then you'd catch it close to main
You should start by thinking about all the things that could stop that connection from succeeding, and which cases you can actually do something with.
E.g. maybe for certain connection failures you can simply retry.
retrying is something you'd obviously want to do relatively locally, for example
I feel like I'm missing fundamentals here. All you've written makes perfect sense. Is there a way for my app to carry on with execution after raising Exception (of course if I deem it's safe for it to do so, and it won't trigger more errors). Till now my knowledge is limited to handling in by except block right when Exception is raised, otherwise the whole script/program stops
The part which confuses me is - if exception is raised, i need to handle it straight away, hence another try block. How can I then let it 'bubble up'
if you handle it in the function that calls the function that raises the exception
say you have foo(bar(baz(qux())))
so, you call qux first, then feed the result into baz, which gets called and ha sits result fed to bar, which then gets called and feeds its result to foo()
now lets say that qux just contains:
def qux():
raise RuntimeError("oh no!")
You can put try/except in baz, or in bar, or in foo
and catch the exception in any of those places
You'll enter the first relevant except block
this is what is meant by "bubbling up"
actually, hah, this example is broken
sorry, let me write it out properly
What happens with poop2() in this case?
def qux():
poop()
raise RuntimeError("oh no!")
poop2()
it doesn't run
def foo():
bar()
def bar():
baz()
def baz():
qux():
def qux():
raise RuntimeError("oh no!")
that's what I get for rushing, anyhow. Okay
So in this code example, we have a proper call stack
Take your time, I'm all ears
As written, nothign is catch the exception
so if I write a program that is just
foo()
then foo will call bar will call baz will call qux
so thats' your call stack
then qux throws. There's no except in qux so the exception "bubbles out of" qux. qux was called by baz, so now the exception keeps bubbling through baz, but there's still no except statement
it will keep going up the call stack until it either hits an except, or until it gets to the main function, and ends your program
If I change bar to:
def bar():
try:
baz()
except RuntimeError as e:
print("hello!")
And then I rerun my program
now my program doesn't crash at all, because bar catches the exception and doesn't let it keep bubbling up
and instead prints hello
Notice how the exception was thrown from qux, and went "through" baz, and gets caught in bar
Yeah it often just takes a good explanation, most of this stuff isn't too bad, but the quality of resources can vary a lot
what's valued about exceptions often is that "intermediate" code doesn't have to be changed to handle errors.
In this example, qux is generating an error, bar is handling an error, but baz is sometimes called "error neutral" it doesn't create or handle errors, and its code reflects that
this gives you flexibility to change where things are handled while minimizing refactoring
the downside is that none of this is a) apparent while reading the code, and b) not tracked by the type system
So could it happen that outermost methods/functions are actually built to handle myriads of exceptions thrown along the way inside of them?
yeah, it can happen, but it usually doesn't
for reasons related to what I first said
basically, the farther you are away from the exception, the more stuff related to the exception is already gone
Like say there's some local variable in baz() that you needed to feed to qux, actually
def baz():
x = get_some_var()
qux(x)
now, say you catch the exception raised by qux in bar still (baz's parent)
by the time you're in bar, baz is already done (interrupted by an exception), so x is already gone
you can't check x's state, or use it again for the qux operation
So usually as you go farther and farther up the call stack, the exceptions tend to get caught and handled in more generic ways
exception handling utilizes inheritance, as well. So typically, as you go up and up the call stack, you are also going up and up the inheritance hierarchy of exceptions.
by generic ways you mean, possibly not trying to remedy it, but e.g. stating 'it didnt work'
more generic ways*
I mean primarily, takikng the same action for a larger and alrger class of exceptions
But yeah the most common thing you can really do generically is notifying that it didn't work, restarting, etc
A lot of my scripts catch Exception at top level, and then notify in some way that there was a problem
To handle all the errors that are "unexpected" and don't ever come up
Or rather, hardly ever come up
Ive got your point
I have other scripts that for example, first catch exceptions from my notification framework itself. If I have an error from the notification framework, then I just log and exit, can't do anything else.
And then I catch all other exceptions and notify + log
I can't believe you wrote this wall of text. Thank you so much for taking time to explain this. It clarified so much to me
Most probably I'll read through it few more times in the coming days 😉
hah yeah, I'm a repeat offender when it comes to walls of text
No problem, feel free to ask follow up questions though technically we should probably use a different channel
Will do, in case of any further questions. I'll find more suitable one 😉
let's say I have a Generator[int, int, int] I can't g.send(None) it to start it: https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=4e69e85942b1a9945b309e0d42ae2369
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
huh, interestingly, next(gen) works
which doesn't seem correct
Generator[int, int, int] shouldn't be considered an Iterator[int], should it?
Does it not follow the same semantics of an Iterator?
Generators that are Iterators get sent None, while a Generator[int, int, int] wants to be sent ints
Humm, so what is the lie here?
mypy lets you call next on a generator that accepts ints
Yes but if you look at typeshed isnt it ok?
def f() -> Generator[int, int, int]:
x = yield 37
assert isinstance(x, int)
yield 1
return 5 + x
next(f())
next(f()) # aaah!
right, the type system doesn't cover this case
class Generator(Iterator[_T_co], Generic[_T_co, _T_contra, _V_co]):
def __next__(self) -> _T_co: ...
@abstractmethod
def send(self, __value: _T_contra) -> _T_co: ...
@overload
@abstractmethod
def throw(
self, __typ: Type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ...
) -> _T_co: ...
@overload
@abstractmethod
def throw(self, __typ: BaseException, __val: None = ..., __tb: TracebackType | None = ...) -> _T_co: ...
def close(self) -> None: ...
def __iter__(self) -> Generator[_T_co, _T_contra, _V_co]: ...
Yeah, I understand it is incorrect
Is the lie that it should inherit, or that the method should be implemented this way?
Dont you also have to prime Generators though?
maybe it shouldn't've inherited from Iterator, but instead be something like ```py
def iter(self: Generator[_T_co, None, _V_co]) -> Self: ...
def next(self: Generator[_T_co, None, _V_co]) -> _T_co: ...
That might work, yes
hmmmm
but __iter__ needs to return an iterator, right?
i.e. T is iterable iff T.__iter__ returns an iterator
isn't then the iterableness of Generator technically undecidable? 
I think this is fully solved by defining an iterator as only having __next__. And then __iter__ is part of the Iterable protocol, unrelated to iterators
but that might have other issues, like breaking millions of programs perhaps
That is just a minor inconvenience
Iterable and Iterator are already mutually recursive, I had to put a recursion guard in when implementing protocols in pyanalyze to handle them
presumably other type checkers also handle this correctly so it might be fine
send a typeshed PR and watch how badly mypy explodes 🙂
it could help me improve my streak though 😄
i added # flake8: noqa to the top of my file but im still getting flake8 issues about the file 😢
is that still a thing?
also apparently a line at the end of a file is good
i thought that was bad
?
Nobody uses Iterable/Iterator anyway it's all about SupportsIter[SupportsNext[T]]
Iterable/Iterator are the ideal understanding of what a thing you can for/next is. But SupportsIter and SupportsNext represent the actual mechanics of what you really need to get it to work
I've always used Iterable/iterator in my code, and what I've seen in annotations
I've never seen SupportsIter/SupportsNext
I guess by "uses" you mean "the part of the API that people actually mean" or something like that?
As opposed to a more literal sense
Ah the nobody uses x is a meme
"nobody goes to that restaurant as it is too crowded"
I know that saying, but like Jelle I'm still confused on what it means here
That's one of many Yogi Berra sayings
🙂
also, wow, I just found out that Yogi Berra lived in Montclair (while idly looking at his wikipedia page)
that's a stone's throw from me
But then you still would have the "primenes" of the generator as an issue no?
yep
So you would need yet another type? Like "PrimedGenerator" or another type var to indicate if it is primed or not
and then overload based on that typevar
I'm pretty sure yield fromables don't even need __next__
I think yield from makes do with whatever it can find on the object
I don't think another type would help
as v.send(None) wouldn't change the type of v from an unprimed generator to a primed one
session types for generators 
Wonder for cases where a file has a stub, do you still typehint or is it one or the other?
type checkers ignore implementation files if there is a stub. If you control the .py file, there's little reason to add a separate stub files though
Okay, thanks 👍
Oh lol I confused that with iter, you're right that wouldnt work
Imho the fix for this is for v.send to throw away the first call rather than force it to be None
(I was looking at how typescript fixed the problem and they just don't have the problem)
Why does mypy not like this? ```py
@overload
def infer_schema(pandas_obj: "pd.Series[Any]") -> SeriesSchema:
...
@overload
def infer_schema(pandas_obj: pd.DataFrame) -> DataFrameSchema:
...
error: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader
Swapping the order doesn't work and pyright is happy with it
Maybe it thinks these classes are Any? Then the second overload is useless because the first will always match
Ah yeah that's it, thanks
Is mypy ignoring the pandas installation because it doesn't have a py.typed file yet?
I'm new to pandas so I don't know what the state of typing is but it looks like it's at least partially typed
you can install https://pypi.org/project/pandas-stubs/
it is not very good either though
they were discussing with pandas devs and MS to include their tests in pandas too
and there is an open pull request for that here https://github.com/pandas-dev/pandas/pull/45594
Typing test stubs originally authored by Joanna Sendorek, Zbigniew Królikowski and pandas_stubs contributors: https://github.com/VirtusLab/pandas-stubs/graphs/contributors
Adapted to pandas needs b...
Thank you!
I think so yes. Not sure why they don't have py.typed, they do have annotations
Is there a setting to report if a library is being implicitly set to Any?
I think that's the default. ignore_missing_imports suppresses the error
hey, i want to typehint some inheited classes i have
@dataclass
class RowLocator:
facet: Union[Facet, RedFacet, BlueFacet, BlueTwo, BlueOne]
but it looks so messy
any ideas how to do it more correctly?
if you annotate it as the base class, all the subclasses will be allowed as well, don't have to Union them
It's not the same though
class A:
pass
class B(A):
pass
class C(A):
pass
Union[B, C] is different from A
do they share some base class?
can you explain more why you need this?
yeah depends on why they were unioning, I just guessed what they wanted
yes, my editor complains when i just use the base class.. but yes, Facet is the base class
because my editor suggested it
I dont need it, i want to practise typing
I have attributes in my base class and all the way down to the red and blue Facets
well, i did this first, but my editor complained. So since im a novice when it comes to typing, i thought i was doing it wrong
in what way does it complain?
can you show more code maybe?
orange squiggly line
sure
and what does it say?
what type checker are you running?
class Facet:
FACET_FAILURE = 'facet_failure.png'
ROW_START = 'row_start_red.png'
class RedFacet(Facet):
COLOUR = 'RED'
ROWS = 1
ROW_END = 'rwo_red.png'
BUTTON = 'red.png'
FACET_SUCCESS = 'red_facet_success.png'
FACET = 'red_facets.png'
FACET_OPEN = 'red_facet_open.png'
class BlueFacet(Facet):
COLOUR = 'BLUE'
ROWS = 2
FACET_SUCCESS = 'blue_facet_success.png'
FACET_OPEN = 'blue_facet_open.png'
ROW_END = 'row_blue.png'
class BlueOne(BlueFacet):
BUTTON = 'Blue1.png'
FACET = 'blue_one_img.png'
class BlueTwo(BlueFacet):
BUTTON = 'Blue2.png'
FACET = 'blue_two_img.png'
pycharm default i think
pycharm?
yes
@livid rampart well, Facet doesn't have a BUTTON attribute
well, you're trying to access Button, but that's not defined on facet
yes, im aware, hence my union
it's only defined on RedFacet, or on BlueOne or on BlueTwo
yeah, but that union shouldn't contain Facet itself
but Facet has attributes i do need
Do you ever create instances of Facet? or any subclass?
that's fine, those other classes inherit those attributes
no, i was not planning to
unless you plan on actually passing Facet instances, or BlueFacet instances, you shouldn't specify them
facet: Union[BlueFacet, BlueTwo, BlueOne] so this would work then?
not quite
Then why do you even make these into classes, just have a Facet object, like ```py
red_facet = Facet(colour="RED", rows=1, ...)
oh.. i see.. why?
it will have something else later
that is unique to the blue facets
then you can't expect type checker to allow you to access Button there though
you could do so with an isinstance check before that
and if it's BlueFacet whcih doesn't have Button, you can set it to none, or access something else
looked nice in my head 😄
because type-checker would then need to assume that whatever attr you access on a variable with that kind of union, it would need to implement all of the variables which are shared across the whole union
you could just make a Protocol if you just want .BUTTON
this means that if you passed a union of some base class and a more specific class, you can only ever safely use the variables defined on that base class, even if the special class has some more, the other class doesn't and the variable can be an instance of either of those (union)
by using that union, you're basically just telling the type-checker "hey, this variable will be set to an instance of one of these types, only allow me to access the things that all of these types share"
if you want to say both Facet subclass and has .BUTTON that would be more problematic, I think that would need intersection types
im not sure i understand your union explaination @brazen jolt
If you have animal: Union[Cat, Dog], you can't do animal.meow()
oh.. hell what
becasue meow isnt in Dog?
yes
yeah, you're telling the type-checker that it's one of those, but that means it can't allow you to access things that are only in one of those
so if animal is a dog, it will explode at runtime
what should i use instead of union then?
yeah type checker will allow only the stuff that works for each item in the Union
so only shared attributes?
union isn't a combination, it means you'll only be able to do things that both of the classes implement
yes
Have a Union[RedFacet, BlueOne, BlueTwo]
for example if both Cat and Dog implemented eat, you could access it
ahhh...
I would personally do this. I hate traversing an inheritance hierarchy and run the interpreter in my head to find what the attribute is
this feature is here to protect you from doing something that wouldn't work if the type was the other union types, even though it would work for one of them
yes, but I have other plans for the classes later
Also it'll need to be Union[type[RedFacet], type[BlueOne], type[BlueTwo]]
(if you're passing in the classes and not the instances)
type and not Type?
typing.Type is deprecated as per PEP 585
typing.Type for python 3.8 but 3.9 already supports type[]
since Python 3.9, you can use type[...]
fancy, we can just use type like i can use list and dict?
right
nice
in fact typing.List and typing.Dict are deprecated as well
there is a prophecy that one day, python's static and dynamic type systems will finally fully embrace each other
and ride off into the sunset
python 5
pycharm bug
double union is probably not necessary 😺
ahh.. here pycharm helps me again
i keep doing selfself all the time as well
When you type self, but the autocomplete is still showing with self. When you press TAB to autocomplete (and hide the menu) it still inserts self causing selfself. I get this with Visual Studio Code
Ah right I type everything in manually
why doesn't this typecheck?
CT = TypeVar("CT", bound=type)
class _Finalize:
def __init_subclass__(cls, *args, **kwds) -> None:
if not kwds.get("_root"):
raise TypeError(f"Inheriting from {cls.__name__} is prohibited")
def __init__(self, **kwargs) -> None:
# Decorator support
self.kwargs = kwargs
def __call__(self, cls: CT) -> CT:
class Ret(cls, _Finalize, **self.kwargs):
# This doesn't typecheck ?
# Expected class type but received "CT@__call__" at passing cls
# Expected class type but received "Self@_Finalize" at unpacking and passing self.kwargs
...
# Should do `return type(...)` than doing this
# doin some testing
return Ret
We take in a class and return the same class?
problems are at __call__
I use autocomplete even for discord emojis
y
faster!!!
hmm vscode doesn't even suggest self to me
maybe they got complaints about selfself and hacked some weird solution
honestly I tried an IDE once and typed a parenthetical and it ignored me and I dismissed the concept wholesale
?
just a quick demo for emoji autocompletion
sorry, we're wildly off-topic 👀
what's everyone's least favorite built in function type annotation? Mine's anext
what's wrong with anext?
I don't like it
Mine is eval :P
you mean its existence or its annotations in typeshed
if the former, I agree; if the latter, please file a PR
oh yeah I think I need to review that
ok but what's your least favorite built in function type annotation
def pow(base: int, exp: int, mod: Literal[0]) -> NoReturn: ...
@overload
def pow(base: int, exp: int, mod: int) -> int: ...
@overload
def pow(base: int, exp: Literal[0], mod: None = ...) -> Literal[1]: ... # type: ignore[misc]
@overload
def pow(base: int, exp: _PositiveInteger, mod: None = ...) -> int: ... # type: ignore[misc]
@overload
def pow(base: int, exp: _NegativeInteger, mod: None = ...) -> float: ... # type: ignore[misc]
# int base & positive-int exp -> int; int base & negative-int exp -> float
# return type must be Any as `int | float` causes too many false-positive errors
@overload
def pow(base: int, exp: int, mod: None = ...) -> Any: ...
@overload
def pow(base: float, exp: int, mod: None = ...) -> float: ...
# float base & float exp could return float or complex
# return type must be Any (same as complex base, complex exp),
# as `float | complex` causes too many false-positive errors
@overload
def pow(base: float, exp: complex | _SupportsSomeKindOfPow, mod: None = ...) -> Any: ...
@overload
def pow(base: complex, exp: complex | _SupportsSomeKindOfPow, mod: None = ...) -> complex: ...
@overload
def pow(base: _SupportsPow2[_E, _T_co], exp: _E, mod: None = ...) -> _T_co: ...
@overload
def pow(base: _SupportsPow3NoneOnly[_E, _T_co], exp: _E, mod: None = ...) -> _T_co: ...
@overload
def pow(base: _SupportsPow3[_E, _M, _T_co], exp: _E, mod: _M = ...) -> _T_co: ...
@overload
def pow(base: _SupportsSomeKindOfPow, exp: float, mod: None = ...) -> Any: ...
@overload
def pow(base: _SupportsSomeKindOfPow, exp: complex, mod: None = ...) -> complex: ...
nice

why is pow builtin and not in math.pow
