#type-hinting
1 messages ยท Page 53 of 1
That'd be entirely up to your IDE whether it supports that.
Yeah, to be clear it works in pycharm (which is what our team uses) but I'm quite surprised
Also, instead of a type alias, you might want to look into typing.NewType - it lets you instead specify the business type is a subclass of that at type check time, so the checker will refuse passing in the original type without conversion.
This is specified in https://www.python.org/dev/peps/pep-0257/#what-is-a-docstring
"String literals occurring immediately after a simple assignment at the top level of a module, class, or init method are called "attribute docstrings"."
This seems like a very good idea, I'll make a note of it in this review
really appreciate the reference, good call
huh! i didn't realize this was part of a pep, i thought it was a sphinx extension
great find
now i can convince my manager to let me use these, after he told me to stop
glad to be of service, anything else your manager needs convincing about?
It is a bit inconsistent runtime-wise, since the interpreter doesn't handle these at all.
yes, please convince him to go back in time 18 months and write tests before putting the application into production, and to draw up a high-level architecture plan too
i love this
how to type hint
a func
which has 3 args: a, b, c
if a is 1
then
c should be None n only b should be specified
overloads
n when a is 2, then b should be None n only c should be specified
with Literal? you might have a hard time getting a type checker to figure that out
consider not using "magic numbers" for this
you can use overload and Literal, but you often cannot statically verify when something is a literal 1 as opposed to any other int
why not use an enum instead? you can use IntEnum if you really need it to be an integer
enum?
!d enum
New in version 3.4.
Source code: Lib/enum.py
An enumeration is a set of symbolic names (members) bound to unique, constant values. Within an enumeration, the members can be compared by identity, and the enumeration itself can be iterated over.
Note
Case of Enum Members
Because Enums are used to represent constants we recommend using UPPER_CASE names for enum members, and will be using that style in our examples.
surely verifying the enum has the same issue
not with python enums
tiz is the code btw
from typing import Literal, overload, Optional, Union
@overload
def foo(a: Literal[1], b: int, c: None = ...) -> int: ...
@overload
def foo(a: Literal[2], c: int, b: None = ...) -> str: ...
@overload
def foo(a: Literal[1, 2], b: Optional[int] = ..., c: Optional[str] = ...) -> Union[int, str]: ...
def foo(a: Literal[1, 2], b: Optional[int] = None, c: Optional[str] = None):
if a == 1: return b
elif a == b: return c
reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
n mypy gives me these errors
untitled.py:12: error: Overloaded function implementation does not accept all possible arguments of signature 2
untitled.py:16: note: Revealed type is "builtins.int"
untitled.py:17: note: Revealed type is "Union[builtins.int, builtins.str]"
okayy
actually you might be right @soft matrix , let me try
if you are only using this at a top level (ie mypy knows they are literal[1/2]) i think the approach you already have is fine
okay
i don't know what i was thinking, of course enum still needs literal ๐คฆโโ๏ธ
yes
ya
mypy's working fine now
but the error
untitled.py:12: error: Overloaded function implementation does not accept all possible arguments of signature 2
still occurs
weird error message
?
from __future__ import annotations
from typing import Literal, overload
@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...
@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...
def foo(a: Literal[1, 2], b: int | None = None, c: str | None = None) -> int | str | None:
if a == 1:
return b
elif a == b:
return c
else:
return None
reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
this works fine
oh or that ig
i m a bit confused with overloads lmao
i thought that's what you were suggesting
my understanding of the situation is that mypy was confused by you switching between keyword args and positional args
wat's the diff between keyword args n positional args
def f(x, y=1), x is positional and y is keyword
often shortened to "arg" and "kwarg"

