#type-hinting
1 messages · Page 1 of 1 (latest)
are you implementing dict as a method?
__dict__ should be a dict, not a function returning dict
I'm not sure what the following mypy error means :
There are no .py[i] files in directory '.'
I'm aware that there are no py files in the directory - but i'm not sure why it's not recursing into the directories and picking up the py files that are in there ?
ah ok
I guess I misunderstood what __dict__ is
Does dict call it or no?
no, it would be called by vars()
ok
and that's not even considering if the class uses __slots__ instead
so I should just use a property if I want a to get a dict repr?
What do you mean?
I just want a dict representation of my class instance
you should make a method like asdict()
quick question, what are the 3 generics for collections.abc.Coroutine?
like suppose Coroutine[A, B, C] what do these A, B, and C stand for?
yield type send type and return type respectively
thanks!
most people use a type alias cause the first 2 are useless for 99% of cases
would also be nice if in the documentation for typing they just named the TypeVars YieldType, SendType, ReturnType in class typing.Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co]) (same for Generator)
Yeah it's annoying I go via https://peps.python.org/pep-0008/#type-variable-names this though
Python Enhancement Proposals (PEPs)
So like Callable[P, Coro[T_yield, T_send, R]]
note - the reason for this (#type-hinting message) is that there were ignores in mypy.ini as:
[mypy]
exclude = [ "tests", 'build']
I'm wondering what's typically done here 🤔
When I push up to gitlab build is excluded, so mypy fails on CI, maybe i should use an include instead of exclude ?
ReturnType is wrong because R is idiomatic, and it looks like a TypeAlias
According to pep8 T_yield is wrong because it's named based on how it's used and not how is behaves, but I don't care :p
doesn't standard lib things like inspect.iscoroutinefunction properly narrow types or do we have to implement our own type guards?
I got into this situation :
def benchmark(anycallable_or_coro: AnyFunc | Coro):
if iscoroutinefunction(anycallable_or_coro):
# AnyFunc is aliased to unions of Callable (see definition)
# Coro is aliased to Coroutine
# At this point, `anycallable_or_coro` should be narrowed to CoroFunc
# but pyright thinks :
# Argument of type "AnyFunc | Coro[Unknown]" cannot be assigned to parameter "corofunc" of type
# "CoroFunc[Unknown]" in function "_corofunc_wrapper"
return _corofunc_wrapper(anycallable_or_coro)
Definitions :
Coro: TypeAlias = Coroutine[None, None, Result]
Func: TypeAlias = Callable[Params, Result]
CoroFunc: TypeAlias = Callable[Params, Coro]
AnyFunc: TypeAlias = Func | CoroFunc
What does reveal_type() say?
A Coro isn't a CoroFunc
There's no return type
anycallable_or_coro is a union including CoroFunc too, no?
includes Coro as well
I think you want Func | CoroFunc | Coro
yeah!
just AnyFunc should do
What happens if you add a return type?
I hope that is what is happening right now 
return type in where?
the function def of benchmark ?
stdlib/inspect.pyi line 191
def iscoroutinefunction(obj: Callable[_P, object]) -> TypeGuard[Callable[_P, CoroutineType[Any, Any, Any]]]: ...```
Also the TypeGuard is different in >= 3.8
I was implementing a typeguard and ended up overloading the definitions. And that seems to work too 
god I love typing
I did it like this :
@overload
def benchmark(anycallable_or_coro: Func) -> Benchmarked:
...
@overload
def benchmark(anycallable_or_coro: CoroFunc) -> Callable[Params, Benchmarked]:
...
@overload
def benchmark(anycallable_or_coro: Coro) -> Benchmarked:
...
def benchmark(anycallable_or_coro):
if iscoroutinefunction(anycallable_or_coro):
reveal_type(anycallable_or_coro)
return _corofunc_wrapper(anycallable_or_coro)
if iscoroutine(anycallable_or_coro) or callable(anycallable_or_coro):
return Benchmarked(anycallable_or_coro)
msg = f"Expected callable or coroutine, got {anycallable_or_coro!r} instead"
raise TypeError(msg)
Just only allow async functions
and the reveal type in this situation said Type of "anycallable_or_coro" is "Unknown"
is it possible to skip the fields of a generic?
like say I have a class Foo which implements two generics
then can I do something like Foo[None, int]
then let first field be inferred?
edit: Apparently this can be done by filling out the fields with an ellipsis ...
you could use Awaitable[Result]
is it valid to annotate to the instance self with class generics for overloading?
for example :
@overload
def function(
self: Benchmarked[Params, Awaitable[Result]]
) -> Callable[Params, Awaitable[Result]]:
...
@overload
def function(self: Benchmarked[Params, Result]) -> Callable[Params, Result]:
...
@property
def function(self):
if self.__original_corofunc is None:
return self.__func # Non-async func
return self.__original_corofunc # async function
because reveal_type shows the the return type as Unknown and assignments are inferred as Unknown as well 
to make this minimally reproducible I did :
from __future__ import annotations
from typing import Generic, TypeVar, overload
T = TypeVar("T", int, str, float)
lol = {
int: str,
str: float,
float: dict,
}
class Foo(Generic[T]):
def __init__(self, bar: T) -> None:
self._bar: T = bar
@overload
def bar_it(self: Foo[int]) -> str:
...
@overload
def bar_it(self: Foo[str]) -> float:
...
@overload
def bar_it(self: Foo[float]) -> dict:
...
def bar_it(self):
return lol[type(self._bar)]()
f = Foo("Hi")
print(f.bar_it())
But why does making it a property make it Unknown
from __future__ import annotations
from typing import Awaitable, Generic, TypeVar, overload, ParamSpec
from typing_extensions import reveal_type
T = TypeVar("T")
P = ParamSpec("P")
class Foo(Generic[P, T]):
def __init__(self, fn) -> None:
self.fn = fn
@overload
def function(self: Foo[P, Awaitable[T]]):
...
@overload
def function(self: Foo[P, T]):
...
@property
def function(self):
return self.fn
@Foo
async def a():
...
@Foo
def b():
...
reveal_type(a.function()) # Type of "a.function()" is "Unknown"
reveal_type(b.function()) # Type of "b.function()" is "Unknown"

There isn't enough information to infer the generics.
Add them explicitly to the Foo call
@Foo[str, str]
generic properties have been denied before, for technical reasons I believe
How to mention that I am expecting an invariant list of float and not a covariant one?
def foo(x: list[float]): ...
variance is a property of the generic type itself, not a property of individual list objects
specifically list[T] is invariant in T
Yeah, list is already invariant over its argument so there isn't a "covariant one"
huh 
I think I got my definition wrong
if list is invariant
then how is this possible :
def foo(x: list[float]) -> None:
print(x)
foo([1, 2, 3]) # This is accepted
# so isn't list covariant?
# Because int was considered as an subtype of float?
it's called type context or bidirectional inference
the type checker infers [1, 2, 3] to be of type list[float]
generally if you put it in a variable l = [1, 2, 3] first it will reject it
oh
interesting
invariance doesn't mean the list can't contain subtypes of float, just that the static type must be list[float]
that rejection after being assigned to a variable is so strange
specifically, the inference goes inside the list itself, interpreting each individual int as a float
which bypasses the concern for variance
yeah...
so is there a way to tell I am accepting a covariant list now?
class Parent:
pass
class Child:
pass
def foo(x: list[Parent]) -> None:
print(x)
foo([Child()]) # Since lists are invariant, this is rejected
# How to mention that we accept covariant list of Parent classes?
(excuse my stupid questions, just learning variance now
)
that would already be accepted if Child was actually a subclass of Parent
for the same reason as we talked about with int/float
there is no way to actually say "covariant list", but a close equivalent is to use Sequence
depends on what you use it for
Sequence[T] is covariant
oh yeah, I forgot to inherit right there 
but now that example gets accepted.
So I can't fully wrap my head around how list isn't covariant when Child classes are accepted when the function signature mentions about accepting parent types
it's because the child object is interpreted as a parent object in that specific expression
not in general
foo: (int, int)
this is not a valid replacement for
foo: Tuple(int, int)
right?
right
(Neither are valid)
im blind
right, should be Tuple[int, int] (or tuple[int, int] in 3.9+)
Is it possible to define a type variable/alias that has an unbound variable to it? I'm probably getting the terminology wrong, but something like:
myalias[T] = Mapping[T, T]
CC @ornate thunder
myalias = Mapping[T, T] will do it
no because then I can't pass in a T
But you can
myalias[int]
Try it
How can I fix this?
https://mypy-play.net/?mypy=latest&python=3.10&gist=62d1dd91437fc1edffd0bd5a298858b8
import re
markers = {
"span_markers": (["abc"], ["abc"])
}
span_markers: tuple[list[re.Pattern[str]], list[re.Pattern[str]]]
span_markers = tuple(
list(re.compile(m) for m in span)
for span in markers["span_markers"])
Incompatible types in assignment (expression has type "Tuple[List[Pattern[str]], ...]", variable has type "Tuple[List[Pattern[str]], List[Pattern[str]]]")
It's like it doesn't like that it somehow can't see exactly how many elements in the tuple there are
Same error when I give it an explicit type markers: dict[str, tuple[list[str], list[str]]]
I'm not familiar with Final[Mapping], what would that do
Final indicates that the variable under the given name does not get reassigned
It does not make mutable types immutable (in terms of typing)
So I wouldn't think it'd help here
Right, ok
I had completely honed in on the Final and missed the Mapping portion, which would indicate that it's immutable, but I don't think it'll work. I don't think specifying the number of arguments would work like this no matter how simple the expression inside is
span_markers: tuple[str, str] = tuple(["a", "b"]) this would still produce the same error
Yeah, that should be span_markers: tuple[list[str]] in your case
@past pumice :white_check_mark: Your 3.11 eval job has completed with return code 0.
('a', 'b')
It's in the specification for the tuple function lol
But yeah I can see why you'd think it'd work that way
Not sure which would be more intuitive
Well, I can't really come up with a good solution
It doesn't seem to be possible to do this with the tuple function
I don't see how else to construct a tuple 😕
Not like there are tuple comprehensions
Yeah so what I've come to is just using the comma format and duplicate the generation
Something like ```py
= list(re.compile(m) for m in markers["span"][0]), list(re.compile(m) for m in markers["span"][1])
Hardly pythonic though
The other options are to embrace the variable-length or disable type checking for that line
Expressing that it's a fixed-length pair of lists is exactly why I'd went with tuple of lists instead of list of lists 😕
Seems like the choice is between expressing the correct semantics, or compromising on them to appease the type checker
I think it's fair to say this is an example where the type checker is an obstacle
Perhaps a set?
I'd wonder "what are they trying to unique here" if I saw a set there, wouldn't you?
Fair
You could use a cast: https://mypy-play.net/?mypy=latest&python=3.10&gist=4b87cc1c5ba0e77e09b779a89f3b200d
But also: Pyright handles your code just fine.
Doesn't subclassing with a new variant typevar make the derived class use the new typevar?
I did this :
from typing import TypeVar
T_contra = TypeVar("T_contra", contravariant=True)
class Parent: pass
class Child(Parent): pass
class ContraTuple(tuple[T_contra]):
def first_element(self: "ContraTuple[T_contra]"):
return self[0]
c = ContraTuple[Child]([Parent()]) # Error during typecheck
and pyright reported this :
Argument of type "list[Parent]" cannot be assigned to parameter "__iterable" of type "Iterable[Child]" in function "__new__"
TypeVar "_T_co@Iterable" is covariant
"Parent" is incompatible with "Child"
Why is ContraTuple using the old covariant typevar when it should be using the contravariant one? why does this happen?
TypeVar "_T_co@Iterable" is covariant
why isn't this contravariant by using the newT_contra@ContraTuple?
you can't make tuple contravariant by force
it's covariant over its arguments
in general custom variance is only accepted by Generic[T]
Oh I see, thanks!
def _corofunc_wrapper(
corofunc: Callable[Params, Awaitable[Result]]
) -> Callable[Params, Benchmarked[Params, Result]]:
def benchmark_wrapper(
*args: Params.args, **kwargs: Params.kwargs
) -> Benchmarked[Params, Result]:
coro: Awaitable[Result] = corofunc(*args, **kwargs)
return Benchmarked[Params, Result](routine=coro, original_callable=corofunc)
return benchmark_wrapper
# Sync Function
@overload
def benchmark(anyfunc: Callable[Params, Result]) -> Benchmarked[Params, Result]:
...
# Async Coroutine Function
@overload
def benchmark(
anyfunc: Callable[Params, Coroutine[Any, Any, Result]]
) -> Callable[Params, Benchmarked[Params, Result]]:
...
def benchmark(anyfunc: Callable):
if iscoroutinefunction(anyfunc):
return _corofunc_wrapper(anyfunc)
if callable(anyfunc):
return Benchmarked(routine=anyfunc, original_callable=anyfunc)
msg = f"Expected callable, got {anyfunc!r} instead"
raise TypeError(msg)
So I have the following signatures as mentioned above.
If a corofuncttion is fed into benchmark then a wrapper function is returned which when called returns an instance of Benchmarked class
if a sync function is fed then an instance of Benchmarked is directly returned.
However, this is not getting inferred.
For example :
@benchmark
async def foo():
return 1
x = foo()
reveal_type(x)
# Getting inferred as Type of "x" is "Coroutine[Any, Any, Literal[1]]"
# When in reality it's Benchmarked[(), Literal[1]]
# How can I get this fixed?
I have looked at the overloads for quite some time, but they seem to be correct. How can I fix this?
It's almost like the typechecker is one step ahead in each type change...
@benchmark
async def foo():
return 1
print(foo, type(foo)) # <function _corofunc_wrapper.<locals>.benchmark_wrapper at 0x000002BED19EE5F0> <class 'function'>
reveal_type(foo) # Type of "foo" is "Benchmarked[(), Coroutine[Any, Any, Literal[1]]]"
x = foo()
print(x, type(x)) # foo: not measured <class 'tools.benchmark.Benchmarked'>
reveal_type(x) # Type of "x" is "Coroutine[Any, Any, Literal[1]]"
I simplified / refactored the above code to this :
class CoroutineWrapper(Generic[Params, Result]):
def __init__(self, corofunc: Callable[Params, Awaitable[Result]]) -> None:
self.cf = corofunc
def __call__(
self, *args: Params.args, **kwargs: Params.kwargs
) -> Benchmarked[Params, Result]:
coro: Awaitable[Result] = self.cf(*args, **kwargs)
return Benchmarked[Params, Result](coro, self.cf)
def benchmark(
anyfunc: Callable[Params, Result | Awaitable[Result]]
) -> CoroutineWrapper[Params, Result] | Benchmarked[Params, Result]:
if iscoroutinefunction(anyfunc):
return CoroutineWrapper[Params, Result](anyfunc)
if callable(anyfunc):
return Benchmarked[Params, Result](routine=anyfunc, original_callable=anyfunc)
msg = f"Expected callable, got {anyfunc!r} instead"
raise TypeError(msg)
Still doesn't typecheck but looks better
@benchmark
async def foo():
return 1
reveal_type(foo) # Type of "foo" is "CoroutineWrapper[(), Literal[1]] | Benchmarked[(), Literal[1]]"
# Actually it's CoroutineWrapper[(), Literal[1]]
reveal_type(foo()) # Type of "foo()" is "Benchmarked[(), Literal[1]] | Literal[1]"
# Actually it's Benchmarked[(), Literal[1]]
# So how to get proper inference?
Just make your code only work for async functions
Don't worry about coroutine objects they are an implementation detail of your async framework
Hey, how can I typehint a type that's common in the whole class?
Example:
class cls:
def foo(self, ...) -> T:
...
def bar(self, e : T) -> int:
...
In both functions the same type should be passed in
!d typing.Generic
class typing.Generic```
Abstract base class for generic types.
A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as:
```py
class Mapping(Generic[KT, VT]):
def __getitem__(self, key: KT) -> VT:
...
# Etc.
``` This class can then be used as follows...
doing class cls(Generic[T]) will have the T in the whole body of the class
And I guess I still need to declare a T = TypeVar('T') right?
yes
okay, thanks
it works
cast was interesting to learn about, thanks 🙏 But it's still too much noise for something that's mypy's fault (and as you said, pyright is fine with it)
Oh yeah! Making two separate decorators for async and sync function makes everything work perfectly (typechecks as well) but I wanted a single decorated which handled both cases, whilst that works during runtime it doesn't typecheck well. So do i have to separate this into two decorators?
ok but why do you need one that works for just coroutine objects?
the type system also doesn't know if something is a function that returns an object or a function that returns an Awaitable[object]
Because Awaitable[object] is also an object @devout barn
also inspect.iscoroutinefunction often doesn't match what is or isn't usable as a coroutine function
https://mypy-play.net/?mypy=latest&python=3.10&gist=b752d2b28cfa9c2345513760a8fbb575
from typing import TypeAlias, Literal, Tuple
MarkerKey: TypeAlias = Literal["start_marker", "na_span_markers"]
StrMarkers: TypeAlias = str | Tuple[list[str], list[str]]
def fn(markers: dict[MarkerKey, StrMarkers]):
pass
my_markers = {
"start_marker": "intro",
"na_span_markers": (["abc"], ["def", "ghi"])
}
fn(my_markers)
Argument 1 to "fn" has incompatible type "Dict[str, Sequence[Sequence[str]]]"; expected "Dict[Literal['start_marker', 'na_span_markers'], Union[str, Tuple[List[str], List[str]]]]"
Looks to me like my_markers obeys dict[MarkerKey, StrMarkers]. Am I missing something?
it looks like you should be using a typed dict but i cant see whats wrong with this
!d typing.TypedDict
class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict "dict").
`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
Don't typed dicts need to have homogeneous types in their values?
no?
Right:
each key is associated with a value of a consistent type
My values can bestrortuple[list[str], list[str]]
fwiw my_markers: dict[MarkerKey, StrMarkers] = { fixes this
but you could surely still just use a union for this as you already do
Right, but I don't want to force the caller to type hint their arg
I'll try this
if you dont want the user to typehint neither will work
from typing import TypedDict
class Markers(TypedDict):
start_marker: str
na_span_markers: str | tuple[list[str], list[str]]
def fn(markers: Markers):
pass
my_markers = {
"start_marker": "intro",
"na_span_markers": (["abc"], ["def", "ghi"])
}
fn(my_markers)
Argument 1 to "fn" has incompatible type "Dict[str, Sequence[Sequence[str]]]"; expected "Markers"
https://mypy-play.net/?mypy=latest&python=3.10&gist=dd750274f42c0164eddc598cb99eaa13
Sigh
also to solve this issue you can just pass the arg straight to the function
fn({
"start_marker": "intro",
"na_span_markers": (["abc"], ["def", "ghi"])
})```should work
Yeah I think I'll go with this approach, along with the TypedDict. Thank you 🙂
I have this BaseChannel TyedDict which looks like this: ```py
class BaseChannel(TypedDict):
type: Literal[0, 1, 2]
Now for every value of type, there will be another child class with more variables.. Like ```py
class Type1Channel(BaseChannel):
type: 0
foo: str
But it raises an error that both classes have different types.. How do I fix that? Should I just remove the type variable from BaseChannel?
type it as Literal[0]
Oh sorry I actually did that just forgot to write it here.. it's still same though
Just in case if this helps
def f(channel: Channel):
channel["type"] = 2
This function is legal, and it won't work correctly if you pass in a TextChannel
Should I just remove the type variable from BaseChannel?
Yes, probably.
You can also use a union type:
Channel = Union[Channel1, Channel2, Channel3, ...]
Yeah removing them works fine. I can have separate types for every subclass or I can just ignore[override] them because BaseChannel can still have any Literal value out of those listed.. Thanks :)
WARNING: there is a new pyright version available (v1.1.262 -> v1.1.263).
Please install the new version or set PYRIGHT_PYTHON_FORCE_VERSION to `latest`
Any way to get rid of this warning? I run a conda environment and 262 is the latest on conda channels
how did you install pyright?
conda install pyright=1.1.262 -c conda-forge -y
conda install pyright -c conda-forge -y
Collecting package metadata (current_repodata.json): done
Solving environment: done
# All requested packages already installed.
I don't think it matters so long as I'm in a conda environment
Their channels are just not updated with the latest pyright yet
conda search pyright | tail
pyright 1.1.260 py38h0a891b7_0 conda-forge
pyright 1.1.260 py38h50598f1_0 conda-forge
pyright 1.1.260 py39h4d8b378_0 conda-forge
pyright 1.1.260 py39hb9d737c_0 conda-forge
pyright 1.1.262 py310h5764c6d_0 conda-forge
pyright 1.1.262 py37h540881e_0 conda-forge
pyright 1.1.262 py38h0a891b7_0 conda-forge
pyright 1.1.262 py38h50598f1_0 conda-forge
pyright 1.1.262 py39h4d8b378_0 conda-forge
pyright 1.1.262 py39hb9d737c_0 conda-forge
Oh. Setting the env var worked
Weird. There was no installing done. But the warning doesn't complain anymore. 🤷♂️
it overrides the version pyright thinks it is
What format is this time string in?
'2022-07-22T17:58:09+0000'
iso8601?
262 is broken, fyi — use 261 or 263.
261 it is. Until 263 is available on conda
Why not install pyright from npm?
npm i -g pyright
it uses node anyway. The pip package is just a wrapper
why is pyright made in node wtf
it is easier to do vscode extensions with typescript+node, and node is faster than python so that is another advantage
Hey, if I have a dict, that contains classes with a common base classes, can I just do this: dict[CommonBase0, CommonBase1]? Or I have to use 2 bound TypeVar's?
class CommonBase0:
pass
class CommonBase1:
pass
my_dict : dict[CommonBase0, CommonBase1] = dict()
Trivially this looks fine
But what does the dict actually look like?
Wait no this is definitely running into variance issues
It's probably better to use a union because you'll get errors if you assign a subclass of common base 1 to the value
Union of all classes that might set as the value/key?
class SubClass0_0(CommmonBase0):
pass
class SubClass1_0(CommmonBase1):
pass
my_dict[SubClass0_0] = SubClass1_0() # Edited
And so on.
If that helps anything, a value of the same class never appears
So the type of both the key and value are unique
can you show me the definition of my_dict
Sorry for the late reply, had some stuff to do.
well I mean
self.my_dict = {
# Key is always a class type (Common base class of all keys is `CommonBase0`), value is always a class instance, and the common base type for all of them is `CommonBase1`
# CommonBase0: CommonBase1()
SubClass0_0: SubClass1_0(),
SubClass0_1: SubClass1_1(),
SubClass0_n: SubClass1_n(),
}
is there any way to mark a class variable required to be defined in subclasses?
Didn't really work when I tried it. Guess I'll just do a normal abstract property to at least partially get it
Wdym by didn't really work?
pyright is complaining about assigning to the property type, and mypy didn't care if I overrode it with a method with the same signature as the property
oh well a variable is incompatible with a property
how would I get something like this to work?
class BaseDict(typing.TypedDict):
a: int
class DerivedDict(BaseDict):
b: int
class Super:
def from_dict(self, arg: BaseDict) -> None:
...
class Child(Super):
def from_dict(self, arg: DerivedDict) -> None:
...
I understand why it doesn't type check, but I'm not sure how to get the behaviour
is something like this pythonic? i dont see why not
typing.ClassVar[list[int]]
that isnt checked is it?
wdym isnt checked?
Like it doesn't enforce that a subclass has to override it
sorry i dont understand what you mean😅
Aren't dictionaries invariant? You can't do that afaik
Was the point of this annotation that subclasses set that class variable? If it was, then Gobot is unsure whether it will be enforced by type checkers.
T = TypeVar('T')
def f(iterable: Iterator[T]) -> Iterator[T]:
for e in iterable:
yield e
Is this the most correct way I can typehint such a function?
I figured I needed Iterator instead of Iterable since that for loop requires a __next__
@buoyant swift :x: Your 3.11 eval job has completed with return code 1.
001 | 1
002 | 2
003 | 3
004 | Traceback (most recent call last):
005 | File "<string>", line 5, in <module>
006 | TypeError: 'list' object is not an iterator
Uuh I get why your code broke but I'm not sure what you're suggesting I do about mine
so you don't need Iterator, you can just use Iterable
Ohh, you meant "for loops use iter", not "for loops, I should use iter"
So __next__ has nothing to do with it, gocha
What if iterable is not a list but an iterator, still holds?
iterators are iterable, so yes
Oh yeah ofc, iterators are iterables
But the return type is more correct as Iterator
yep
Thank you
PEP 695 is your answer
With PEP695 I'm surprised more keywords aren't made soft
Wonder if there are any that can't be soft, kinda doubt it
you mean using keywords instead of punctuation? that was specifically considered and rejected
!pep 695
pretty excited about this
I don't understand this: ```python
class ClassC[V]:
# The use of M and K are not allowed for "method2". A type checker
# should generate an error in this case because this method uses the
# new syntax for type parameters, and all type parameters associated
# with the method must be explicitly declared. In this case, K
# is not declared by "method2", nor is it supplied defined an outer
# scope.
def method2[M](self, a: M, b: K) -> M | K: ...
If I want to type my method like that, what would be the fix?
def method2[M, K]?
What would M and K refer to here? The input types or return types?
they're type variables, they're allowed in both input types and return types
I was just thinking why repeat itself if the input annotations are already there; I guess the input annotations are necessary to identify each parameter as a specific type
Ooh nvm I read this wrong. I thought K was V and got thoroughly confused.
Both, they would automatically be inferred to be invariant because they're both in the arguments and return type afaik.
How do you lie about covariance or similar with this?
What's the right way to type hint a pred parameter so as to also work with a regex match as pred?
Currently I'm just marking it as Callable but I'm trying to be more specific if possible
def fn(pred: Callable, iterable: Iterable[T]) -> Iterator[T]:
for e in iterable:
if pred(e):
yield e
fn(compiled_re_pattern.match, my_list)
My first reflex was Callable[[T], bool] but that didn't work. Part of it was that re.match doesn't return a bool
Callable[[T], bool] (Callable[[T], Any] is technically more correct but i think bool is better)
This is what I get with Callable[[T], bool]
incompatible type "Callable[[str, int, int], Optional[Match[str]]]"; expected "Callable[[str], bool]
ok this is a case where Any makes sense as the last argument
but idk whats up with the param list here
That's re.Pattern.match's function signature Pattern.match(string[, pos[, endpos]]) (Callable[[str, int, int], ...)
Instead of Any, is there a way to say "convertible to bool"?
[
Apparently it is in the rejected ideas https://peps.python.org/pep-0695/#explicit-variance
Python Enhancement Proposals (PEPs)
May want to move forward with posting to the thread about some use-cases for lying about covariance
What return type do you put for a function that could return T or raise exceptions?
there's no way to annotate that a function might raise, like in java, so just T
Alright, T it is, thanks
There is a Exception type in the code snippets in the typehinting docs, but it's apparently only for "documentational" purposes because Guido didn't want it in
where in the docs?
def async_query(on_success: Callable[[int], None],
on_error: Callable[[int, Exception], None]) -> None:
# Body
that just means the callback takes an Exception object as an argument
Yeah. Exception is neither defined nor mentioned anywhere before
it's a builtin
Oh. That I didn't know lol
https://stackoverflow.com/questions/44282268/python-type-hinting-with-exceptions
Exceptions
No syntax for listing explicitly raised exceptions is proposed. Currently the only known use case for this feature is documentational, in which case the recommendation is to put this information in a docstringGuido van Rossum has strongly opposed adding exceptions to the type hinting spec, as he doesn't want to end up in a situation where exceptions need to be checked (handled in calling code) or declared explicitly at each level.
You actually want object, because you're not using any functionality of the object beyond the base functionality. If you used Any, the type checker would then permit calling methods or whatnot, but object promises you only do base object functions like bool(), str(), == etc.
Took me a minute to grok that, but it's an interesting notion. Since I'm just using the thing's __call__ method, that's considered a base functionality and hence object fits better iiuc
Not call, that's not base object behaviour. But yes to bool.
Oh right __call__ is obv not base. By bool you mean __bool__ which is a base method?
Yep, because all objects are convertable to bool, unless they explicitly raise an exception. By defautl they're just always true.
Other examples are __str__()/__repr__(), __eq__(), __ne__()...
Nice. Thank you! I'll try it out later
numpy arrays would like to talk
Is bool None?
!e ```py
import numpy as np
array = np.array(10)
print(bool(array))
@rare scarab :white_check_mark: Your 3.11 eval job has completed with return code 0.
True
??
!e
import numpy as np
array = np.array([10, 11])
print(bool(array))
@acoustic thicket :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "<string>", line 4, in <module>
003 | ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
that's a 0-dim array. for arrays of multiple- ^
Is every regular type annotation like
class DerivedInt(int):
pass
def f(x: int):
return "Some stuff", x
f(DerivedInt())
covariant by default? 
Yes, T is covariant with respect to T
whereas list[T] is invariant with respect to T
Does
cannot be further subscribed.
(https://github.com/python/cpython/blob/main/Lib/typing.py#L623)
mean that ClassVar cannot be aliased?
Because creating aliases for typing.ClassVar does not depict the same behavior as the non-aliased ones.
Example :
class Foo:
var: ClassVar[str] = "Some"
def __init__(self) -> None:
self.var = "Should fail" # Fails as expected (fine)
but,
Aliased = ClassVar[str]
class Bar:
var: Aliased = "Some"
def __init__(self) -> None:
self.var = "Should fail but doesn't" # Typechecks
Could this be a bug?
I am using pyright 1.1.263 (latest)
Lib/typing.py line 623
ClassVar accepts only types and cannot be further subscribed.```
classvar cannot be generic (ClassVar[list[T]])
that's what the error is supposed to be for
I'm trying not to pollute the code file with # type: ignores so I'm doing it from mypy.ini, but isn't there a simpler/more compact way of doing this?
[mypy]
[mypy-funcy.*]
ignore_missing_imports = True
[mypy-more-itertools.*]
ignore_missing_imports = True
[mypy-docx.*]
ignore_missing_imports = True
[mypy-simplify_docx.*]
ignore_missing_imports = True
Like somehow applying ignore_missing_imports to a list of specific modules?
I have
class Foo:
x: tuple[int, int] | tuple[str]
def __init__(self, *args: ???):
self.x = x
```what should go in ??? to make this actually work?
I've tried *tuple[int, int] and *tuple[str] and they both work individually
but not in a union
(I changed it to Unpack cause its a syntax error with the union)
isn't it just the type of the individual element? so tuple[int, int] | tuple[str]
*(tuple[int, int] | tuple[str]) would make sense but I don't think the runtime allows that
can't unpack unions
Expected TypeVarTuple or Tuple as type argument for Unpack Pylance reportGeneralTypeIssues
but then what's the idea? if you want to assign to self.x, why even have the *args as opposed to a single, non-variadic arg?
because i shouldnt have to pander to the type system for something so trivial
also i dont like the way passing a tuple literal looks
what is the trivial part? inferring the type of *args ?
i guess...
I still don't get the reason for the variadic args... did you want the type checker to infer that 2 arguments should be tuple[int, int] and a single arg should be a tuple[str] ?
yes
aha
Well, i kinda think it's good that this is not allowed. Otherwise whoever is reading the signature for __init__ would have to also read the type of self.x to figure out that it's not really a param that takes an arbitrary number of arguments, but rather a shorthand for a context-dependent type
im the only person using this
Your future self in 3 months is essentially a different person ^^
maybe @overload for __init__?
you already mentioned it, sorry for ping
Defining an overload would be best, specifying 1 or 2 positional-only args.
How would I write a type stub for a class to indicate that the class should not be instantiated directly (via cls())?
I tried def __new__(cls) -> NoReturn: ... but it didn't work, so right now I am including an argument in the constructor which type cannot be accessed externally, like
class _Dummy:
pass
class MyClass:
def __init__(self, internal_only: _Dummy) -> None: ...
Wondering if there is a better way to do this
make it an abc.ABC?
hm instantiating ABCs doesnt set off typecheckers
interesting
it should complain only if there's still an abstract method so makes sense I suppose
You could have a dummy abstract method just so that the class can't be instantiated. What sucks is that all child classes will have to implement that dummy method to make them instantiable, though
I am wrapping my head around dataclasses's typed descriptors: https://docs.python.org/3/library/dataclasses.html#descriptor-typed-fields
The example in the docs has the descriptor saving the value to the underlying dataclass, and using __set_name to figure out where that should be saved.
Why not store the value directly on the instance of the descriptor? EG https://pastebin.com/zMw71Svk
which feels way less complicated? There's something I'm missing here I feel like?
I also get the error: TypeError: non-default argument 'foo' follows default argument when defining it, which doesn't make sense to me because I am giving a default argument to that field - does default here mean "not a primitive type" ?
I don't remember whether this was possible... how do I type that two attributes are of the same type out of a union?
Making the class generic is not a choice for me. I want to make type checkers be able to infer the type of one of the attributes if it knows the other
isn't that just a typevar bound to more values?
could you give some example on where you need this?
do you want a tagged union?
Yes, technically, but I cannot tell the type checker this. For example, the following doesn't work because "T has no meaning" ```python
from typing import TypeVar, Union
T = TypeVar('T', bound=Union[str, int])
class Test:
attr: T
var: T
and why can't you make the class generic again?
the type-checker will infer what the type should be if it knows one of those types
I don't know sorry
It is not known statically what the attributes will be, and that will just become Any which gives me no benefit?
how are these attributes getting set?
It's based off of runtime data I get from the network. I cannot know in advance what the types of these are
well then what better thing would you want to do, the type checker won't run on runtime to check those things
I don't want to make separate classes for this. If I can't figure this out I'll just leave it as-is with unions
just make the class generic
yeah, the type-checker will infer it as the union without being able to narrow it. but that's fine
In the above example, I want to be able to do ```python
def func(x: Test):
if isinstance(x.attr, str):
# The type checker now knows that x.var is also str
Well, I am hoping to.. if I can 😅
this is your best option probably: ```py
from typing import TypeVar, Union, Generic
T = TypeVar('T', bound=Union[str, int])
class Test(Generic[T]):
attr: T
var: T
I don't think type-checkers are smart enough to narrow the type of x if some of it's values is narrowed
i think they are
Yeah, unfortunate
but type-wise making the class generic does make sense for what you want
and this might get added into pyright eventually
Hmm yeah
@blazing nest
from typing import TypeVar, Union, Generic
from typing_extensions import reveal_type
T = TypeVar("T", str, int)
class Test(Generic[T]):
attr: T
var: T
a = Test()
if isinstance(a.attr, str):
reveal_type(a.var)
mypy tells met the revealed type is str here
Ooh! Fancy haha
@blazing nest or if you want both pyright and mypy there's type guards
from typing import TypeVar, Generic, TypeGuard
from typing_extensions import reveal_type
T = TypeVar("T", str, int)
class Test(Generic[T]):
attr: T
var: T
def is_string_test(t: Test[T]) -> TypeGuard[Test[str]]:
return isinstance(a.attr, str)
a = Test()
if is_string_test(a):
reveal_type(a.var)
yeah, typeguards would solve this
you could even make one to test out the specific type from the union dynamically
def is_test_of(test: Test, of: type[T]) -> TypeGuard[Test[T]]:
return isinstance(a.attr, of)
How does one type hint fixture request type?
I have a fixture whose only purpose is monkey patching a aiosmtp smtp client since i need to test methods that use them internally. I would also like to parametrise the fixture to represent different states the mail server could be in. However I'm not sure how to type hint the request parameter of the fixture
@pytest.fixture(params=["hello", "pytest"])
def patch_smtp(monkeypatch: pytest.MonkeyPatch, request: pytest.FixtureRequest) -> None:
def mock_init(self, *args: Any, **kwargs: Any) -> None:
self.is_mock = True
self.message = request.param
monkeypatch.setattr(SMTP, "__init__", mock_init)
however using request.param later is treated as an error by mypy error: "FixtureRequest" has no attribute "param"
oh
ok
-.-
How do you do type a forwarding function? E.g.:
from typing import Optional
def foo(a: str, b: str) -> None:
pass
def bar(*args, c: Optional[str] = None) -> None:
foo(*args)
How do you type *args without copying the signature from foo manually?
paramspec
If bar will only forward to foo, then I guess you just write the args again =/
But if you're doing something like a decorator that can be applied to any function (and therefore receives such function as an argument), then you can use ParamSpec
Yer, I want to forward bar to foo so I guess I'm out of luck.
Can you show me how to do it with my example? I only know how do use ParamSpec with decoration. Thanks.
nvm you can't use paramspec for this, my bad
_P = ParamSpec("_P")
_T = TypeVar("_T")
def call_fn(fn: Callable[_P, _T], *args: _P.args, **kwargs: _P.kwargs) -> _T:
fn(*args, **kwargs)
I think there's this pending PEP going around where you can declare type arguments for functions using the square bracket syntax... that would allow you do have a forwarding function that doesn't need to take a function as a runtime parameter
and if you need more arguments, have a look at https://docs.python.org/3/library/typing.html#typing.Concatenate
You can but it doesn't work well
You need to make a decorator that copies the type
from __future__ import annotations
from typing import Callable, Generic, TypeVar
from typing_extensions import reveal_type
A = TypeVar("A")
B = TypeVar("B")
C = TypeVar("C")
class Compose(Generic[A, B]):
def __init__(self, f: Callable[[A], B]):
self.f = f
def __or__(self, g: Callable[[B], C]) -> Compose[A, C]:
return Compose(lambda a: g(self.f(a)))
def __call__(self, a: A) -> B:
return self.f(a)
def generic(a: A) -> A:
return a
def concrete(a: int) -> int:
return a
reveal_type(Compose(concrete) | generic) # "Compose[int, int]"
reveal_type(Compose(concrete) | concrete) # "Compose[int, int]"
reveal_type(Compose(generic) | generic) # "Compose[A@generic, A@generic]"
reveal_type(Compose(generic) | concrete) # Expected "Compose[int, int]"
# Operator "|" not supported for types "Compose[A@generic, A@generic]" and "(x: int) -> str"
Bad inference or just incompatible types?
seems like a bug to me
It might be that the bidirectional inference isn't strong enough for generic types
If so, there's probably a closed issue somewhere
Hi, how is it going? Can I ask what's your opinion about pydantic? Is this a right place to ask it ?
Q: Can I ask a question?
Yes. Always yes. Just ask.
A guide for how to ask good questions in our community.
That was a perfectly fine question
I think pydantic is directly tied to typing, as its main feature typing and typing validation. So it is best place to ask about it here
Fixed
Pydantic is literally in the channel description
I mean, I thought the person wanted to ask people's general opinion about pydantic. Whether it's good, bad, where it is applicable etc.
Oops. Yeah. My mind fuzzy searched "can I ask" pattern and fired false positive event
Have you had any bears infect you lately? 😛
I don't get it. I said fuzzy, not furry 😛
Yeah I didn't mean that, I got something else mixed up
Awesome since it provides really easy to use data validation, but its main feature for me auto generating openapi schema for automated interactive documentation generation in FastAPI(redoc and etc)
I am in general a bit sceptical of typing validation on run time. I would prefer to see typing validation on compiling and runtime at the same time, and therefore not really seeing only runtime data validation as worthy goal to pursue. (Having good unit / integration testing and code architecture is first goal for me)
I still do typing anyway, but just because I wish getting IDE syntax colorations.
Perhaps together with mypy it should shine, but it is a bit of pain in the ass to setup. May be I am wrong though, but why Django mypy required additional library of stubs in order to work, will it be same difficult in fastAPI or more? 
Regardless of pydantic or not, but regular dataclasses I use as often as wishing. Just for better self documentation and variable grouping / cleaner code / better code structure. I haven't explored everything pydantic can offer though
pydantic is nice, but all the automatic type conversion feels a little dicy for me. here's a blog post i found interesting on the subject: https://threeofwands.com/why-i-use-attrs-instead-of-pydantic/
This post is an account of why I prefer using the attrs library over Pydantic. I'm writing it since I am often asked this question and I want to have something concrete to link to.
I feel like pydantic just nearly misses what it'd do best
Rather than just data validation I'd expect it to do actual full data parsing of often unpredictable fields
I used pydantic for my models whenever I encountered epsecially pesky APIs that were just horribly unstandardized and it helped a bit but far from what I'd expect
can you type hint the elements that you'll put into a queue.Queue?
Yes, it's generic
Queue[int]
(I think)
yup, seems to work, thanks!
Ok, thanks. It seems a good starting point, I will check the link you shared. About my experience with pydantic atm I can say, it is not always so elementary to use it when you have crazy dict to parse and validate (for example keys that are file names and you want a different class for every file). Documentation something is not enough and you need to search in the source code.
Anyway, one of my big "?" are the 500 open issue on github. A lot of stars, a big community of users, great sponsors, but I have some doubts (this is just my nature, I feel safe only when using modules in the core of Py
).. but I think atm using it just for runtime validation for example is ok. What do you think?
def num(val: str) -> float:
basenum = 20.
prefix = 0
power = 1024
return basenum * power**prefix
mypy strict mode doesn't like it lol
why are you doing power^0 in the first place?
but also try annotating prefix as Final
any ideas why mypy hates this?
def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
if arg is None:
return {'foo': 'bar'}
arg = cast(Dict[str, Any], arg)
if arg['baz'] == {'foo' : 'bar'}:
arg = arg['baz']
return arg
error: Incompatible return value type (got "Optional[Dict[str, Any]]", expected "Dict[str, Any]")
ok, I think the error is a little misleading here -- if I move the cast right before the return statement, mypy is fine with it
so probably mypy doesn't know that arg['baz'] is Dict[str. Any] , but I'm not sure why that would trigger this particular error message instead of mypy inferring that arg is being assigned to something with type Unknown
sounds like a bug to me, idk
huh, pyright has the same issue
Probably no type narrowing applied to arg[‘baz’]
So type checkers think it could be any value compatible with the original type of arg, which includes None
yeah, computer confirms:
from typing import *
def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
if arg is None:
return {'foo': 'bar'}
if arg['baz'] == {'foo': 'bar'}:
arg = cast(Dict[str, Any], arg['baz'])
return arg
I guess because Any is assignable to Optional[Dict[str, Any]]?
This code does not error for me: ```python
from typing import Any, Dict, Optional, cast
def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
if arg is None:
return {'foo': 'bar'}
new = cast(Dict[str, Any], arg)
if new['baz'] == {'foo' : 'bar'}:
new = new['baz']
return new
Anyways you should be able to do ```python
from typing import Any, Dict, Optional, cast
def returns_dict(arg: Optional[Dict[str, Any]]) -> Dict[str, Any]:
if arg is None:
return {'foo': 'bar'}
if arg['baz'] == {'foo' : 'bar'}:
return arg['baz']
return arg
Why do I have a feeling you're talking about Beartype
nope, that's not related
Argument of type "Literal[1]" cannot be assigned to parameter "__s" of type "slice" in function "__setitem__" "Literal[1]" is incompatible with "slice".
What? I'm using the update operator on a TypedDict object. I don't get the error
The Literal[1] is a list index and I am trying to update the 2nd element in the list, which is the TypedDict object
Hi, do you know if there is an "attr" equivalent of the "pydantic" Custom root types ? https://pydantic-docs.helpmanual.io/usage/models/#custom-root-types
Data validation and settings management using python 3.6 type hinting
ah ok..I've seen that cattr is similar to pydantic and has something like root
is this a valid way to annotate a generic subclassed generator
import typing as t
from collections.abc import Generator
T_yield = t.TypeVar("T_yield")
T_send = t.TypeVar("T_send")
T_return = t.TypeVar("T_return")
class WrapGenerator(Generator[T_yield, T_send, T_return]):
def __init__(self, gen: Generator[T_yield, T_send, T_return]):
self.gen = gen
def send(self, value: T_send) -> T_yield:
return self.gen.send(value)
def throw(self, *args, **kwargs) -> T_yield:
return self.gen.throw(*args, **kwargs)
I'm not sure what types the generic .throw() method is supposed to be
doesn't it just accept an exception object?
here's the one from the abc:
@overload
@abstractmethod
def throw(
self, __typ: Type[BaseException], __val: BaseException | object = ..., __tb: TracebackType | None = ...
) -> _T_co: ...
Could I just copy it? What about the ... defaults?
ah it's complicated

error: Signature of "throw" incompatible with supertype "Generator"
note: Superclass:
note: @overload
note: def throw(self, Type[BaseException], object = ..., Optional[TracebackType] = ...) -> T_yield
note: @overload
note: def throw(self, BaseException, None = ..., Optional[TracebackType] = ...) -> T_yield
note: Subclass:
note: def throw(self, Type[BaseException], Union[BaseException, object] = ..., Optional[TracebackType] = ...) -> T_yield
def foo(raisable=None):
if raisable:
raise raisable
What's the right way to type raisable? Exception | None?
Type[BaseException] | BaseException | None is the most exact and expansive annotation, but honestly you probably shouldn't be passing SystemExit to foo :p
Type[Exception] | Exception | None is best IMO. I added Type[Exception] since it means you don't have to create an instance when passing an exception foo(RuntimeError()) vs foo(RuntimeError)
not sure if in Python 3.10, you can use the built in type instead of typing.Type or not ...
3.10.5. So...I could use type[Exception] | Exception | None?
Oh, that worked, neat. Thank you 🙂
I'll look into this type generic, never used it before
it means the class object itself
Exception | None accepts None and also any class instance of the Exception class (or its subclasses)
Type[Exception] | None accepts None and the Exception class and any subclasses it may have
I'm actually using it like
def foo(raisable: type[Exception] | Exception | None=None):
try:
# ...
except StopIteration as exc:
raise raisable or ValueError("...") from exc
uh, the type annotation is its own separate thing from the default, you'll want to do this instead
def foo(raisable: type[Exception] | Exception | None = None):
try:
# ...
except StopIteration as exc:
raise raisable or ValueError("...") from exc
Yeah, I edited since 😅
ah, poor timing then on my side
iiuc, type[Exception] won't accept instances right? so Exception is still needed
yup.
Thank you btw!
You could do foo(raisable: Exception = ValueError("foo"))
if someone wants to raise ValueError, well, too bad, they'll have to call the class
Interesting. I think the type annotation wouldn't even be necessary in that form
pyright will infer the annotation, but I wouldn't rely on it
mypy will probably treat it as Any
raise raisable or ValueError(f"Expected ... but got {value!r}")
In this case, I can't use ValueError as a default parameter since it depends on local value
UtException: TypeAlias = type[Exception] | Exception | None
Value of type "Type[type]" is not indexable mypy mypy always lagging behind. type: ignore-ing it
import re
from collections.abc import Callable, Iterator
from functools import partial
class B:
re_compile = partial(re.compile, flags=re.UNICODE)
@staticmethod
def join_bisections() -> Callable[[str], Iterator[str]]:
bisection = B.re_compile(r"(\w+)-\s(\w+)") # This is the line that errors
return lambda p: map(partial(bisection.sub, r"\1\2"), p)
main.py:11: error: Access to generic instance variables via class is ambiguous
https://mypy-play.net/?mypy=latest&python=3.10&gist=55f5abe49dbe4115ef08f4ab8b9ab4ef
Why is this a problem?
Try this. ```py
import re
from typing import Callable, Iterator, Pattern, ClassVar
from functools import partial
class B:
re_compile: ClassVar[Callable[[str], Pattern]] = partial(re.compile, flags=re.UNICODE)
@staticmethod
def join_bisections() -> Callable[[str], Iterator[str]]:
bisection = B.re_compile(r"(\w+)-\s(\w+)")
return lambda p: map(partial(bisection.sub, r"\1\2"), p)
@brittle socket ?
https://pyright-playground.decorator-factory.su/
from collections.abc import Callable, Iterator
from typing import ClassVar
from functools import partial
import re
class B:
re_compile_unicode: ClassVar = partial(re.compile, flags=re.UNICODE)
@staticmethod
def join_bisections() -> Callable[[str], Iterator[str]]:
bisection = B.re_compile_unicode(r"(\w+)-\s(\w+)")
return lambda paragraph: map(
partial(bisection.sub, r"\1\2"), paragraph) # line that errors
Could not bind method "sub" because "Pattern[AnyStr@compile]" is not assignable to parameter "self"
TypeVar "AnyStr@Pattern" is invariant
Type "AnyStr@compile" cannot be assigned to type "str" (reportGeneralTypeIssues)
iiuc, the call to re.sub tries to assign something to a self parameter and pyright doesn't like it? 😕
The problem seems to be unrelated to the class
@manic wolf what your T should have been is Union[TypeA, TypeB, TypeC], but I can't quite put my finger on why your TypeVar wasn't allowed, perhaps because it wasn't related to a second use of T?
(context:
TypeA = NewType("TypeA", str)
TypeB = NewType("TypeB", str)
TypeC = NewType("TypeC", str)
T = TypeVar("T", TypeA, TypeB, TypeC)
def f(s: str) -> Union[T, Literal["error!"]]:
if s == "a":
return TypeA("hello A")
if s == "b":
return TypeB("hello B")
if s == "c":
return TypeC("hello C")
return "error!"
the docs for TypeVar don't seem so great to me :x
i think you're looking for a type alias not a typevar
T = Union[TypeA, TypeB, TypeC]
typevars are for generic functions and types
yes, but, what makes it wrong
I'm guessing that T can't be determined what it is statically, though perhaps there's a better way to phrase that
a union would be allowed to dynamically pick any of those, but a TypeVar would for a single call be a statically selected type... I think
but I don't think that's the whole reason :x
... or even necessarily correct
to simplify:
from typing import TypeVar
T = TypeVar("T", bound=int)
def f() -> T:
return 0
a typevar is meant to link the argument types and the return type, just one occurrence of T in the signature is not meaningful
but in haskell you get to do:
f :: a
f = undefined
though if I try to give a concrete value instead of undefined I run into trouble.
can't say it's any and then return an Int
though that is something python will let me do, with Any
oh well. I sort of kinda almost get it. But I wouldn't be able to explain it.
Any is special
it's not a traditional bottom type but a mix of top and bottom
Assignable to everything, as well as the reverse
the type variable is simply redundant, since it doesn't constrain any more than an alias does
in practice this is a type checker warning
If I use
@contextmanager
def temporary_accounts(
apih: ApiHandle,
obj_payload: List[Dict[str, Any]],
) -> Iterator[List[Account]]:
and I want to yield a list of the class Account
should it yield Iterator[List[Account]]
or just yield List[Account]
It should be a Generator actually, contextmanager requires one.
The type hint isn't correct at the moment, it's unresolved whether it's a good idea to correct that hint.
perhaps not the perfect channel to ask, but is there any library for that is similar to dataclasses, but allows me to have a static class variable?
I have a little event dispatching system, and event_name is defined inside each Event class:
@datacalss(frozen=True)
class Event:
event_name : str
@datacalss(frozen=True)
class MyAwesomeEvent(Event):
event_name = "my_awesome_event" # Shouldn't be initializable when constructing the class
event_specific_arg : int # This one should be though
Perhaps, my approach isn't the best either, so what do you guys think?
I looked at using attrs too.
i think this already works
It doesn't, as it requires event_name to be passed to __init__
I'm looking at attrs, and seems like this:
@attrs.frozen()
class Event:
event_name : str = attrs.field(default="", init=False)
@attrs.frozen()
class MyAwesomeEvent(Event):
event_name : str = attrs.field(default="my_awesome_event", init=False)
event_specific_arg : int # Not sure if i need attrs.field here?
may work?
Define it as a ClassVar[str], then it'll be ignored.
hm
from dataclasses import dataclass
from typing import ClassVar
@dataclass(frozen=True)
class Event:
event_name : ClassVar[str]
@dataclass(frozen=True)
class MyEvent(Event):
event_name : ClassVar[str] = "my_event"
arg : int
print(MyEvent(arg=1))
works
That's how you specify class attributes not defined on the instance themselves. Both decotators detect and skip processing them.
Thanks!
It also seems like I don't need to typehint in the derived class
which is nice
neat, just what I needed
How to type hint Class type self inside of a class.
@dataclasses.dataclass(frozen=True, eq=True)
class Point:
x: float
y: float
def to_tuple(self):
return self.x, self.y
@ancient thistle
def pointlist_points_to_tuples(pointlist: list[Point]):
return [point.to_tuple() for point in pointlist]
Like here in the static method, what to replace list[Point] with
Tried self and pycharm said that was wrong as well
typing_extensions.Self
just "Point" (as a string) would work too, but its not as inheritance friendly
Sweet that works thanks
shouldnt work in staticmethods
you should just use Point cause you cant actually check the current type inside a staticmethod
Hrm you think? Pycharm stopped complaining but just ran it and didn't realize typing_extensions was not' builtin.
Point causes NameError on runtime as well
do this
Ok yeah that works correctly without any bs
is there a way i can fix this linter error?
@dataclass
class Thing:
something: Optional[Something]
class SomeClass:
def do_something(self):
thing = self.__get_thing_somehow()
self.__validate_thing(thing)
# Getting an error here because linter thinks
# thing.something could be None, when it cannot
self.__handle_something(thing.something)
def __validate_thing(self, thing: Thing):
if not thing.something:
raise ValueError("Something is missing here.")
def __handle_something(self, something: Something):
...
have the function return thing.something as Something not Optional[Something]
and then just pass that
Need some help with this type alias. I'm trying to denote a callback that takes no arguments and either returns None or returns a coroutine that returns None ```py
Callback = Callable[[], Coroutine[None]|None]
class PubSub:
def init(self):
self.subscribers = defaultdict(set)
def subscribe(self, trigger: Hashable, callback: Callback):
self.subscribers[trigger].add(callback)
pleae @ me on reply!
probably Callable[[], None] | Callable[[], Coroutine[Any, Any, None]]
@analog orbit
Thank you, do you know what the 2 Any's are for in the Couroutine ?
the send and yield type. https://docs.python.org/3/library/typing.html#typing.Coroutine in practice you're likely going to want to keep them Any
Thanks I'll read up on that
this code:
from typing import Callable, Generic, TypeAlias, TypeVar
T = TypeVar("T")
A = TypeVar("A")
B = TypeVar("B")
class SealedA(Generic[A, B]):
def __init__(self, val: A) -> None:
self._inner = val
def show(self) -> str:
return f"SealedA: {str(self._inner)}"
class SealedB(Generic[A, B]):
def __init__(self, val: B) -> None:
self._inner = val
def show(self) -> str:
return f"SealedB: {str(self._inner)}"
PseudoSealed: TypeAlias = SealedA[A, B] | SealedB[A, B]
def a_not_none(func: Callable[[A], T]) -> Callable[[A | None], PseudoSealed[T, None]]:
return lambda val: SealedB(val) if val is None else SealedA(func(val))
@a_not_none
def identity(x: T) -> T:
return x
def main() -> None:
n: int | None = 5
not_none_a = identity(n)
print(not_none_a.show())
if __name__ == "__main__":
main()
has these mypy errors:
error: Argument 1 to "identity" has incompatible type "Optional[int]"; expected "Optional[T]"
that feels like a bug
or is there a way to type hint this to be considered valid by mypy?
If you want to specify the type of a parametrically polymorphic function, you need to use typing.Protocol
basically if you have a higher order function that takes a function with generic argument types, and returns a function that takes generic types too, applying it to a function that takes generic results in the genetics being treated as explicit types
E.g. ```py
class Iden(Protocol):
def call(self, x: T, /) -> T: ...
Hmmm actually
Yeah you'll have to make some callable protocols
I rewrote it to this using protocols
from functools import wraps
from typing import Generic, Protocol, TypeAlias, TypeVar
T = TypeVar("T")
A = TypeVar("A")
B = TypeVar("B")
CO = TypeVar("CO", covariant=True)
CT = TypeVar("CT", contravariant=True)
class SealedA(Generic[A, B]):
def __init__(self, val: A) -> None:
self._inner = val
def show(self) -> str:
return f"SealedA: {str(self._inner)}"
class SealedB(Generic[A, B]):
def __init__(self, val: B) -> None:
self._inner = val
def show(self) -> str:
return f"SealedB: {str(self._inner)}"
PseudoSealed: TypeAlias = SealedA[A, B] | SealedB[A, B]
class UnaryFunction(Protocol[CT, CO]):
def __call__(self, val: CT) -> CO:
...
def a_not_none(
func: UnaryFunction[A, T]
) -> UnaryFunction[A | None, PseudoSealed[T, None]]:
@wraps(func)
def inner(val: A | None) -> PseudoSealed[T, None]:
return SealedB(None) if val is None else SealedA(func(val))
return inner
class Iden:
def __call__(self, val: A) -> A:
return val
identity = a_not_none(Iden())
def main() -> None:
n: int | None = 5
not_none_a = identity(n)
print(not_none_a.show())
if __name__ == "__main__":
main()
and it still throws errors
error: Argument 1 to "__call__" of "UnaryFunction" has incompatible type "Optional[int]"; expected "Optional[A]"
Awaitable[None] is probably what you want instead — it's the protocol for "everything that can be awaited for None" rather than an explicit coroutine object
I have Callable[[T], object] and I want to call it as Pred[T]. What should I make it as? A generic?
!d typing.TypeAlias
typing.TypeAlias```
Special annotation for explicitly declaring a [type alias](https://docs.python.org/3/library/typing.html#type-aliases). For example:
```py
from typing import TypeAlias
Factors: TypeAlias = list[int]
``` See [**PEP 613**](https://www.python.org/dev/peps/pep-0613) for more details about explicit type aliases.
New in version 3.10.
Yeah but I wouldn't be able to pass a T to a TypeAlias
Pred: TypeAlias = Callable[[T], object]
How can I use this as Pred[T]?
like that
and if I call it as Pred[U], will it resolve to Callable[[U], object]?
try it and see
On phone, sec
ok then yes
from typing import TypeAlias, TypeVar
T = TypeVar('T')
Pred: TypeAlias = Callable[[T], object]
def f(pred1: Pred[T], pred2: Pred[U]) -> tuple[T, U]:
pass
So this should work
Weird...I can't intuit how Pred is subscriptable without being a generic, nor how it could take any type as a parameter and correctly resolve it as Callable's input
at runtime the subscription will go to CallableGenericAlias' getitem and that will perform the subscription if the generic alias has __parameters__ that can be substituted into
but i wouldnt worry about it that much
the annotation TypeAlias does nothing to the actual type of Pred
Alright, thank you 🙂
T = TypeVar('T')
UnaryPred: TypeAlias = Callable[[T], object]
def f(pred: UnaryPred[T]) -> Callable[[UnaryPred[T]], Callable[[Iterable[T]], int]]:
return lambda pred=pred: lambda iterable: g(pred, iterable)
Cannot infer type of lambda
https://mypy-play.net/?mypy=latest&python=3.10&gist=1770e8a2605d2c5a0afa3e053ea7fd2c
How can I fix this?
use named functions with annotations
Regardless of mypy, that's considered better practice?
very complicated lambdas tend to be frowned upon
I assume your real use case doesn't just return 42
It returns a function call which resolves to Iterator[T]
f here is actually just a currying function
Alright, named functions it is, thanks
Which I'm now realizing I'm getting wrong 🤦♂️, it just needs to be return lambda iterable: g(pred, iterable)
# pyright: reportPrivateImportUsage=false
I'm disabling this warning like this in my code, but does pyright have a flag or a config file where I can specify it instead of in my code?
pyrightconfig.json
I think there's something about it in the README/docs on github
Thanks 🙏
Ended up specifying it in a pyproject.toml instead 🙂
What does pyright mean by No configuration file found.? What is it looking for?
I'm assuming it's looking for pyrightconfig.json but I'm handling that directly in pyproject.toml's [tool.pyright]
Actually I'm moving this to #tools-and-devops
How good is pyright compared to mypy
I mean pyright is integrated into vscode but it doesn't seem to do shit
by default it has no type checking enabled
Mypy was such a pita, imagine how dumb it is to type hint every function and variable
How do i enable it
you need to set it to either basic or strict in the config
Yea i think i could do that on a workspace basis
you can do that on a workspace basis or globally
Globally would be better
you can also set the rules you want enforced in the pyrightconfig.json
I didn't know I could do this
Kinda bummer that vscode doesn't have it enabled by default
that's cause type hinting is optional in python, and a lot of people treat it as an extra opt in feature
It must be cpu heavy though, in vscode once I go above 1k loc in a single file, even highlighting gets messy and wrong
Yea mostly I don't feel the need to type hint but when I am dealing with complicated collections it kinda helps
i don't think so? it usually takes some time to setup when opening a project, but then it's caching the changes
i've used it for much larger projects and it's fine
i use it in strict mode and add type hints everywhere
wow ok what cpu do you have
if you were using mypy before, and used the default vscode mypy integration, then i think it was not really optimised
Vscode doesn't use mypy anymore
Its not included in pylance
It was replaced with pyright
mypy with its daemon is quite fast btw
i know, but mypy is available as a linter in vscode
Oh
HPC languages are statically typed and used in projects with millions of LOC. Typing was never really a performance hit.
Typing gets weird with decorators amd stuff
Why do you do it compulsorily when it can make the code look unnecessarily verbose
Decorators have nothing to do with typing
You didn't get the question there
was there a question?
Decorators accept callables and stuff, the typing for that is quite big
And it imo provides no added value
you can just not do it then
and even though i'd agree type hinting higher order functions can get a little unreadable fast in python, it does have a lot of value
Is it a good idea to import the annotations and use shorter syntax?
Like the | for union types
Type aliasing can help when types get complicated
I just wrote a UnaryPred: TypeAlias = Callable[[T], object] and been using it a lot
I got a library which I need to keep compatible with 3.6+, I am rewriting it so it will be a major change so I guess I can use annotations and make it 3.7+ compatible
I noticed that importing annotations breaks some stuff like inspect.signature, but I won't be using that it kinda seems hacky
it's doable, definitely, but when comparing it to something like haskell, the type annotations look a little verbose
flip :: (a -> b -> c) -> (b -> a -> c)
flip f = \x y -> f y x
def flip(func: Callable[[T1, T2], T3]) -> Callable[[T2, T1], T3]:
return lambda x, y: func(y, x)
Wow haskell looks nice
heh, got another one
What do you think abt this
there's a tool for modernising python i think
i don't actually remember what's in and not in python 3.7. I've hard switched to 3.10 as soon as it came out for structural pattern matching
3.7 has dataclasses, lazy type annotations
What does this error TypeVar "_MT" appears only once in generic function signature mean?
@slender timber
hmm
how do I use it for type hinting an arg which accepts a instance of a class or its subclass?
using the parent class by default means that subclasses are also acceptable
Oh I vaguely remember mypy once complaining about that
It felt wrong to me too
Hi, why is there a whole channel for type-hinting?
I wanted to ask:
Do you use both -> str and :return: result_blocks: list or choose one?
Yep
Although you might be able to get away with just the first if you use Sphinx
It should add things like that itself
Thank you
because it's a complicated topic that sort of stands apart from other topics, and there is also a lot of active development on it in the python ecosystem. before this channel existed, people didn't really know where to ask type hinting questions, and they ended up scattered across #internals-and-peps , #tools-and-devops , and #unit-testing .
I love it
personally i love it too, but it's still very clunky to use and can be severely limiting in some codebases
it's great for things like backend web servers and cli tools
it's a lot more complicated for libraries like pandas that have complicated, highly-polymorphic interfaces
the sooner python devs re-discover design-by-contract, the better imo
i've had mixed experiences so far w/ deal and icontract, because i think they made some questionable interface design choices. but i've come around to just putting assert statements for any properties that are difficult to test with static types. it's annoying that i lose integration w/ hypothesis, but it's so far been a good combination of "developer ux" and "runtime safety"
I meant that I love the existence of the channel. As for so much additional text for documentation, I'm still on the fence
meh, the basic use case of f(x: float, y: float) -> float | None: is great and definitely beats a docstring to me
right, I meant the docstring actually, as having too much text. My bad.
So you skip the docstring?
i usually try to write one as long as it's not totally redundant with the function name
e.g. i tend to skip it for smaller "internal-only" functions
guys
i was looking at godbolt and apparently type annotations don't just disappear and are actually of use for the runtime
so 2 questions
- what does it do with that info
- does this incur overhead that i might avoid by leaving types to be inferred where it's correct
because it's not really consistent for people who might not have inlay in their editors
- Libraries like
pydanticordataclass-factorycan inspect annotations to do some deserialization. The original rationale for function annotations (PEP3107) included the ability to introspect annotations at runtime. - Yes but it's pretty insignificant, only occuring at startup time (when you import the module)
deserialisation of what
i just google pydantic and this seems great
im gonna use it
Like, decoding a JSON object into a dataclass according to type annotations in the class
Similar stuff is implemented in typer but for command-line interfaces
OHHH
do you don't have to construct the primitive classes manually
Apparently pyright complains that I cannot subclass my enum class
class _EventEnumMeta(enum.EnumMeta):
def __contains__(cls, id: int):
try:
cls(id)
except ValueError:
return False
else:
return True
class EventEnum(int, enum.Enum, metaclass=_EventEnumMeta):
def __new__(cls, id: int, type: Optional[Type[Any]] = None):
obj = int.__new__(cls, id)
obj._value_ = id
setattr(obj, "type", type)
return obj
class EventID(EventEnum):
...
You don't need to subclass Enum.
I need to
Because I am using the EventEnum at quite a few places in separate enums
I need the __new__ method logic
isn't the enum class just this? ```py
class Enum(metaclass=EnumMeta):
pass
I am sorry, didn't I post the entire code above?
I didn't get your question.
I'm talking about the standard enum.
no?
its py class Enum(metaclass=EnumMeta): @_magic_enum_attr def name(self) -> str: ... @_magic_enum_attr def value(self) -> Any: ... _name_: str _value_: Any _ignore_: str | list[str] _order_: str __order__: str @classmethod def _missing_(cls, value: object) -> Any: ... @staticmethod def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> Any: ... # It's not true that `__new__` will accept any argument type, # so ideally we'd use `Any` to indicate that the argument type is inexpressible. # However, using `Any` causes too many false-positives for those using mypy's `--disallow-any-expr` # (see #7752, #2539, mypy/#5788), # and in practice using `object` here has the same effect as using `Any`. def __new__(cls: type[Self], value: object) -> Self: ... def __dir__(self) -> list[str]: ... def __format__(self, format_spec: str) -> str: ... def __reduce_ex__(self, proto: object) -> tuple[Any, ...]: ...
hm... then I have no idea what I'm talking about 😂
but why does pyright complain?
what exactly is the error you are getting?
Also the __contains__ method it is a perfectly working solution from SO, yet pyright complains of missing args
Enum class "EventEnum" is final and cannot be subclassed
strange, I only get the missing args warning on my side
I haven't used the final decorator either
Example subclass:
class ArrangementID(EventEnum):
New = (WORD + 35, U16Event)
Name = TEXT + 49
Playlist = (DATA + 25, PlaylistEvent)
maybe you have to apply the metaclass on each enum?
No, Enum has its own metaclass and IntEnum doesn't apply that metaclass
Infact, IntEnum is declared as class IntEnum(int, Enum)
What does mypy say about the class?
The metaclass is applied to EventEnum, it should get applied to all subclasses of EventEnum
Python enums are a really fucked up mess
enum is highly magical in its implementation, and type checkers need to special case it heavily as a result. Non-standard use of enums is unlikely to be understood by type checkers
Non standard how? This example is literally in Python docs
Maybe it's just one of those things where maybe a typechecker implies the enum is abstract if it has no atrributes
An example like this which inherits from bytes and Enum
what version of pyright are you using?
I am not getting this warning on the latest version
How do I check that?
what version of pylance is installed?
(the extension that adds pyright)
the latest probably
cant hurt to make sure
I am using a 3.10 venv btw and pyright has some venv config going on but that shouldn't be a problem
Its v2022.8.10
hmmm, strange
Anyways yet another messed up typechecker
How do I do an ignore for such cases
I don't wanna add # type: ignore everywhere
Also I don't understand this error, I mean TimeMarker is clearly a subclass of MultiEventModel.
Anyways how to make a List contravariant?
This has got something to do with invariants as well ig
no
no?
you can't really "make" things have variance, they just are
Well how can i return a list containing objects of different subclasses of the same parent class?
Make a TypeVar?
I guess then
why not just return list[Parent]?
I am bored of creating a thousand TypeVars
That doesn't work
return a Sequence, or make _collect_events also return list[Parent]
def _collect_events(self, enum_: Type[enum.IntEnum], type: Type[MultiEventModel]) -> List[MultiEventModel]: It does return parent objects
Basically it return a list of objects of a type specified by the type param
I see, that is a good case for a TypeVar then
meh
okay then
MEMT_co = TypeVar("MEMT_co", bound=MultiEventModel, contravariant=True) that would be I guess
Oh nice so the typevar has to appear in one of the args as well as he return type, fantastic
type: Type[MEMT_co]
If you're returning an object of type C with a __call__, should the return type be C or Callable? (The object is called right after)
Ig C
C is more correct neither is wrong though
That's what I'm doing but pylint and pyright complain about the object not being callable when I call with it
Working on isolating an example
Ah, here we go, getting something with mypy here:
from collections.abc import Callable
class B():
def __call__(self, text: str, check=True):
pass
class M(B):
def __call__(self, text: str, check=True) -> str:
return 'M'
class C(B):
def __call__(self, text: str, check=True) -> str:
return 'C'
class Other():
def __call__(
self, text: str, n: int, pred: Callable[[str], object] = lambda _: True
) -> str:
return "Other"
def factory(name: str) -> B | Other:
if name == "M":
return M()
elif name == "C":
return C()
elif name == "O":
return Other()
else:
raise NotImplementedError
s = factory("M")
s("text")
Missing positional argument "n" in call to "__call__" of "Other"
https://mypy-play.net/?mypy=latest&python=3.10&gist=50b7fd5b4dbefc97420c84d6a4cc3f60
How can I fix this?
Oh shit, that's a typo, my bad
(or if it makes sense, add a default argument to n in Other.__call__ )
yeah ok still i think the answer is overloads
@overload
def factory(name: Literal["M"]) -> M: ...
@overload
def factory(name: Literal["C"]) -> C: ...
@overload
def factory(name: Literal["O"]) -> Other: ...
@overload
def factory(name: Any) -> Never: ...
```not sure if name should be Any or object
probably Any as its an input type
Interesting...working on it
Which one would have the actual implementation? def factory(name: Any)?
And I can't find Never in typehint docs
personally id do (name: Literal["M", "C", "O"]) -> M | C | Other | Never cause then you get narrowing inside the function
its in 3.11 typing and the newest versions of typing_extensions
Never is effectively an alias for NoReturn
Didn't work in my actual code...working on translating what I did to an example
well feel free to send that
from typing import overload, Literal, Any
from typing_extensions import Never
from collections.abc import Callable
class B():
def __call__(self, text: str, check=True):
pass
class M(B):
def __call__(self, text: str, check=True) -> str:
return 'M'
class C(B):
def __call__(self, text: str, check=True) -> str:
return 'C'
class Other():
def __call__(
self, text: str, n: int, pred: Callable = lambda _: True
) -> str:
return "Other"
@overload
def factory(
name: Literal["Other"],
encoder: Any
) -> Other: ...
@overload
def factory(
name: Literal["C", "M"]
) -> B: ...
def factory(
name: Literal["C", "M", "Other"],
encoder: Any | None = None
) -> B | Other | Never:
if name == "M":
return M()
elif name == "C":
return C()
elif name == "O":
return Other()
else:
raise NotImplementedError
s = factory("M")
s("text")
Is this what you meant?
Sigh, yeah it does in the example, but not in my code 🤦♂️
And it's a bit complex to paste here
Can I show you with a screenshare?
!paste
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.
theres fine
well id hazard a guess and say that the parameter is missing
but since FrenchTextRank isnt actually included here i cant say for sure
It executes fine so it can't be the parameter 😕 I'm working on reducing the example to what's necessary
I'll work on factoring out the conditional
Nope, didn't work. Sigh
Ended up only returning B from that factory function, and handling Other elsewhere. Now pyright is content
Pylint is not, but for some reason re-annotating the variable at the call-site (with the type already declared as the function's return) silences it
Wow how awesome the exact channel for this exists here 🙂
I'm trying to create some really general base class that can accept a standardized API SDK class that we have as an initialization param. This then gets stored in a class attribute; however, one thing that's really painful is not being able to (to my knowledge) declare the type annotation of which SDK class was used for initialization.
This is primarily to facilitate auto-completion of these SDK methods. It's quite painful not to have access to all methods via ".".
Any ideas how to dynamically assign the class type on initialization?
Do you know the set of possible SDK types in advance?
@brittle socket the number is quite large... At the moment there are 6 but this will continue to grow. It would be a little annoying but possible to list them out but then would the editor be able to identify which one it actually is when recommending methods/attributes? Thanks for the help
Generally what I'm aiming for is this (pseudocode). Seems pretty straightforward but I guess the biggest issue is that it doesn't know the type until runtime which doesn't help Pycharm properly recommend attributes/methods.
class Foo:
def __init__(self, sdk):
self.sdk: type(sdk) = sdk
Foo.sdk.{Pycharm recommends attributes/methods properly}
is there some least common denominator for all the sdk types
because if there is you can make a generic with that as its bound
you just want the least constrained unique thing that they all have in common
@twin lantern that's interesting. Would having some base class that they all inherit help? They each have a lot of custom methods though so I assume those wouldn't be pulled in. These custom methods are typically what we want to call. There's some repeatable stuff under the hood that could be inherited but usually these are just tools that we use to build out the later methods we want to call. Not sure if I answerd your question fully
you can make a marker BaseSdk class or something like that
and have sdk = TypeVar["SDK", bound=BaseSdk] or something
idk what the string is for in that ill google
there's also a thing called Generic
in typing
if someone could provide insight
because this is quite confusing and messy
Interesting, I'll try to look into this pattern a bit more. It feels kind of crazy that this isn't easier 😛
nails and plywood my friend
nails and plywood
even im confused
I don't know if python typehinting has something akin to defining a type by the methods it must have. That feels like what you need here
!d typing.Protocol ?
class typing.Protocol(Generic)```
Base class for protocol classes. Protocol classes are defined like this:
```py
class Proto(Protocol):
def meth(self) -> int:
...
``` Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing), for example...
Nice
@sacred flower
class SDK(Protocol):
# list signatures of methods all SDKs have in common
class Foo:
def __init__(self, sdk: SDK):
self.sdk = sdk
You'll be looking at some pattern similar to
from typing import *
class Sdk: ...
T = TypeVar("T", bound=Sdk) # the bound is optional but helpful
class Class(Generic[T]):
sdk: type[T]
def initialize(self, sdk: type[T]):
self.sdk = sdk
class MySdk(Sdk): ...
thing: Class[MySdk] = Class()
thing.initialize(MySdk())
keep in mind only instance attributes work easily like that, there's a strange limitation with ClassVar that doesn't allow generic types as class attributes
This way you get can exact autocomplete for your SDK-specific methods, which I assume was a goal 😄
Interesting
It assumes the SDK type is known at instantiation. If that is the case, I think it's the most correct solution
Thanks a ton guys, sorry I'm a bit slow to understand all of this but I will be playing with it until I do. Really appreciate the help, this is quite a new area of Python for me 🙂
Having some troubles with Pycharm recognizing "credentials" as an attribute of sdk. It's printing them correctly, just Pycharm being a pain. Going to try with the Protocol approach.
Maybe I misunderstood and the approach screenshotted above will only recognize attributes of the BaseSDK class in the editor, not the child SDK?
pycharm's type checker might be unwilling or unable to infer that self.credentials means that there will always be a credentials attribute. also you didn't give it a type in __init__ so it would be Any anyway.
try:
class MySdk(BaseSdk):
credentials: YourTypeHere
def __init__(self, credentials):
self.credentials = credentials
or:
class MySdk(BaseSdk):
def __init__(self, credentials: YourTypeHere):
self.credentials = credentials
someone pls explain the difference between generic and TypeVar
generics are types that have parameters. a list is a generic type because lists can contain different types of data. a typevar is a placeholder in type expressions. typevars are often used in conjunction with generics.
A = TypeVar('A')
class MyThing:
x: A
def __init__(self, x: A):
self.x = x
def get_x(self) -> A:
return self.x
in this example, you don't know what A specifically is, but you know that A must always be the same type wherever it appears in this class definition
(note that "getters" like this are considered bad style in python, but it makes for an easy demonstration)
ok and would generics be able to do the same thing
or no
because like
i see classes inherit from generic
what does that do
inheriting from Generic makes the class a generic
you can make MyThing a generic
A = TypeVar('A')
class MyThing(Generic[A]):
x: A
def __init__(self, x: A):
self.x = x
def get_x(self) -> A:
return self.x
now MyThing is a generic class. e.g. you can write int_thing: MyThing[int], and inside int_thing the A placeholder will always be given the specific value int.
so like list would inherit from it
yes, at least conceptually
it's a limitation in python, the string must be the same as the variable you assign it to
e.g. if you did B = TypeVar('A') you'd get an error when running the type checker
i agree. other programming languages with compilers and type systems that are built into the language don't have limitations like this. it's just something we have to live with in python.
im only using it temporarily anyway so i can go back to the lands of extensive static analysis but i want to bring as much as i can with me at least for now
i think it's good to explore using it in order to see how much of it you're willing to stomach
some codebases are more amenable to thorough strict typing than others. it depends a lot on what libraries you're using and who your intended end user is.
ye
also does pydantic do any other checking than just making dataclasses or whatever
because that's the most.msrketed thing
because ideally it runtime checks everything
if im having the overhead im going all the way
they aren't actually dataclasses, but it's the same idea. it also does runtime type validation and provides some tools for working with pydantic models
This is due to the strange opinion that Foo[Bar] is the same as Foo at runtime
I only found this out recently
!e
from typing import Generic, TypeVar
T = TypeVar('T')
class X(Generic[T]): ...
x = X[int]()
print(x.__dict__)
x = X()
print(x.__dict__)
@tranquil turtle :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | {'__orig_class__': __main__.X[int]}
002 | {}
X and X[int] isnt the same at runtime
Yep
https://paste.pythondiscord.com/ilelijezex
i wrote pure-python implementation of builtin GenericAlias class
it is very close to code of GenericAlias written in C
Is there a tool that automatically migrates all the List and Dict type annotations to lowercase and deletes the import?
fix's time to shine
I don't know about automatic conversion, but there's a flake8 plugin to detect them
!pypi flake8-pep585
There's also pyupgrade which actually does the updates but it doesn't catch all of them
I'll just have to make a regular expression to do it.
that's a big 🍑 regex 👀
hey, so I have a recursive json type, for some reason I am getting a error from pyright when passing a variable, but not a literal: ```py
JSON_DATA = str | int | dict[str, "JSON_DATA"]
def foo(data: JSON_DATA) -> None: ...
bar = {"a": "b", "c": 1}
foo(bar) # this gives an error
foo({"a": "b", "c": 1}) # but not this?
/home/vivax/coding/jerde/minimal.py:6:5 - error: Argument of type "dict[str, int | str]" cannot be assigned to parameter "data" of type "JSON_DATA" in function "foo"
Type "dict[str, int | str]" cannot be assigned to type "JSON_DATA"
"dict[str, int | str]" is incompatible with "str"
"dict[str, int | str]" is incompatible with "int"
TypeVar "_VT@dict" is invariant
Type "int | str" cannot be assigned to type "JSON_DATA"
Type "int" cannot be assigned to type "JSON_DATA"
"int" is incompatible with "str" (reportGeneralTypeIssues)
it only happens with strict mode on, so I am assuming that `bar` is being narrowed to some other type than with "basic" mode.
but I dont understand where the issue is?
I am feeling like this a pyright bug? (I am on the latest version, `1.1.265`)
I think the issue is that variance is just screwing you
but the line
Type "int" cannot be assigned to type "JSON_DATA"
is clearly wrong, right?
or ...
wait I see
okay yhe that does make sense when I think about it
You need to use Mapping I'm pretty sure
I think pyright can do joining or meeting (I forget which) in the direct call but not in the variable
that makes sense
Mypy does the same for some things
I realized it wouldn't let me with dict because according to the type hint I could in theory add a value that was a dict in the foo function.
which would make the (inferred) type hint wrong in the outer scope
@trim tangle pyright playgrounds generate permalink feature is 502ing
Well that sucks
I'll take a look in a few hours. I hope you don't base anything life-critical on it
maybe i do 
i was just using it for a pyright issue, ill generate the link myself
oh wait it doesnt work either way
hi, I was wondering what I should do in these cases
from typing import Optional, List
def foo(a: Optional[List[str]] = None) -> None:
if a == None:
pass
if isinstance(a, list):
for i in a:
pass
# mypy error:
# Value of type Optional[List[str]] is not indexable, use SupportIndex
update your mypy cause that should work
oh wait thats not ever gonna produce that error
K, maybe I'll repost my code later, I used only one example
@oblique urchin is there a forum or something similar for mypyc? I know of the mypy gitter, but it seems rather dead 😦
def write(self, string: Optional[str] = None, delay: float = 0.05):
for character in string:
stdout.write(character)
stdout.flush()
sleep(delay)
print("")
error: Item "None" of "Optional[str]" has no attribute "__iter__" (not iterable)
You need to do what you did before where you check if string is not None before iterating over it
yep the code won't work with None
oh
ok
thx y'all
tbh id just remove the default of None
Search for CS50 lecture about regexp on YouTube
there is a secret mypy core dev discord but it's not very active
i know this is really late but the issue here is most likely that you need to compare None with is None or is not None instead of ==
actually if you pass, it won't narrow
actually you have a isinstance there.... confusing
generally you need to ensure that the value you want to narrow is the only thing able to reach that branch. branches can be stopped with a exception, return or continue (in loops)
it's just an example, obviously the code will not have pass after the conditions
btw thx for your tips
Okay
For a class-based decorator
(As opposed to a function bases decorator)
from typing import Callable
from typing import Generic
from typing import ParamSpec
from typing import TypeVar
T_Params = ParamSpec('T_Params')
T_Return = TypeVar('T_Return')
class Wrapper(Generic[T_Params, T_Return]):
def __init__(self, wrapped: Callable[T_Params, T_Return]) -> None:
self.function = wrapped
def __call__(self, *args: T_Params.args, **kwargs: T_Params.kwargs) -> T_Return:
return self.function(*args, **kwargs)
Is this how I'm supposed to do it?
Looks like it
you could do while importing
from typing import (
Callable,
Generic,
ParamSpec,
TypeVar
)
or
from typing import Callable, Generic, ParamSpec, TypeVar
😕
Not meaning to be rude, its just
I super duper don't care, and I don't really have patience for pointless adherence standard
Pointful adherence to standard I do care about. But whether or not I import my types one at a time isn't something I lose sleep over
ok
Sorry, I was kinda rude. Python has been getting on my nerves a bit lately
no problem
I haven’t done too much mypyc stuff, but do let me know if I can help with something
if there anyway to type hint a class from a different file without importing the class?
Yes, you can use if typing.TYPE_CHECKING: to add code only the type checker reads to do the import.
If you've got an import cycle that can help. However, you might want to consider if you can rearrange your code to make it less cyclical, it might improve the conceptual layout.
you can also write your type annotations in a separate "type stub" file
^ that makes it work with py<3.7
Templates file in python? 
more like an interface file
Isn't it what templates are? Haven't done C in a decade
templates are kinda like bad generics
Maybe you mean header files?
I just remember there was such thing as templates that were kind of like an interface
Thanks for the offer. I was actually looking for a space that isn't SO regular folks can use to get help with mypyc. Got someone who read my mypyc series asking me whether I knew of any spaces. https://github.com/ichard26/ichard26.github.io/discussions/4
I posted a type hinting question in #help-avocado if anyone feels like taking a look
how should functions that either do nothing or raise an error be type hinted?
talking about something like this ```py
def check_os():
if platform.architecture()[0] != '64bit':
raise OSError('This script is only compatible with 64bit systems.')
if platform.system() not in ('Linux', 'Windows'):
raise OSError(f'Unsupported OS "{platform.system()}"')
-> None. Raising errors is not part of the type system
thanks!
pylint is emitting unpacking-non-sequence for an assignment from a function typehinted as returning a tuple. 😕
def factory(name: str) -> tuple[A | B, Callable]:
if some_cond:
return A(), lambda x: x*2
else:
return B(), lambda x: x*2
raise NotImplementedError
a, b = factory(name) # pylint unpacking-non-sequence
There is no pylint playground to replicate it unfortunately
your function doesn't actually return a tuple
My bad, just corrected it
looks like a bug if thats still the case
Meh, I'll just disable it on that line then
Hi, I need help with std libraries. I'm using two libs: functolls and typing. I'm trying to make my code look cleaner with singledispatchmethod and got this problem:
pipenv run dev
Traceback (most recent call last):
File "/Users/mark/Git/diam-core/app.py", line 3, in <module>
from diamcore import *
File "/Users/mark/Git/diam-core/diamcore/__init__.py", line 5, in <module>
from .datamodels import *
File "/Users/mark/Git/diam-core/diamcore/datamodels.py", line 63, in <module>
class Integer(BaseDataModel):
File "/Users/mark/Git/diam-core/diamcore/datamodels.py", line 106, in Integer
def _(self, v: IntegerType):
File "/opt/homebrew/Cellar/python@3.9/3.9.13_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/functools.py", line 933, in register
return self.dispatcher.register(cls, func=method)
File "/opt/homebrew/Cellar/python@3.9/3.9.13_2/Frameworks/Python.framework/Versions/3.9/lib/python3.9/functools.py", line 872, in register
raise TypeError(
TypeError: Invalid annotation for 'v'. ~IntegerType is not a class.
And here is the code:
from dataclasses import dataclass as struct
from dataclasses import field
from functools import singledispatchmethod
from typing import Union, TypeVar
IntegerType = TypeVar('IntegerType', bound='Integer')
@struct
class BaseDataModel:
def __post_init__(self):
...
class Integer(BaseDataModel):
value: Union[int, float]
@singledispatchmethod
def __init__(self, v):
raise TypeError(f'Incorrect provided type! Getted {type(v)} is acceptable here!')
@__init__.register
def _(self, v: int):
self.value = v
@__init__.register
def _(self, v: float):
self.value = v
@__init__.register
def _(self, v: bool):
self.value = int(v)
@__init__.register
def _(self, v: str):
self.value = float(v)
@__init__.register
def _(self, v: IntegerType):
self.value = v.value
you shouldnt be using Integer here as a type var bound
you need to use Integer just in quotes
Just left the IntegerType = TypeVar('IntegerType'?
Enabling the __future__.annotations doesn't helps