y would just be an optional argument in this case
did you mean just f(x, y=1)
as in while calling it
i'm fudging a bit
people refer to them by those terms
but yeah, more precisely y is a parameter with a default argument
or def f(x, *, y=1)
if you have def f(x, y=1) you could still pass y by position, f(1, 2) would be valid
well that was issue i saw, i just thought (a: Literal[1, 2], *, b: int | None = None, c: str | None = None) -> ... would be more clear
yeah
but it doesnt matter
all the helpers n mods r here lmao
we like types and type hinting
lol
the s in my nickname is for static typing
There's a lot more than just 2 helpers and 1 mod lol
lol
wat does the error missing return statement mean
def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str]:
The function itself might be missing the return?
What tool are you using? mypy? @undone carbon
That might help?
i solved the prob
i think it's cos
nah
i dun know how to explain
just show ya the code
from typing import Literal, overload, Optional, Union
@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...
@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...
def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str]:
if a == 1:
if b: return b
elif a == 2:
if c: return c
reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
tiz is the initial code
which caused the error
from typing import Literal, overload, Optional, Union
@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...
@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...
@overload
def foo(a: Literal[1, 2], b: Optional[int] = ..., c: Optional[str] = ...) -> Union[int, str]: ...
def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None):
if a == 1:
if b: return b
elif a == 2:
if c: return c
# else:
# return None
reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
Yeah
n tiz solved it
It's the same issue as what I linked
oh
okay
ah
yes
i understood now
from typing import Literal, overload, Optional, Union
@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...
@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...
def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str, None]:
if a == 1:
if b: return b
elif a == 2:
if c: return c
return None
reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
u mean tiz?
@solid light
Well technicallypy else: return None
Or actually yeah
You need it how you put it
So that works aspy return b if a == 1 and b else c if a == 2 and c else None
Because then the nested-ifs don't have a return
didnt knew u could do so
oh
got it
anyways thx a lot
๐
so u normally use overload to list down all the possible combinations of type hints? @solid light
not really sure how to phrase ma question lol
lol
so u normally use overload to list down all the possible combinations of type hints?
generally you shouldnt have tonnes of overloads to a function with different params and returns generally its a sign of bad design
and you should probably make separate functions
lol
https://mystb.in/MeetupThongPaid.python
is it possible for me to keep the parameter info (ie so the type checker knows that self doesn't have to be passed if the method is called from an instance)? or would I have to remove the _P.args/kwargs in async def __call__(self, *args: _P.args, **kwds: _P.kwargs) -> _T: ...?
But the type checker is correct
the code would run without error at runtime, i wasn't sure how to type it correctly though
(stubs for https://github.com/aio-libs/async-lru)
!e ```python
def deco(func):
func('One arg')
class Test:
@deco
def some_method(self, arg):
print(arg)
t = Test()
@blazing nest :x: Your eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | File "<string>", line 6, in Test
004 | File "<string>", line 2, in deco
005 | TypeError: Test.some_method() missing 1 required positional argument: 'arg'
That decorator is getting the function before it becomes bound
Exactly like the static type checker is complaining about
ah, so I would have to drop the paramspec for the __call__ to get it to not complain?
I am saying that the code doesn't work
In [1]: from async_lru import alru_cache
In [2]: class A:
...: @alru_cache
...: async def foo(self, a: int):
...: return a + 2
...:
In [3]: a = A()
In [4]: await a.foo(3)
Out[4]: 5```
I don't follow when it becomes bound though ๐ค
yeah
it would be easier to return the callable itself but then I lose the information about the methods/attributes added to it
you probably need descriptor behaviour here
this is definitely doable but i think you need like 2 classes
alright thanks
tried py def __get__(self) -> Callable[_P, _Coro[_T]]: ... instead of the __call__ but it doesn't recognize the decorated function as a method of the instance then
oh right that's not how get works
Oh I thought it didn't become a bound method
tried this, still doesn't work though https://mystb.in/TechrepublicPosingAccurately.python
Ah, ill try seeing if I can repro it again
https://mystb.in/FloatingBrasClasses.python is what i have which i think is slightly better
Ah okay, i was using Self earlier but then removed it
or something along those lines
I'll try that out thanks
well i just changed _wrapped_alru_cache to subclass _callable_alru_cache cause you dont have to use it in a class right?
pyright gave me grief about it being private
also pyright still doesn't recognize the method with this, so it's probably a bug yeah
yes
oh wait i got it working
you were passing args and kwargs to get
https://mystb.in/HarleyDomainsLingerie.python that type checks fine for me
oh that actually doesnt work still
it says it doesnt take any positional args
ah i missed that
it works when the function isn't in a class so that's weird
this signature doesn't seem correct (for args/kwargs)
the return type should be int and not _T@... as well
maybe just ask eric
sure ill make a discussion
how to type hint not None?
There is no general "not None" type.
!d typing.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.
oh, that can be None, still
I would hope that would be included with Intersection if that ever happens
Sad...
No, that's different from intersection
Intersection is being discussed on the python/typing tracker
is this the correct channel for this?
What keybinding do you guys use for arrow keys?
No, this channel is for type hints aka type annotations aka function annotations. If your question isn't about Python, you can ask it in an off-topic channel
okk sure
is there any way to remove a mypy error?
it feels annoying
oh
i found it
just use # type: ignore
Thanks
out of curiosity, what is the error
write a plugin ๐
How can I add type hints to attributes that are added dynamically when overriding __getattr__
Imagine the following classes. I would like to have ide auto-complete/type hints for the topping_cost method.
class Topping:
def __init__(self, name: str) -> None:
self.name = name
def topping_cost(self):
return 5
class Pizza:
def __init__(self, topping: Topping) -> None:
self.topping = topping
def __getattr__(self, attr):
if hasattr(self.topping, attr):
return getattr(self.topping, attr)
raise AttributeError(f'Missing attr {attr!r}')
mushroom_topping: Topping = Topping('mushrooms')
pizza: Pizza = Pizza(mushroom_topping)
print(pizza.topping_cost()) <================ I need IDE's to recognize that topping_cost is available to Pizza. How can I type hint this?
there isnt a good way to do this
well with autocompletion, type checkers shouldnt error there
but without something like a mypy plugin you cant do this
thanks @soft matrix
ok, that's disappointing. How can I create a mypy plugin, can you please point me to a good resource?
thanks.
lolol
can i do smth like tiz?
foo = Union[str, int]
def func(arg: foo) -> None: ...
is it right?
@pastel egret
You can yes, it's a "type alias".
okay
vscode wouldnt give me the hints tho
it would just show me arg: foo which kinda sucks cos i need it to show tat arg: Union[str, int]
I think that's intentional, usually people want to see the type alias name
okayy
how do you typehint frozensets
is it like tuples, where frozenset[int, int] would mean a frozenset of exactly two ints
hmm apparently it expects only one type argument
so how does one typehint an unordered pair of integers
Frozensets just have 1 arg, which is whatever's contained in them. Tuples are unique in that you can specify a specific length, for frozensets you'll have to do frozenset[int] for any number.
You could also use a tuple[int, int], sorting the contents first, perhaps.
hmm
why can't you specify a specific length for a frozenset
it's not like you can add to them
Since there's no order, a frozenset[int, str] would be pretty useless, you wouldn't have a way to extract just the string to use it? And the type system didn't have a way to specify specific integers or the like, so it'd be kinda unique?
the order doesn't matter though
i don't understand
and you can still extract the string
a, b = my_frozenset; my_string = a if isinstance(a, str) else b
it is kind of weird to have two different types in a set though
but i'm not a typing guy
i prefer no types to complicated types
I mean with tuples we do often care about the order, but you canโt do that with sets.
It looks like MyPy hasn't quite implemented support yet, though make sure you're updated to the latest version.
@overload
def execute(self, query: str, *args, fetch_all: Literal[True]) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[False]) -> SQLResult:
...
def execute(
self, query: str, *args, fetch_all: bool = True
) -> SQLResults | SQLResult:
with self.connection as cursor:
result: Cursor = cursor.execute(query, args)
if fetch_all:
return result.fetchall()
return result.fetchone()```
is this how this should be written?
i feel like it's wrong since fetch_all is optional that the overloads shouldn't be like that, but if i set it to `fetch_all: Literal[True] = True` it'll error that the overloads' signatures don't match the return type
i don't exactly have an error per-se, just advice
SQLResult = tuple
SQLResults = list[SQLResult]```
for reference of those explicit types
fetchall returns a list of tuples, fetchone returns a tuple
Flip the order of those two around, then omit fetch_all entirely in the true case?
Would it be better to just make these two different methods anyway?
Hi everyone, I am having trouble with my abstract class inheritance. Basically, I want to inherit from an abstract class that takes another abstract class as argument, but how can I make the attributes of its implementation recognized correctly its argument types? Is it confusing? Here is what I mean:
from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass
# Define abstract classes
class Params(ABC):
"""An abstract class for storing parameters."""
pass
class Solver(ABC):
"""An abstract class to solve a problem with a specific parameter set."""
def __init__(self, params: Params) -> None:
"""Initialize with a Params object."""
self.params = params
@abstractmethod
def solve(self) -> None:
"""An abstract method that must be implemented in the subclasses."""
pass
# Implementation of abstract classes
@dataclass
class ParamsX(Params):
"""A subclass of Params which has attributes first_param and second_param."""
first_param: int
second_param: list[float]
class SolverX(Solver):
"""A subclass of Solver which solve the problem X."""
def __init__(self, params: ParamsX) -> None:
"""Initialize with the ParamsX object."""
super().__init__(params)
def solve(self) -> None:
"""Solve the problem X."""
print(self.params) # The params is recognized as Params
print(self.params.first_param) # The first_param is recognized as Any
print(self.params.second_param) # The second_param is recognized as Any
# There're also ParamsY and SolverY, ParamsZ and SolverZ, etc
My question: How can I make the first_param and second_param show respectively as the int and list[float] types? I haven't check with mypy but the Visual Studio Code indicates that.
An attempt: If I do with the following code, the types are correct but I don't think it's a pythonic way, it will be like the abstract class Params doesn't have any purpose at all.
class SolverX(Solver):
"""A subclass of Solver which takes a ParamsX object as argument."""
def __init__(self, params: ParamsX) -> None:
super().__init__(params)
self.params: ParamsX # Forced annotation
What you need is a Generic, not necessarily an ABC.
Change Solver to be like this:
ParamT = TypeVar('ParamT', bound=Params)
class Solver(ABC, Generic[ParamT]):
def __init__(self, param: ParamT) -> None:
self.params = param
class SolverX(Solver[ParamsX]):
....
TypeVar defines a type variable, which you later substitute with a specific type. bound restricts it to only permit subclasses of the specified type. Then having the class "subclass" Generic means that the variable is scoped to the whole class. So then when you use the subscripted version, it's as if the type variable was replaced by the real class.
Note the other way you can use it is specific to a function - you put it in the signature, then it takes on whatever type you use to call the function.
Wow man, I took a whole day to find this answer, I even posted a Stack Overflow question ๐คฃ . Thank you so much for the answer, and the detailed explanation too. Really appreciate that!
I guess I have to read the docs one more time, maybe two ๐
The MyPy docs have a lot more info about how things work, as well as say PEP484:
https://mypy.readthedocs.io/en/stable/generics.html
There's also typeshed, the type definitions for the standard library:
https://github.com/python/typeshed/tree/master/stdlib
Cool! Thanks a lot. Can I give you a credit on Stack Overflow post?
Sure!
frozenset[Union[X, Y]]
Though I understand that this doesn't correctly tell the type checker that there is 1 X and 1 Y
Well, even if you did what could it do with that information?
Even if you say unpack it has to be a union since it can't know which is which.
how would you represent an unordered pair then?
Sounds like Tuple[Union[A, B], Union[A, B]] or Union[Tuple[A, B], Tuple[B, A]], depending on which you mean
hm, both of those also could mean a tuple with two of the same types
You can't enforce typevars representing different types
you need a separate type (and therefore class), I think
Not the latter
yeah
Union[Tuple[A, B], Tuple[B, A]] could perhaps work (depending on use case) if there was a way to express the constraint that A and B are are not equal
but there isn't in Python
Are A and B type variables or just "something to replace"?
Union[Tuple[int, str], Tuple[str, int]] will error if you try to pass Tuple[int, int] or Tuple[str, str]
they're type variables, aren't they
I had meant A and B to be replaced with specific types, but I may have misunderstood the question
I thought we were discussing the arbitrary case of how to describe an unordered pair
I don't understand the goal then, I'll wait to hear more
like I honestly don't know what the original question was
I only jumped in because I was interested in the abstract problem ๐
Hmm, still if you just give the generic types then it should work fine
but you can't enforce A != B
Yeah you can't enforce that someone doesn't do TheGeneric[int, int]
#type-hinting message original problem here
yeah. defo not right to use a frozenset for that
well I mean unless you want to have business logic outside your type signature which is fine too
that's why we need a more powerful type system! ๐
my main goal was (is) to typehint an edge of an undirected graph
ie an unordered pair of vertices
frozenset made sense to me because its the only unordered and fixed size structure i could think of
I don't think Eric saw my response, could anyone help with this? https://github.com/microsoft/pyright/discussions/2508#discussioncomment-1555957
Still not sure if it's a pyright bug or not
is there any type system where you can typehint a set of two integers?
besides Agda
I think it's a really fringe use case
But why do you want to restrict it to two?
because each edge is between two vertices
ah
then you'd need the type checker to understand the semantics of equality with your types @acoustic thicket
because you don't know if {a, b} has size 1 or 2
wdym
{3, 1 + 2}
oh true
So if you write {Vertex(...), Vertex(...)}, the type checker would need to have some knowledge of how == works on vertices
and if you don't construct them in-place, well, then it just can't know if they're equal
hm makes sense
so that brings me back to
how do you represent an unordered pair
I'd just use a tuple
you could also have both edges (a,b) and (b,a)
but... why?
ยฏ_(ใ)_/ยฏ
Does mypy recognize
import sys
if sys.version_info >= (3, 7, 0):
fixed_code ...
else:
deprecated_code ...
because I just tried this and it was still warning. Removing the last item in the tuples fixed it ๐ค
print((3, 7) >= (3, 7, 0))
print((3, 7) >= (3, 7))
oops
!e
print((3, 7) >= (3, 7, 0))
print((3, 7) >= (3, 7))
@trim tangle :white_check_mark: Your eval job has completed with return code 0.
001 | False
002 | True
Yeah, but doesn't sys.version_info contain the micro value too?
Type checking only supports two elements in the version tuple
Micro versions don't exist
sorry, bugfix version
!eval
import sys
print(sys.version_info)
although it's called micro here
@leaden oak :white_check_mark: Your eval job has completed with return code 0.
sys.version_info(major=3, minor=10, micro=0, releaselevel='final', serial=0)
Oh I always forget which one is which
that's... strange
tbh
I'm actually not sure what the motivation for this was, maybe implementation simplicity
it'd be usually fine except for maybe typing which was unstable in the 3.5.x and 3.6.x series
thankfully my usecase isn't impacted :)
Like you can pass --python-version 3.6 to mypy but mypy wouldn't want to have to remember every single micro version
We've actually seen this come up most often in typeshed with asyncio, which was also provisional
Black dropped support for 3.6.0 and 3.6.1 due to typing's changes during its provisional period so yeah
Can you change a functions annotations not in runtime
and have them effect the type checker?
async def foo(arg_one: int) -> str:
return arg_one.capitalize() # I dont want pyright to get mad at me
foo.__annotations__['arg_one'] = str
>> await foo('wee')
'Wee'
(more specifically with pyright).. pls ping on reply im going to bed
@river hollow That won't work. Why not just do this? ```py
async def foo(arg_one: str) -> str:
return arg_one.capitalize()
Because I gave a VERY simplified example of what I need lol
In actual use, that wonโt work
When I wake up tmrw I can provide more context if you need
Use overload
Update pycharm but this isn't the right place for questions like this maybe try #editors-ides?
it has fixed size, but that's not encoded @ the type level
and the size can't be determined statically
because no duplicates
yeah, i realised
Opinions on mypy vs pyright ? Personally I am using mypy in every day programming but 5inking to switch to pyright
pyright IME has better control flow analysis, and it's very fast. However, it doesn't have a plugin system, so if you want to use e.g. sqlalchemy, you're probably out of luck
what am i doing wrong?
what version are you using? that only works in 3.10
not sure then, seems like a mypy issue
i'd check if mypy supports that expression
dict[Union[str, int], Union[int, str, bytes]]
```this fixes the error
An example function for context:
def make_inst(cls):
return cls()
How would one type hint this to say that it takes any class and returns an instance of that class?
(I believe I'd use typing.Type for the class vs. class instance issue, but i don't know how to represent 'any class')
It won't work for this class: ```py
class Foo:
def init(self, bar: str):
self.bar = bar
If you want to support any class that has a no-param init, you could accept any callable that takes no arguments: ```py
T = TypeVar("T")
def make_inst(cls: Callable[[], T]) -> T:
return cls()
oh i see, i actually need to represent any sort of initialisation then
actually, the real function im trying to hint isn't much more complex
it just uses inspect.getmembers to get all members of a certain type from a module object
C = TypeVar('C', object) # this is syntactically incorrect
def get_objects(module: ModuleType, cls: Type[C]) -> List[C]:
members = inspect.getmembers(module, lambda obj: isinstance(obj, cls))
return [i for _, i in members]
would i just need to use TypeVar('C')?
well thinking about it that would work... but im not sure it's tight enough? like would Type[C] ensure only classes can be used in the cls parameter?
@river hollow No, no type checker supports this kind of thing. But maybe your use case has some other workaround (e.g. decorator + PEP 612 based?)
I thiiiink this is a mypy bug, but one that was fixed months ago on master. maybe try pip install -U git+git://github.com/python/mypy.git
I looked at that and I have no idea what their use case could be ๐
it works, thanks
No, since it's at runtime, those would just get evaluated.
If you're using forward references though, and called the function before that reference was actually defined that'd break...
okay thx
How should I type-hint a Union of the exact object of discord.embeds.Embed.Empty or a str. Embed.Empty is already initialized from discord.embeds._EmptyEmbed. It is expected to be used directly so I wouldn't really like to just type-hint with discord.embeds._EmptyEmbed
I considered something like Literal[Embed.Empty] but that's probably missusing Literal since it was only for str, int or enum values
@twilit badge You can't really do that. You'll have to typehint it as _EmptyEmbed. Or just accept None and access Embed.Empty in case of None
oh, why is that impossible though?
I'd expect there to be a way to type-hint singletons like this
Literal works for int, str, bytes, tuple and Enum literals.
there isn't any way to type-hint your own singletons
you can make a PEP ๐
hmm, yeah it would certainly be a nice feature imo
type() doesn't work?
hm?
you mean x: type(Embed.Empty)? that's just the same as x: _EmptyEmbed
I think type checkers don't allow calls in annotations
ah
Yeah so do type[Embed.Empty]
That won't work, because Embed.Empty is not a type
That's the whole point isn't it
Annotate it with _EmptyEmbed or use your own proxy singleton
Or wait for PEP 661
!pep 661
I can't tell if it's a joke ๐
Question about typing.overload
Its a decorator that exists simply as an indicator, right? What's the point of having it if there is no concrete implementation?
Oh, is it simply to map return types to input types, with the concrete implementation doing some sort of if-else check and returning as needed?
Yes, it's there to provide overloads for signatures that wouldn't otherwise be possible without it. The function does all of the logic itself through
e.g. when you accept an int argument the function does something and returns ClassA and when it accepts a str it returns ClassB; without the overload you'd just have unions which doesn't quite capture how the function behaves
For sure!
Another use is with functions that only allow specific combinations of optional arguments or other weird things - range() for instance.
By default, yes. You can use the --allow-redefinition flag to allow this though
interesting, thanks i didnt know that flag
https://github.com/microsoft/pyright/discussions/2508#discussioncomment-1572771
still a bit confused on how to approach this based on what eric said though, my current code is https://mystb.in/SimilarBarbaraPreviews.python
I need the signature of A().in_class to be _callable_alru_cache[(a: int), int] (without self and the Any) but I'm not sure if that's possible
class A:
@alru_cache
async def in_class(self, a: int) -> int:
return a + 1
async def main():
reveal_type(A.in_class)
reveal_type(A().in_class)
await A().in_class(1) # Should not have an error
await A.in_class() # Correct error
await A().in_class._origin(1) # Should only say a is missing```
this is the most confusing typing i've done 
eric also mentioned __p0 (the first argument it expects us to pass when the method is accessed from an instance) though I don't really understand why that's there
replacing Concatenate[_O, _P] with just _P gives this
the first error is the incorrect one
Hey I would be eternally grateful if someone could help me out with this..
I have a class where I'm inheriting from requests.Session
I've overridden the request method, but I'm not changing the method signature at all.. I'm really just setting a new flag that I'm adding to the class essentially.
Problem is mypy wants me to add typing to my method arguments, of which the original request method has many.
What is the best way to handle this?
I feel like there has to be a better way, since when I look at the methods signature, I see this:
_Data = Union[None, Text, bytes, Mapping[str, Any], Mapping[Text, Any], Iterable[Tuple[Text, Optional[Text]]], IO[Any]]
_Hook = Callable[[Response], Any]
_Hooks = MutableMapping[Text, List[_Hook]]
_HooksInput = MutableMapping[Text, Union[Iterable[_Hook], _Hook]]
_ParamsMappingKeyType = Union[Text, bytes, int, float]
_ParamsMappingValueType = Union[Text, bytes, int, float, Iterable[Union[Text, bytes, int, float]], None]
_Params = Union[
SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType],
Tuple[_ParamsMappingKeyType, _ParamsMappingValueType],
Iterable[Tuple[_ParamsMappingKeyType, _ParamsMappingValueType]],
Union[Text, bytes],
]
def request(
self,
method: str,
url: str | bytes | Text,
params: _Params | None = ...,
data: _Data = ...,
headers: MutableMapping[Text, Text] | None = ...,
cookies: None | RequestsCookieJar | MutableMapping[Text, Text] = ...,
files: MutableMapping[Text, IO[Any]] | None = ...,
auth: None | Tuple[Text, Text] | _auth.AuthBase | Callable[[PreparedRequest], PreparedRequest] = ...,
timeout: None | float | Tuple[float, float] | Tuple[float, None] = ...,
allow_redirects: bool | None = ...,
proxies: MutableMapping[Text, Text] | None = ...,
hooks: _HooksInput | None = ...,
stream: bool | None = ...,
verify: None | bool | Text = ...,
cert: Text | Tuple[Text, Text] | None = ...,
json: Any | None = ...,
) -> Response: ...
I'm really just setting a new flag that I'm adding to the class essentially.
wdym?
like, do you just have a single statement in the function and a super().request(*args, **kwargs)
No there's a bit more im doing to proxy the request. But my main question is with typing. How should I handle the typing in q situation where I'm overriding a method?
The signature is the same though. Same method arguments and return value
Sounds like we need deconcatenate
How do I typehint an exception? I tried : Exception but that doesn't work.
Example: https://mypy-play.net/?mypy=latest&python=3.9&gist=e3040015a7e036b633832002c2cb4c3b
class Foo(ValueError):
pass
def perform_test(expected_error: Exception) -> str:
return "WIP"
print(perform_test(Foo))
```Gives the error `Argument 1 to "perform_test" has incompatible type "Type[Foo]"; expected "Exception"`
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
from types import BaseException I think?
Somewhere in types at least
type[Exception] (or BaseException) should work
Let me dig it up hold on
It's a builtin
Ooh, nvm. Just remove the import
type[Exception] does indeed work, thanks ๐
Why does it need to be type[Exception] and not just Exception?
I was confused because when type annotating an __aexit__ you had to import TracebackType from types
Yes, the problem is the missing type[]. Exception means an exception instance, not an exception class.
so perform_test(Foo()) would be legal but perform_test(Foo) is not
BaseException is a builtin, no need to import it
Ah right
Ty ๐
And how would I annotate a function?
Thought maybe FunctionType but apparently not
Callable
From typing?
yes, or collections.abc on recent versions
On 3.9 so should I use collections?
FunctionType would work if you only wanted a function, which usually is not what you want and is more limited
yes
Not really, I don't think mypy lets you pass an actual function to something typed as FunctionType
type hinting, isnt that what we all need?
The type system basically doesn't know that functions are instances of types.FunctionType
Anyway to fix this?
its like in typescript?
is that in pycharm? there's an open bug on them for it
easy workaround is to use typing.Type instead
from collections.abc import Callable
from typing import Optional, Type
from errors import BodyError, InvalidLoginCredentials
from interactions import login
class TestFailure(Exception):
def __init__(self, test_name: str, message: str):
self.test_name = test_name
self.message = message
super().__init__(self, f"Test {test_name!r} failed - {message}")
class UnexpectedErrorMessage(TestFailure):
def __init__(self, test_name: str, expected_message: str, actual_message: str):
self.expected_message = expected_message
self.actual_message = actual_message
super().__init__(test_name, f"Expected message {expected_message!r} but got {actual_message!r}.")
class ExceptionPlaceholder(Exception):
pass
def perform_test(
test_name: str,
function: Callable,
*args,
expected_error: Type[Exception] = ExceptionPlaceholder,
expected_error_message: Optional[str] = None,
**kwargs,
):
print(kwargs)
try:
function(*args, **kwargs)
except expected_error as e:
if (actual_error_message := e.args[0]) == expected_error_message:
print(
f"Test {test_name} passed! Got expected error type and message "
f"({expected_error.__name__}, {expected_error_message!r})"
)
else:
raise UnexpectedErrorMessage(test_name, expected_error_message, actual_error_message)
def test_login():
perform_test(
"Login (No Username)",
login.login,
"", "pass", # username, password
expected_error=BodyError,
expected_error_message="`username` not provided"
)
```Now have the first steps to a super basic testing suite
Still need to add a bit more though, like an UnexpectedErrorType(TestFailure)
Ah, I thought it at least worked in pycharm because I used it in a decorator that accessed function obj only attrs but looks like that was just because it didn't resolve it (or warn) when applied as a deco
That's possible, I haven't used pycharm's type checker
Well, at least the auto completion with it works
Yeah I realized that quite quickly and was like.. "UUuuuuhh..."
anyone knows how i can
type annotate a list with an object in it that has "mode" and "value" as params ?
use a protocol
class ModeValueParams(Protocol):
def __init__(self, mode, value) -> None:
...
# then do
list[ModeValueParams]
Thank you, i will try it even though it looks confusing compared to typescript
from where did you get the Protocol?
from typing import Protocol
from typing import Protocol
oh typing, i tried types/type
also what do the 3 dots mean? or are they supposed to be code?
you might be better changing __init__ with __call__ but i think thats what asked for
the 3 dots are
hmm
!d Ellipsis
Ellipsis```
The same as the ellipsis literal โ`...`โ. Special value used mostly in conjunction with extended slicing syntax for user-defined container data types. `Ellipsis` is the sole instance of the [`types.EllipsisType`](https://docs.python.org/3/library/types.html#types.EllipsisType "types.EllipsisType") type.
thanks
its the basically the same as pass
but its more a sign of nothing is supposed to be here
also how do i use it?
i've actually seen people use it as an indication that something is supposed to be here (but isn't rn)
async def search_messages(self, guild_id, search_obj: list[ModeValueParams]):
i think this is similar to typescript
like this? it s till gives me an error
and it tells you one of the ways to fix it
sorry i have no clue how to fix it :/
Thank you. didnt knew that strings now also can be read different
@soft matrix @rustic gull if you're using Python <3.9, you should use List[ModeValueParams]
(where List is from typing)
string-based annotations will break if you inspect them
they dont they go to forward refs
oh not without future annotations
oh maybe they dont
ยฏ_(ใ)_/ยฏ
@soft matrix if you try to evaluate list[ModeValueParams] (for example, if you put that as an annotation for pydantic), it will obviously break\
And there are lots of edge cases with the new annotation system. For example:
def foo(key: K):
Item = Dict[str, K]
def function(strings: List[str], default: K) -> "Item":
return {s: default for s in strings}
return function
bar = foo("quack")
here there's no way to correctly evaluate Item
PEP 649 to the rescue
!pep 649
oh right
is = ... or = the_actual_default better practice for optional arguments in overloads (not in a stub file)?
I'd say = ... because otherwise you just repeat the default, and the default is irrelevant for the overloads
thanks
is Union[Type[T1], Type[T2], Type[T3]] or Literal[T1, T2, T3] better practice?
Literal[] doesn't accept type objects according to the current rules
i see, probably should have tried it before asking, thanks again
You know if you're in stubs you can use 3.10 syntax for stuff right?
wdym?
yeah, this isn't in stubs
it's in the actual file
why would GenericClass[T1] not match the type GenericClass[T1 | T2 | T3]? (I know if the type was GenericClass[T1] | GenericClass[T2] | GenericClass[T3] it would successfully type check, but this seems like weird behaviour)
ApplicationCommandOptionChoiceType = TypeVar('ApplicationCommandOptionChoiceType', bound=Union[str, int, float])
class ApplicationCommandOptionChoice(Generic[ApplicationCommandOptionChoiceType]):
...
@overload
def application_command_option(
*,
choices: Optional[Union[
List[ApplicationCommandOptionChoice[int]],
List[ApplicationCommandOptionChoice[float]],
List[ApplicationCommandOptionChoice[str]],
]] = ...,
min_value: Optional[Union[int, float]] = ...,
max_value: Optional[Union[int, float]] = ...,
) -> Any: ...```would there be a way to type this so these rules are followed?
if the type of `choices` is `List[ApplicationCommandOptionChoice[int]]`, `max/min_value` should take an `int`
if the type of `choices` is `List[ApplicationCommandOptionChoice[float]]`, `max/min_value` should take a `float`
if the type of `choices` is `List[ApplicationCommandOptionChoice[int]]`, `max/min_value` should NOT be passed
your 2nd and 4th lines are the same
anyway, I think what you want is generics
Say I'm using a 3rd party package and I'm making a class that inherits from one of their classes. For the methods that belong to the inherited class, do I rewrite all the type declarations? Or is there a better way to do it?
Yes, you have to repeat the type annotations. I think there's been talk of automatically inferring the parent's types, but I'm not sure any type checker implements that.
i decided to use more overloads for this instead, which partially works, except why isn't an error being called for the call at the bottom of the code? https://mystb.in/IanWrappingRailway.python
max_value is an int, so choices should be List[ApplicationCommandOptionChoice[int]], though the float overload seems to be used and therefore List[ApplicationCommandOptionChoice[float]] is being accepted
oh it falls back to the float overload because the int overload doesn't work
would there be any way of preventing that? or no since float accepts int
How do instance vs class attributes work in stub files?
Same as in normal python, with class attributes in the class scope, and instance attributes defined in the constructor?
Both go at the class scope, to indicate a class attr you use typing.ClassVar[type].
Yo, I actually want to create a mother type of two types like this : temp_var: IntOrStr = ""
Do you have an idea ?
in 3.10 you can use |, in older versions there is typing.Union @spiral garnet
Yes i know but i want to create a type that is basically an union
to avoid writing union everytime
whats the correct way to annotate a nested datastructure with DATA_OPTIONS = Dict[str, Union[str, "DATA_OPTIONS"]] mypy complains about a cyclic specification
I think that's only a mypy limitation, the hint itself is correct
you can alias it to an another name like the DATA_OPTIONS above
You can make your own type like my_type = Union[int, str] and then use my_type
from __future__ import annotations
class DATA_OPTIONS(dict):
def __getitem__(self, key: str) -> str | DATA_OPTIONS: ...
def __setitem__(self, key: str, value: str | DATA_OPTIONS) -> None: ...
can this one be used in type annotations when normal dicts pass in?
ok ty i was actually searching complicated :x
yes
you can also add __class__: type[dict] in body of this class
@tranquil turtle i see, so the declaration for older python would roughly be the same just use Union?
im observing mypy rejecting dicts for DATA_OPTIONS
yea
and Type[] instead of type[]
if you dont care about annotations at runtime, you can use new syntax
https://github.com/python/typing/issues/182 this may help
Idk just suck it up not working on mypy and use DATA_OPTIONS: TypeAlias = "dict[str, str | DATA_OPTIONS]"
You aren't closing the DAG(... parenthesis
Isn't that the last line?
Oh lol I read that as a variable because my phone displayed it on a new line
It could be you the trailing , when there's no more arguments @rustic gull
sorry trailing , where i dont see it
After the =None
Besides, the error is in the package and not your code ๐คทโโ๏ธ
i can post it lemme remember how to dump text
!paste is this what you're after?
Pasting large amounts of code
If your code is too long to fit in a codeblock in discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the floppy disk icon in the top right, or by typing ctrl + S. After doing that, the URL should change. Copy the URL and post it here so others can see it.
The error is L169 which ispy spec.setdefault('consumes', ['application/json']) # type: List[str]
So I'd assume it doesn't like the # type: List[str]
Right, type annotations aren't valid on function calls
Am I right that type checkers don't really understand cython code?
I wanted to add stub files to lupa
They can't parse it
How would I type-hint an object that defines __str__?
def foo(x: StrConvertable):
return str(x)
isn't that all objects?
oh, I didn't realize python just supports this for everything, so I suppose I can just do x: Any
all objects can be converted to string
you wouldn't need the function, just use str
although in general you would use protocols
self.m_processes = queue.Queue()
self.current_process = None
why does this need a typehint
hovering over self.m_processes correctly shows a type of Queue
but mypy errors
It wants a generic argument, like Queue[int]
ah, ok
ok one more question
what does this mean
No overload variant matches argument type "Frame"
woops, left stuff out
ClassesUI\share_price_download.py:22: error: No overload variant matches argument type "Frame"
ClassesUI\share_price_download.py:22: note: Possible overload variants:
ClassesUI\share_price_download.py:22: note: def (master: None = ...) -> Tcl_Obj
ClassesUI\share_price_download.py:22: note: def (master: Union[Wm, Tcl_Obj]) -> None
@granite plover Can you show the line?
i sorted it out
the line was
self.transient(parent)
when it should have been
self.transient()
woohoo!
Success: no issues found in 63 source files
Hey guys. Been hitting my head against a problem for awhile now and haven't had any luck, so I'm trying to get some help. I have several classes that are derived from one. These classes inherit a specific method from their parent that intends to return an instance of the same type as the class. I'm using CPython so I'm not fighting against a type-checker, but I want to hint it in such a way that a language server will provide proper on-hover, autocompletion info, etc. on the return without needing to redefine the method on every subclass. For example:
class Base(metaclass=ABCMeta):
# The return of this will actually never be `Base` because the class is
# abstract. It will always be a subclass.
def get_random_instance(self) -> 'Base':
"""Return a random, existing instance of `type(self)`."""
...
class Derived(Base):
...
derived = Derived()
# Since the return hint of the method is `Base`, the language server of course thinks that
# it is as such. I would like it to be of type `Derived` instead.
random_derived = derived.get_random_instance()
Hopefully this makes sense.
You need a bound TypeVar. BaseT = TypeVar("BaseT", boound=Base)
then def get_random_instance(self: BaseT) -> BaseT:
Oh. That was much easier than I would have thought. I tried something close to that before, but I forgot to hint the friggin' method parameter...
Thank you very much!
also there's a proposal for Self, and pyright already supports it 
oops I misspelled bound above. Also, there's an upcoming proposal to make this even easiier
^ yes that
So like, -> Type[Self]?
No, you'd just write -> Self
its very nice ;)
although to use it you need to import it from typing_extensions
i need to talk to pradeep about making a pr to python/peps
and getting a sponsor for it
I can sponsor it if necessary
Though if you can get Guido to do it even better ๐
thank you for the offer, ill email guido and ask him if hed be willing to
Can someone help me out with an issue I'm having.
yeah sure just ask your question
You'll have to share the issue before anyone can help ^^
from typing import TypeVar, Any
MISSING: Any = object()
IntT = TypeVar('IntT', bound=int)
def foo(n: IntT = MISSING) -> IntT:
if n is MISSING:
n = 3 # Expression of type "Literal[3]" cannot be assigned to declared type "IntT@foo"
return n```why doesn't this type check? `3` is an `int` which doesn't conflict with the bound type of `IntT`
@upbeat wadi If you call foo(b) where b has type bool, foo is typed to return a bool. But it will not.
ah okay, thanks
Just as you can't do this:
T = TypeVar("T")
def f(x: T) -> T:
if x is None:
return 42069
return x
in other words, 3 is not assignable to IntT because there's some extra constraint on IntT you don't know.
i see you're a fellow pyright user ๐
yeah i use pyright
I was planning to dig into pyright and figure out how it works on the weekend
any pointers?
as in, read the code
hey all, is it possible to do the following with pydantic?
class User(BaseModel):
id: int
name: str
if name == 'John Doe':
signup_ts: Optional[datetime] = None
friends: List[int] = []
elif name == 'Jane Doe':
batch_size: int
positive_weights: bool
I have two different classes, each with some shared parameters (like "id") as well as some unique parameters "batch_size, positive_weights". It would be nice to just have a single pydantic object to check the types for either class
to read (and understand) the code, it would help to know ts as that's what it's written in
ah nice
why am i getting this error? https://mystb.in/RacingObtainPersonals.dart
(the error is when updating OPTION_TYPE_MAPPING, not using dict.fromkeys)
https://mypy-play.net/?mypy=latest&python=3.10&gist=519d4f20b7a7f4d747d38d001d83f5c4 works when using mypy
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
@upbeat wadi
fromkeys accepts Mapping[ValidTypes, int]. Because the key type variable of Mapping is invariant, ChannelTypes cannot be used in place of ValidTypes.
However, fromkeys also accepts Iterable[tuple[ValidTypes, int]]. So if you do OPTION_TYPE_MAPPING.update(<expr>.items()), it works.
subtyping is tricky
stdlib/typing.pyi lines 434 to 436
class Mapping(Collection[_KT], Generic[_KT, _VT_co]):
# TODO: We wish the key type could also be covariant, but that doesn't work,
# see discussion in https://github.com/python/typing/pull/273.```
oh this only works on mypy cause it makes things "frozen"
does it special-case stuff for dicts?
e๐ ฑ๏ธic jank I guess
Ill see if that has the same result, thanks
And yeah I was using pyright
maybe ask eric what he actually things of that mypy

I would but I don't really get how it works since I don't use mypy much
Is it documented?
you can just make an issue, Eric proabably knows something about it
Alright
or he will ping gvanrossum ๐
i'm wondering whether this is a bug or not now py a: dict[ChannelTypes, int] = dict.fromkeys(CHANNEL_TYPE_MAPPING.keys(), 1) since dict.update expects the dict to have the same type as the dict being updated
and the dict being updated is dict[ValidOptions, int]; since mypy doesn't report an error I'll report it anyways and see what Eric says though
from typing import Union
options: dict[Union[int, str], int] = {}
channel_types: dict[str, int] = {}
options.update(channel_types) # error on both mypy and pyright
options.update(dict.fromkeys(channel_types.keys(), 1)) # error on pyright only
```this confuses me a bit, why does mypy give an error on the first update but not the second?
It probably uses type context to infer a different type for the second dict
reveal_type(channel_types) # note: Revealed type is "builtins.dict[builtins.str, builtins.int]"
reveal_type(dict.fromkeys(channel_types.keys(), 1)) # note: Revealed type is "builtins.dict[builtins.str*, builtins.int*]"
```mypy has the key and value type of `fromkeys` as conditional while pyright doesn't, would that be why?
The asterisk doesn't mean conditional on mypy
Yeah it means "inferred"
ah i see
Do magic methods work with the typing.overload decorator?
@overload
def __get__(self, instance: Element, prototype: Type[Element]) -> Primitive:
pass
@overload
def __get__(self, instance: None, prototype: Type[Element]) -> Attribute:
pass
def __get__(self,
instance: None | Element, prototype: Type[Element]) -> Attribute | Primitive:
pass
yes
i would advise using ... instead of pass though
(for the overloads only, assuming that there is an implementation for it)
you dont need to provide the non overloaded function there then
and ... is used when overloading/for methods in stub files usually
if you're using pyright/pylance, you'll also get an error for pass (assuming this was not in a stub file, it doesn't seem to give that error in stub files)
you dont need to provide the non overloaded function there then
So you're saying a stub file will automatically overload?
@upbeat wadi
Do I still include the decorator?
def foo(bar: type) -> bar
Is there a way to do something like this? Where the parameter is a type, and the return type is whatever the parameter was?
is there a way to type hint likepy if self.x is None, self.y is an int if self.x is a str, self.y is a bool
One way you can sorta do it is define a property, which checks the other attribute (raising an exception if not the right type), then casts and returns.
Not particularly efficient, but safe. Perhaps making the class generic and using typeguard would also work?
i dont think you can use TypeGuard based on attributes of the class
Well you could define a is_y_int() method, which typeguard-casts self to MyClass[int].
also, i meant something like: py instance = ... reveal_type(instance.x) # str | None reveal_type(instance.y) # int | bool if instance.x is None: reveal_type(instance.y) # int else: reveal_type(instance.y) # bool
iirc i tried using a generic, but i'll try again
ideally for my use case i wouldn't use generics (at least without being able to default the type arguments to generic)
i just remembered, TypeGuard doesn't take self/cls arguments into account
T = TypeVar('T', int, bool)
class SomeClass(Generic[T]):
x: str | None
y: int | bool
def cls_is_none(obj: SomeClass) -> TypeGuard[SomeClass[int]]:
return obj.x is None
if cls_is_none(instance):
reveal_type(instance.y)
ah i should have mentioned this is within the class
Just need to make it a function, or pass the instance again as a parameter.
unfortunate, thanks
Maybe you could alternatively use an overloaded property, where self has a generic type to constrain which is used.
good idea, though pyright doesn't seem to like property overloads
mypy gives an error of Decorated property not supported
oh i just realized this wouldn't work in my case either 
https://github.com/Rapptz/discord.py/blob/master/discord/reaction.py#L86-L89
e.g. to type guard this where if it returns True, <reaction instance>.emoji is an instance of Emoji | PartialEmoji, otherwise it's an instance of str
discord/reaction.py lines 86 to 89
# TODO: typeguard
def is_custom_emoji(self) -> bool:
""":class:`โbool`โ: If this is a custom emoji."""
return not isinstance(self.emoji, str)```
This sort of thing sometimes indicates perhaps you need to change your type hierarchy.
in what way?
Well, change self.emoji to also include the other attribute. But actually since it's just a type check on that attribute already, you could just do it in the callers.
i see
What's the attribute this is guarding?
discord/reaction.py lines 69 to 70
emoji: Union[:class:`โEmoji`โ, :class:`โPartialEmoji`โ, :class:`โstr`โ]
The reaction emoji. May be a custom emoji, or a unicode emoji.```
Okay, just check isinstance directly. Since your code will be using str, that's not really private anymore.
that could also be changed to just Emoji | PartialEmoji probably
You could also do this:
@property
def emoji_str(self) -> str:
if isinstance(self.emoji, str):
return self.emoji
else:
raise ValueError(f"Emoji {self.emoji!r} is not a string!")
Then inside the if-guarded code, use this which will error if the wrong value was provided.
Or make it not error, instead return the string form of those other emoji classes.
๐ thanks
are type mappings a thing?
def atomicview(buffer, atype: Type[AtomicView], **kwargs) -> AtomicViewContext:
if atype is AtomicView:
return AtomicViewContext(buffer=buffer, **kwargs)
elif atype is AtomicBytesView:
return AtomicBytesViewContext(buffer=buffer)
elif atype is AtomicIntegralView:
return AtomicIntegralViewContext(buffer=buffer, **kwargs)
elif atype is AtomicIntView:
return AtomicIntViewContext(buffer=buffer)
elif atype is AtomicUintView:
return AtomicUintViewContext(buffer=buffer)
else:
raise TypeError("'atype' must be or derive from 'AtomicView'.")
wanna somehow map Atomic*View -> Atomic*ViewContext
but i'm not sure if i'm reaching here
(As in the parameter type with the return type, not the function contents)
Ah, @overload to the rescue ๐
Your type checker should already be able to do this?!
Nope, i want the type of an attribute to be based on the type of another one
i tried this when i did audit logs afaik it isnt possible
ask eric what he thinks of adding overloading properties
https://github.com/doodspav/atomics/blob/devel-ffi/src/atomics/_impl/atomic/view.py
I have this code using @overload a bunch
(it's 5 type overloads for a function, and then the function is like 10 lines)
but when i use it, PyCharm thinks the return type is of all the overloads that match the input type
is there a way to make it think the return type is a single type?
like have Type[T] accept only T and not types that derive from T?
I see, thanks
maybe make the classes @final?
they're not tho :/
gonna try TypeVar stuff
I would try Union[Type[C1], Type[C2], Type[C3]] instead of Type[Union]
it turns out PyCharms type checking just isn't up to scratch
https://github.com/doodspav/atomics/blob/devel-ffi/src/atomics/_impl/atomic/view.py this works fine now ๐
(also @trim tangle )
like if i use pyright or pylance it's fine, just PyCharm's type checker is confused (a little, but not in a detrimental way)
I've heard that it isn't the best
PyCharm's checker is a fair bit more lenient, I think the primary purpose is for autocomplete and the like.
I have found it to be somewhat limited even in that regard
Hello
if I have a class that has embedded within it some kind of custom descriptor
Do I type the attribute as the descriptor's type, as its return turn, or both (somehow)
class SomeDescriptor(object):
def __get__(self, instance: None | SomeClass, type: Type[SomeClass]) -> Some_Return_Type:
...
class SomeClass(object):
some_value : SomeDescriptor
or
some_value : Some_Return_Type
or
some_value : Classvar(Some_Return_Type)
some_value : Some_Return_Type
This is in the context of a typing stub
The static type checker should be able to figure out descriptors as far as I know
class SomeClass(object):
some_value: SomeDescriptor
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
This
Is my new favorite toy
Is it possible to create a type-variable within a generic class such that it is bound by a type parameter of that class?
Something like this: ```py
A = TypeVar('A')
class Foo(Generic[A]):
B = TypeVar('B', bound=A)
...
That's not possible. Why do you need that?
Alright, so it's related to the last thing I asked in this channel ๐
#type-hinting message
I'm trying to make an event bus, which would look something like: ```py
class Event:
pass
E = TypeVar('E', bound=Event)
class Bus:
def __init__(self):
...
def subscribe(
self,
event_type: type[E],
callback: Callable[[E], None],
) -> None:
...
def notify(self, event: Event) -> None:
...
Except, rather than providing my own base event, Event, I'd like to make it generic:
class Bus(Generic[E]):
...
Where E is the type parameter for the base event.
I'm not sure this really makes very much sense tbh.
Yeah sure, so why define a TypeVar inside the class now?
So that the event_type must be a subclass of E.
Is this not the behaviour right now?
In this example, event_type is a subclass of Event. But I want it to be a subclass of the type parameter E.
But E is bound to Event
Do you not want event_type ans the event for the callable to be the same?
So like, you'd create your own event hierarchy of classes where the base event is called e.g. BaseEvent. Then you'd create your bus: ```py
bus: Bus[BaseEvent] = Bus()
The class for Bus is something like (I know this is wrong): ```py
T = TypeVar('T')
class Bus(Generic[T]):
E = TypeVar('E', bound=T)
def __init__(self):
...
def subscribe(
self,
event_type: type[E],
callback: Callable[[E], None],
) -> None:
...
def notify(self, event: Event) -> None:
...
Is it always gonna be based on BaseEvent?
Well the idea is that the user would define their own base event.
So that the code for the bus can be re-used for different busses.
Not even a subclass to yours?
Nah
print("lol")
This example should work how you want it to if you just use the T from the generic
I still don't see that problem E solves
T = TypeVar('T')
class Bus(Generic[T]):
def __init__(self):
...
def subscribe(
self,
event_type: type[T],
callback: Callable[[T] None],
) -> None:
...
def notify(self, event: T) -> None:
...
The only potential problem is the callback, if I remember correctly callbacks are like invariant, is that the issue you've run into?
Yeah, the issue is that I think the arguments of a function are contravariant? Am I using that term correctly? ๐
So I know that this code will work: #type-hinting message
I genuinely can't tell, I'm sure that's correct
Ah okay, well yes then you do want another TypeVar.
T = TypeVar('T')
ET = TypeVar('ET', bound=T)
class Bus(Generic[T]):
def __init__(self):
...
def subscribe(
self,
event_type: type[ET],
callback: Callable[[ET] None],
) -> None:
...
def notify(self, event: T) -> None:
...
So this is kind of what you want then? Just have to move it out of the class I believe
Hmm, nah I don't think you can bound one typevar by another. I was just hoping that inside the body of the class the type parameter would be considered "realised", so to speak.
Right exactly, so if you make it covariant?
Because for methods within a class, rather than being a type variable, the type parameter can be thought of as representing a constant type.
T_co = TypeVar('T', covariant=True)
class Bus(Generic[T_co]):
def __init__(self):
...
def subscribe(
self,
event_type: type[T_co],
callback: Callable[[T_co], None],
) -> None:
...
def notify(self, event: T_co) -> None:
...
Hmm, I'm not sure how to interpret that. Isn't making the type parameter covariant more about the relationship between different specialisations of Bus?
Like the relationship between Bus[C1] and Bus[C2], where C1 is a subtype of C2.
I might just go with the original code and remove the bound on the type-variable. So essentially any class could represent an event type on any bus.
As I'm essentially treating the event types as atomic anyway.
And maybe using classes for this is a bad idea to begin with ๐ I mean, rather than denoting each type of event with e.g. a string.
As a result that also becomes the case
Hey guys, I'm writing a typed decorator for one of my classes. My decorator sets additional attributes on the class - a static method and a bool. How do I properly type the decorator so that mypy understands that the class will have those attributes?
You'll have to write a plugin
i.e. you can't really typehint arbitrary mutations
thanks - i've just seen mypy occasionally complain about instance of that class not having attribute 'custom_bool' for example
should i # type: ignore those lines?
is that normal practice?
no
you should try and make it type check, which from the sounds of things it probably should be typeable
so, for example what should be type-able?
def mydeco(cls: Any):
def open():
print('Opening')
cls.open = staticmethod(open)
cls.is_fruit = True
return cls
@mydeco
@attr.s
class Apple:
color: str = attr.ib(default='Yellow')
Can you show your actual use case?
Maybe it's doable with just a mixin?
Not really, this would probably require intersection types which Python doesn't support
well ackchually
This is pretty-much my actual use case, the only real difference would be that open in my case would be a constructor of Apple and would return a new Apple based on another, let's say FruitPlate
Why can't you make it a mixin, for example?
I probably could, the only thing I don't know how that would affect the initialization of attrs
it shouldnt
would mixins have any other benefits over a @dec?
ah actually
i can tell you why i can't really use a mixin
because def open(): actually uses cls inside of it...
so?
oh that would then be self
mixins can have classmethods
that seems to work! thank you @trim tangle
one more q, if mydeco has optional args, how does one define those with a mixin?
def mydeco(cls: Any, default: str = 'banana')
You could use a classvar, I guess?..
depends on what the optional args are for and how many
they'd only be referenced by the open() function
so if i want to redefine them, i'd do something like
class Apple(FruitMixin(default='apple')):
maybe customize __init_subclass__?
although I'm not sure how that would work with mypy
inheritance tricks generally are hard to introspect and type
I was playing around with intersection types in pyright... but it didn't work
why is pyright's code so cursed ;-;
are you trying to implement intersection types?
Pyright already sorta has them, but you can't explicitly define it
yeah im just warning you eric said implementing them is a lot of work
"hmm, what would be a good, small file to hack at"
2 minutes letter
"hey, namedTuples.ts is just 400 lines of code"
"oh wait"
"it's a single 400 line function...
"
gotta optimise for that speed
well... if Eric gets hit by a bus the project has a non-zero chance of dying, I guess
although maybe I'm exaggerating
it's a really impressive project. it seems like he fixes every bug within a day and there's pretty much nobody else working on it
yeah, I agree
but I've been trying to figure out how it works multiple times, and failed
maybe I'm just stupid
for some reason, all the Eric's I know are incredibly smart and I don't understand why
maybe it's a classvar... lol
is using object or typing.Any more recommended? as they both refer to the same thing
typing.Any
and technically they aren't the same, Any is also a subtype of every type
object would only expect operations that work on all objects, which aren't many
They mean different things. It's actually better to use object when you can, because it is type safe. Any basically turns off the type system.
ah thanks
My typehints are a bit rusty, and I'm not used to pyi files
If I want to use a typevar, for example, in a sequence type which works with a T_Contained type
T_Contained = TypeVar('T_Contained')
class MyContainer(Sequence[T_Contained]):
def __iter__(self) -> Iterator[T_Contained]:
...
Or is there a shorthand, like
class MyContainer(Sequence[TypeVar('T_Contained')]):
def __iter__(self) -> Iterator[T_Contained]:
...
No, you have to define the TypeVar separately like in your first example
Excellent, thanks! ๐
Is there a difference, other than intent, between typing something as None | SOMETHING versus Optional[SOMETHING]?
Those are exactly equivalent, and also to Union[Something, None]. That'll produce Optional[Something] actually.
Oh, neat
In this case I think Optional[] notation is best, since it declares intent a little more, but in general
Praise the union operator ๐
| None is best
Because it's not really an Optional type, that would be an actual class with methods to extract the wrapped value
how should I type-hint a class method that returns an instance of itself?```py
from typing import TypeVar, Type
T = TypeVar("T", bound="Foo")
class Foo:
@classmethod
def bar(cls: Type[T], my_var: str) -> T:
...
I figured I could do this, but isn't there some better way?
for some reason this just looks weird to me
There are plans to add Self, but right now this is the solution
from __future__ import annotations
Why does importing annotations fix using generic collections in type hints backwards compatability? e.g. list[int] on Python 3.7? Because the annotations delays the evaluation of the type hint till runtime? Why doesn't it fail at runtime?
use the annotations import, then you can use Foo as a normal type hint within Foo ๐
Because all annotations become strings, so trying to do [] on a class isn't executed.
I can do that anyway just as a string, but that's not what I want since the class is being subclassed at which point it wouldn't be Too that's expected, but rather the new subclassed class
I don't think there's a type hint for a subclass of something, but Foo type includes all subclasses of Foo, afaik
so it would fail at runtime if I tried to access the annotations I'm guessing? I wonder if this bad practice then? Although for just IDE type stuff it works fine as I guess the tools know how to read it
No it wouldn't, because it's a string. So it won't ever fail until a library is forced to literally tell the Python interpreter to run that string as code.
That's when it fails
ah that's kind of what I meant, but I think I need to go reread the annotations pep
On second viewing, I see what you are trying to do, and I think you've done it the best way as well. Will be interesting to see how many IDEs etc can understand that though ๐
Do you have any idea if this is good or bad practice btw?
Most could, these IDEs usually just use static type checkers such as pylint or mypy, typevars are relatively standard feature of python type-hinting and pretty much all IDEs will understand them, I was just wondering if there was something cleaner than a global typevar bound to my specific class, which currently there apparently isn't
good to know
It's pretty bad practice to use eval() or exec() yeah
just reread the PEP, didn't realise that 'eval' was the only way to resolve them
assumed there would have been a safe Foo.__get_annotations__() or something
Right, but bar returns the subclass it's called on, not just Foo
is beartype overload out?
Hello, any idea with mypy how to remove this error?
test_mypy_optional.py:12: error: Argument 1 to "open" has incompatible type "Optional[str]"; expected "Union[Union[str, bytes, PathLike[str], PathLike[bytes]], int]"
POC
import os
A = os.getenv("A")
assert A is not None
# Fine for mypy
with open(A, newline='') as xfile:
pass
# Not fine for mypy
def x() -> None:
with open(A, newline='') as xfile:
pass
As you can see it still considers A as possibly None, despite asserting it
!e this is how I use type hints. a bit more of a type nudging. ```py
from ctypes import py_object as p
class annotations(metaclass=lambda*a:type(*a)()):
q={}
def setitem(self, this, that):
if this in globals():
dict.update(globals(),{this:that(globals()[this])})
else:self.q[this]=that
def getitem(self, this):
if this in self.q:return q[this]
return globals()[this].class
def del(self):
print("Unused variables:",*self.q)
class flogbals(dict):
def setitem(self, this, that):
if this in annotations.q:that=annotations.q.pop(this)(that)
dict.update(globals(),{this:that})
def start():p.from_address(id(globals())+8).value=flogbals
def end():p.from_address(id(globals())+8).value=dict
start()
a:str
a = 6
b:str = 9
c:int = a + b
d:dict
print(c)
end()
@boreal vessel :white_check_mark: Your eval job has completed with return code 0.
001 | 69
002 | Unused variables: d
hello - what's the proper way to type hint when a param is a list, but optional?
def th3_categories(df_inp: pd.DataFrame, categories: list[str] = None):
"""Input th3 dataframe and return filtered based on categories"""
if categories is None:
categories = ['SH', 'SA']
filt = df_inp['DEPTNO'].isin(categories)
df = df_inp.loc[filt]
return df
doesn't Optional[list[str]] works?
ok, and what is it with python 3.10?
My guess would be that MyPy doesn't know if A could change. Just move the assert into the function though?
list[str] | None
Though I've seen None come first in most code with that syntax
(not that i'm using 3.10 yet but)
It does work thanks, but not practical at all ๐ Doesn't mypy look for assignements already? I'm surprised it doesn't catch that it's not updated
Are you running with --strict?
I am pretty surprised as well honestly
mypy my_file.py. Could you reproduce? May be on my end
I'm on Python 3.9.7, with mypy==0.910and mypy-extensions==0.4.3
does this essentially mean that the param type will be a list, or that the value of the param will be None ? isn't that then along the same line of thinking as param: list[str] = None ?
Because of how often = None is used, I believe type checkers have been starting to basically automatically Union with that type when you do that. So yes it is the same
oh rly
Mostly speculation, I just noticed that I've stopped getting complaints of None not being assignable to whatever typehint I had
or just a bad checker ๐ณ
wait am i still supposed to do: categories: Optional[list[str]] = None ? i have to add the =None for pycharm not to squiggly line me when i call the function
That is not true. In fact, type checkers used to have that behavior but we deprecated it.
that's so ugly lol
Yes that's what you're supposed to do
Optional static typing for Python. Contribute to python/mypy development by creating an account on GitHub.
I guess mypy still has this behavior by default ๐
Hey Guys, I'm unstructuring a pretty complex data model with cattrs, I have some fields that are within lists, etc. Sometimes, these are None, is there a way to 'omit_if_None' in cattrs? I know GenConverter has 'omit_if_default'
https://cattrs.readthedocs.io/en/latest/unstructuring.html#unstructuring-hook-factories - allows us to decide whether to omit based on attribute properties, but I can figure out how to do it on the actual values
honestly i find cattrs pretty confusing
it reminds me of some highly abstracted haskell library
i can try to figure out a solution when i get back to a computer, this shouldn't be a difficult task
the issue with cattrs is that they rely on all these factory functions and don't clearly explain the interfaces that those factory functions expect or emit
@fierce ridge so far we have...
dataclass-factory
pydantic
desert
marshmallow
cattrs
for basically serializing or deserializing structs. Why so many, and why is serde still much better?
(P.S: serde the rust library, not whatever is on pypi)
Do they support serializing from/to JSON?
well, not JSON, but you get the idea
i mean, cattrs is on top of attrs
desert also works with attrs
ye
you can also make a system which uses attrs, marshmallow, and desert
no, i am not kidding
yes, i do wish i was kidding
!pypi serde
interesting
I do need to see if I can do it a different way.
Its a configuration system that uses attrs, desert, and marshmallow to be able to support both partial loading, or full loading and validate for any missing values, along with not passing any defaults, to just seralize a single source.
in addition to be fully typed
oh lmao I didn't know it was a thing
I meant the Rust library serde
I think beartype also falls into that category. Isn't serde like pickle but not bad?
What are the communities thoughts on beartyping?
Personally, if it does what they say it does, I think its a great addition to Python. Optional static typing FTW
I don't find runtime explicit typechecking all that compelling. The nice thing about static typing is that it finds issues without running all your code
class Node(object):
children : Iterable[NodeTypeA, NodeTypeB, NodeTypeC]
@property
def firstSiblingAny(self) โ> None | Node:
...
@property
def firstSiblingTypeA(self) โ> None | NodeTypeA:
...
@property
def firstSiblingTypeB(self) โ> None | NodeTypeB:
...
@property
def firstSiblingTypeC(self) โ> None | NodeTypeC:
...
@property
def firstSiblingThisType(self) โ> None | THIS_TYPE:
...
class NodeTypeA(Node):
...
class NodeTypeB(Node):
...
class NodeTypeC(Node):
...
How do I indicate THIS_TYPE?
Or, do I just reimplement it on all the subclasses of node?
T_ThisNodeType = TypeVar('T_ThisNodeType')
...
@property
def firstSiblingThisType(self: T_ThisNodeType) โ> None | T_ThisNodeType:
...
???
Sounds like a job for a self type. define a TypeVar(bound=Node), then use that as the type for self and the return type for firstSiblingThisType
Lemme look it up
T_ThisNodeType = TypeVar('T_ThisNodeType', bound='Node')
cdef class Node(Target):
...
# ------------------------------------------------------------------------------------------
# ------------------------------------- First Siblings -------------------------------------
# ------------------------------------------------------------------------------------------
def firstSiblingOfType(self, nodetype: T_NodeType) -> Undefined | T_NodeType:
...
@property
def firstSibling(self) -> Undefined | T_ThisNodeType:
...
@property
def firstSiblingNode(self) -> Undefined | Node:
...