#type-hinting
1 messages · Page 8 of 1
The example that comes to mine is typing.AnyStr, which is ```py
AnyStr = TypeVar("AnyStr", str, bytes)
this is correct, and i never quite understood the purpose of this design
maybe it's a variance/covariance thing again
no, it's to help better model python semantics. (you actually see a similar desire come up with literal types every now and then)
here, from pep 484:
wait.. i thought that MyStr would explicitly be disallowed by the constraint
i understand the distinction in that paragraph though, and i agree that it's useful
"you can give me any subtype of str, but i will only ever treat it as a str"
no kidding.. i've totally misunderstood constraints for years
from typing import TypeVar
AnyStr = TypeVar('AnyStr', str, bytes)
def foo(s: AnyStr) -> AnyStr:
if isinstance(s, str):
return s.replace('x', 'y')
else:
return s.replace(b'x', b'y')
class MyStr(str):
pass
reveal_type( foo(MyStr('hello')) )
same with the variance/covariance/invariance of containers thing
well, you can always do ```py
my: MyStr = MyStr("hello")
s: str = my
foo(s)
Why is types and typing separated?
Because one is for concrete types and the other is more abstract special forms
ah, that's probably what i had tried in the past, and got confused about
thanks for clarifying!
Hey guys, why are my overloads not working correctly? It should be PartialUser | None right?
Show the type of user_id
Oh wait
int
Why do you think it should be PartialUser | None?
because full and affirm are false by default
Overloads don't really "look" at the function definition
Remove the defaults (...) from the overloads. Then remove the False parameters completely from overloads
Can you show the implementation btw?
like this?
so um
yes
you can also remove in each overload the parameters which are False
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.
is this something i need to deal with that stuff like mypy would care about?
or is it pycharm being annoying?
It's saying it wants True or False, but it got the literal type bool instead.
mypy would also care about that
ohhhh
maybe you did x = bool instead of x: bool
I have parameter that I intend to be passed as str or None, is it still valid to use : str on it? Or maybe I should just use an empty string in that case.
: str | None
: Union[str, None]
Can I type a callback with optional parameters without a Protocol?
no
why do you want a callback with optional parameters though?
can't it just accept Thing | None?
No, (x: int = ...) -> str is an intersection of () -> str and (int) -> str
not a union
because if you have a Callable[[int], str], it's assignable to Callable[[], str] | Callable[[int], str]
A custom callable protocol is how it goes for most nontrivial function annotations
Is there a way to make recursive types in Python?
For instance, say I have a dictionary of int → dictionaries of dictionaries… and so on.
I can’t do:
rec_dict_type: TypeAlias = dict[int, rec_dict_type]
because I get an error when running the code.
I also tried this:
def get_rec_dict_type():
return dict[int, get_rec_dict_type()]
rec_dict_type: TypeAlias = get_rect_dict_type()
but it doesn’t work.
Is there any way I can type this other than dict[int, Any]?
use strings
either rec_dict_type: TypeAlias = "dict[int, rec_dict_type]" or rec_dict_type: TypeAlias = dict[int, "rec_dict_type"]
oh cool
I tried it and I get an error: Cannot resolve name "rec_dict_type" (possible cyclic definition)
I used the code rec_dict_type: TypeAlias = dict[int, 'rec_dict_type']
is this mypy?
Yes
youll need the latest version to use recursive typehints
I am using
mypy 0.981 (compiled: yes)
oh it appears the latest version is 0.991
I will update
I am using my distro’s packaged version though so it will take a while
Thank you, though
tuple[()] from https://mypy.readthedocs.io/en/stable/builtin_types.html
thank you
I have a decorator:
def supports_slice(func: Callable[[ModelCollection[MT_co], str | int | slice], MT_co]):
However I cannot use it here:
class Arrangements(EventModel, ModelCollection[Arrangement]):
@supports_slice
def __getitem__(self, i: int | str | slice):
as this is the error:
Argument of type "(self: Self@Arrangements, i: int | str | slice) -> Arrangement" cannot be assigned to parameter "func" of type "(ModelCollection[MT_co@supports_slice], str | int | slice) -> MT_co@supports_slice" in function "supports_slice"
Type "(self: Self@Arrangements, i: int | str | slice) -> Arrangement" cannot be assigned to type "(ModelCollection[MT_co@supports_slice], str | int | slice) -> MT_co@supports_slice"
Parameter 1: type "ModelCollection[MT_co@supports_slice]" cannot be assigned to type "Self@Arrangements"
"ModelCollection[Arrangement]" is incompatible with "Arrangements"
ModelCollection is just a protocol
@runtime_checkable
class ModelCollection( # pylint: disable=abstract-method
Iterable[MT_co], Protocol[MT_co]
):
@overload
def __getitem__(self, i: int) -> MT_co: ...
@overload
def __getitem__(self, i: str) -> MT_co: ...
@overload
def __getitem__(self, i: slice) -> Sequence[MT_co]: ...
youre getting messed up by the contravariance of Callable's parameter types
i am confused between co- and contra- too
co- appears more useable, contra- almost always errors
so.. what should I do?
type: ignore, theres nothing you can do
¯_(ツ)_/¯
it might not be type safe 🤔
but honestly idk it seems to be a pretty big problem with typing decorators
reveal_type is working correctly even after # type: ignore next to where I get errors
return num*2
print(double("test"))
print(double(2))
How do I do that it has to return int and have the parameter as int
you already have 
@soft matrix I am running the code with a string and it executes instead of giving an error
well yeah python doesnt enforce types at runtime
but if I want to enforce, that was my question
i know i could do an if statement to check but I want to know if its possible if type hinting
no python type hints are only checked statically
use some runtime type-checker. For example, beartype:
from beartype import beartype
@beartype
def double(num: int) -> int:
return num*2
print(double(2)) # ok
print(double("test")) # BOOM!
thanks
Generally that's not possible. Here's my explanation:
https://decorator-factory.github.io/typing-tips/faq/runtime-checking/
You can do it for simple types like int or str | None, but with more complicated types it just breaks down. Or becomes very expensive
The point of type annotations is that you use a tool like pyright or mypy to find errors without even running your program
thanks
how do you typehint classmethods that return the same class?
!d typing.Self
typing.Self```
Special type to represent the current enclosed class. For example:
```py
from typing import Self
class Foo:
def return_self(self) -> Self:
...
return self
``` This annotation is semantically equivalent to the following, albeit in a more succinct fashion...
the typehint is still the same
Self doesn't mean it returns the same object, it means it returns the same type
class Point:
def __init__(self, x: float, y: float) -> None:
self._x = x
self._y = y
@classmethod
def from_polar(cls, angle: float, distance: int) -> Self:
...
return cls(x=some_x, y=some_y)
I often think twice about making a classmethod though, because __init__ usually changes in child classes
The one place where LSP violations are considered okay 
@craggy quest
someone here (fix error maybe?) recently straightened me about about LSP. instances need to be substitutable, but the classes themselves and/or their constructors need not be
so it's not at all an LSP violation to have alternate constructors and init/new methods that are incompatible with the parent class, although practically i think a lot of code tends to assume that they are compatible
subtyping of types is a mess anyway
well, it's kinda vague
I think it's just a matter of convention
Is it possible to get mypy to understand implications?
suppose I have this code
from typing import Optional
def foo(num: int, val: Optional[str] = None) -> list[str]:
assert num != 0 or val is not None
if num == 0:
return []
else:
return [val] * num
After the assert inside the else, val is definitely not None
How can I communicate this to mypy?
Why is val even allowed to be None here?
I mean it should understand asserting type guards like this
But this a strange function
This just looks like a mypy bug
should I report it?
assert gets skipped in optimized mode
If I change that or to and, it works in pylance
with or, it doesn't
actually, you know what? the type checker is right. What if num!=0?. Then val can be None and that's valid.
I think you meant to do the opposite, assert num == 0 or val is not None
def foo(num: int, val: Optional[str] = None) -> list[str]:
assert num == 0 or val is not None
if num == 0:
return []
else:
return [val] * num
doesn't work for me in pylance though
sadly here it's just not smart enough to understand that num == 0 or val is not None (after the assert) and not(num==0) (inside the else) together imply val is not None.
Yeah
it's because, whenever num != 0 the assertion is true, so val is None can be either true or false and that assertion doesn't actually narrow the type at all
mypy isn't smart enough to connect that to the num == 0 case
what about rewriting this way?
from typing import Optional
def foo(num: int, val: Optional[str] = None) -> list[str]:
if num == 0:
return []
else:
assert val is not None
return [val] * num
dc: typing.Dict[
Literal[4004, 4010, 4011, 4012, 4013, 4014], str] = {
4004: "reason",
4011: "reason",
...: ...
}
is there any other way to typehints the keys of a dict to a literal value and all the values to str?
Why not just dict[int, str]?
they're opcodes coming from discord, wouldn't it be more correct to type them as Literal?
Literal is useful when there is a human user actually typing in the values, like "r+" as the file mode.
If you're receiving untrusted input from a remote service, there is always a possibility that the value is complete nonsense (or maybe they just added a new error that your code doesn't handle)
ok
Is there a way I could describe what does look PutObjectRequest like?
def put_object(request: PutObjectRequest):
...
# What vscode shows when you hover `put_object`: (function) put_object: (request: PutObjectRequest) -> None
# I want to describe what `request` dict would ask or maybe use `**kwargs` but that gives me a `Dict[str, PutObjectRequest]` instead of just `PutObjectRequest`
And this is PutObjectRequest:
class PutObjectRequest(TypedDict):
ACL: ACLType
Body: Union[bytes, BinaryIO]
Bucket: str
CacheControl: str
ContentDisposition: str
# And more
if the signature was **kwargs would you want it to reveal (ACL: ACLTYPE, Body: bytes | BinaryIO, ...)?
from typing import TypeVar
from collections.abc import Iterable
T = TypeVar('T')
U = TypeVar('U')
def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T], tuple[U]]:
return tuple(zip(*zipped))
error: Incompatible return value type (got "Tuple[Tuple[Any, ...], ...]", expected "Tuple[Tuple[T], Tuple[U]]") [return-value]
https://mypy-play.net/?mypy=latest&python=3.11&gist=402604fe2b8c2d8b6e300d66021e82ed
How can I fix this? 😕
This is the most relaxed I could get it without mypy complaining
def unzip2(zipped: Iterable[tuple]) -> tuple:
return tuple(zip(*zipped))
# type: ignore
actually the correct return type would be tuple[tuple[T, ...], tuple[U, ...]]
wait... your return type doesn't make sense
!e
def unzip(zipped):
return tuple(zip(*zipped))
print(unzip([(1, "a"), (2, "b"), (3, "c"), (4, "d"), (5, "e")]))
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
((1, 2, 3, 4, 5), ('a', 'b', 'c', 'd', 'e'))
oh I see now
yeah it should be tuple[tuple[T, ...], tuple[U, ...]]
alternatively you could do:
def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T, ...], tuple[U, ...]]:
ts, us = zip(*zipped)
return ts, us
``` although this isn't any more "type safe"
But I like the ts, us = zip(*zipped) line. It runtime-enforces that exactly 2 tuples are expected
Mypy doesn't complain, pyright does :/
Expression of type "tuple[tuple[T@unzip2 | U@unzip2], tuple[T@unzip2 | U@unzip2]]" cannot be assigned to return type "tuple[tuple[T@unzip2, ...], tuple[U@unzip2, ...]]"
Tuple entry 1 is incorrect type
Tuple entry 1 is incorrect type
Type "T@unzip2 | U@unzip2" cannot be assigned to type "T@unzip2"
Man, linters still have a lot of work to do
Indeed
!pep 646
(it needs the map type)
Just put #type:ignore in function. It signature is good and calls can be typechecked
Yeah I think I'll just go with
def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T, ...], tuple[U, ...]]:
ts, us = zip(*zipped)
return ts, us # pyright: ignore
Since it's just pyright that complains
Why does a variadic make more sense for mypy though? Shouldn't (1, 2, 3) be just tuple[T] rather than tuple[T, ...]?
Oh wait, no it really is tuple[T, ...]
I bet mypy infers ts and us as Any so it doesn't complain
It doesn't complain here either 🤷♂️
def unzip2(zipped: Iterable[tuple[T, U]]) -> tuple[tuple[T, ...], tuple[U, ...]]:
ts: tuple[T, ...]
us: tuple[U, ...]
ts, us = zip(*zipped)
return ts, us
https://mypy-play.net/?mypy=latest&python=3.11&gist=45dd7211e0be70d70abb1b39d932c364
yes
I tried with
def put_object(**kwargs: Unpack[PutObjectResponse]):
...
that should work
ok ty
def slide_2(seq: list[T] | tuple[T]) -> Iterator[tuple[T, T]]:
return zip(seq[ :-1], seq[1: ])
How can I properly type seq here? I feel like list and tuple should fall under some generalization
Or rather a type that expresses anything indexable
Sequence[T]?
yes
Just stumbled on it 🙂
also, as before, a tuple with any number of items is tuple[T, ...], not tuple[T]
tuple[int] is the type of e.g. (42,)
Oh but this is specifically a tuple of exactly 2 items
(1, 2, 3, 4) -> ((1,2), (2,3), (3,4))
yeah I meant the tuple[T]
Oh the input param right, that should be just Sequence[T] anyhow
yeah
def slide_2(seq: Sequence[T]) -> Iterator[tuple[T, T]]:
return zip(seq[ :-1], seq[1: ])
also, I suppose you could make it accept Iterable
I can't slice/step into any iterables though
itertools.pairwise(iterable)```
Return successive overlapping pairs taken from the input *iterable*.
The number of 2-tuples in the output iterator will be one fewer than the number of inputs. It will be empty if the input iterable has fewer than two values.
Roughly equivalent to:
```py
def pairwise(iterable):
# pairwise('ABCDEFG') --> AB BC CD DE EF FG
a, b = tee(iterable)
next(b, None)
return zip(a, b)
```...
Oh, it is
I hate how overloaded the term pairwise is. Makes it hard to search for what you want
In scipy pairwise means all pairwise combinations (i.e. itertools.combinations(seq, 2))
Then you have contexts where pairwise means zip(it1, it2)
Anyhow, yep that'd be what I'm looking for thank you
why are the type hints for ctypes so fucked
most of the really useful type hints are private
aren't those internals?
Does mypy having version 0.991 mean it’s close to a 1.0 release, or will there simply be an extra digit?
the next release will be 1.0
cool!
mypy v0.9999 
fun coincidence (star count)
only pyrightt
how do I typehint a funciton like this: py def moveable(self) -> ?: yield ... yield ...
Either as a Generator or an Iterator
Generator is normally a better idea
I annotate my generators that don't have a send or return type as iterators
which is like 99% of them
one day soon hopefully theyll (optionally) have the same number of arguments
so it should be a no brainer to use Generator
why? callers probably only want to iterate over it, the fact it's a generator is an implementation detail
what would the other arguments do?
Generator[YieldType, SendType, ReturnType] is the current structure
Right, and with PEP 696 we'd default the last two to None
For stuff like contextlib.contextmanager?
That and you then get more accurate types which in my book is always good
sure, that's a case where you really do need a generator
but if I write a library that exposes some iterator that happens to be implemented as a generator, I wouldn't want my users to rely on the fact it's a generator
so I might annotate the generator as returning -> Iterator
thats fair
hello. i'm not sure how to type this correctly. essentially, i have a set of classes which each have a "kind." i care nothing about what this "kind" is, only that it's consistent between these classes. a heavily minimized example would be something like:
class GreenNode:
kind: T
tokens: list[GreenToken]
class GreenToken:
kind: T # this must be the same as the other T, specifically if they're related. if they're unrelated, it can once again be whatever
i think this has to do with Generic, but i'm not 100% sure. maybe TypeVar?
from typing import TypeVar, Generic
T = TypeVar("T")
class GreenToken(Generic[T]):
kind: T
class GreenNode(Generic[T]):
kind: T
tokens: list[GreenToken[T]]
yeah use generics
ahhhh, it's both together. that's what i was missing, thank you.
What is some good resource to really learn dataclass_transform?
All there literally is a PEP
Which is good but ...
the PEP is all there currently exists that i'm aware of. (when mypy adds support, we'll add some mypy docs, which are usually quite good)
I think I read somewhere that wemake-python-styleguide was considering making a version of their plugin that is just their custom linters minus all the extra stuff. Does anyone know if there's a github issue or something I can watch for that?
where do people here draw the line between type safety and actual usability for public typehints?
im currently struggling to decide whether to make a lot of things a union between a fully resolved type and the partial version of that type when fetching failed
all pets want to live... where do you draw the line?
i think 99% of the time its gonna be a fully resolved version of the type but idk if that percentage is good enough for me to not care
a good thing about typing is that it forces you to think about the 1% case when you write the code, not when it happens in production and the world breaks
but it makes the developer experience so bad 😢
although maybe the negativity bias outweighs that
I guess part of the issue is that the type system is "bolted on" to the language, they weren't really designed together
as opposed to statically typed languages where grass is green and unicorns are happy
i dont think this would realistically be fixed in other statically typed languages
just slap Any 
An interesting thought experiment is marking members of a sum type as "hot" or "cold" (within the type system)
im currently just throwing type ignore everywhere and leaving a todo
user, # type: ignore # TODO: do we care?
well, regardless of the language, you will have to handle all the cases that come in
some languages make it more ergonomic, some not
in Python you'd probably do this with an exception
yeah but discarding the whole response cause i couldnt get one thing to completely resolve seems like bad design
well, you'll have to handle that exception
where you're calling the function
if you want to represent a partial response as a value, then you will have to handle it in some way
(because that's just the interface of the function, regardless of typing information)
maybe you can introduce a common interface for the real and partial response?
like a Protocol
oh one is already a subclass of the other
or a type alias at least
then just specify the superclass?
it's literally the same
it just means losing so much type info and adding a bunch of asserts everywhere
its not the same, unions arent flattened
If A is a subclass of B, then A | B is exactly the same as B
in some cases, you can write a function that encapsulates your type unsafety, so you don't have to propagate your type-ignores everywher
Because every operation that you are able to do on B, you can also do on A. But any extra operations available on A are not available on the value because it doesn't have to be an A
ok from a type theory perspective yeah
do you have some example where this doesn't work?
but from a user in an ides perspective they are different
especially if they only have basic type checking on
surely any type checker can realize subclass relationships?
as a user, I would find it confusing if I saw a union of a parent and child classes 🙂
out of interest, why?
I don't know, it's just unexpected. It's not clear what the intention in the type was. Maybe I'd consider it a typo or something being overlooked
because functionally, that's redundant
ParamSpec is a lifesaver
No more worrying about decorators erasing the wrapped function signature
I’m trying to type a Maybe class and I’m having some issues
I have the following code that works :
class Maybe(Generic[T]):
@overload
def __init__(self, present: Literal[False]) -> None: ...
@overload
def __init__(self, present: bool, value: T) -> None: ...
def __init__(self, present: bool, value: Optional[T] = None) -> None:
self.present = present
self.value = value
present: bool
value: Optional[T]
But I want to add just and nothing methods.
If I do this:
T = TypeVar('T')
Q = TypeVar('Q', bound=Maybe[object])
class Maybe(Generic[T]):
@overload
def __init__(self, present: Literal[False]) -> None: ...
@overload
def __init__(self, present: bool, value: T) -> None: ...
def __init__(self, present: bool, value: Optional[T] = None) -> None:
self.present = present
self.value = value
@classmethod
def just(cls: type[Q], value: object) -> Q:
return cls(True, value)
@classmethod
def nothing(cls: type[Q]) -> Q:
return cls(False)
present: bool
value: Optional[T]
then mypy can’t infer the type of Maybe.just(1) as Maybe[int].
I tried doing this:
T = TypeVar('T')
Q = TypeVar('Q', bound=Maybe[T])
class Maybe(Generic[T]):
@overload
def __init__(self, present: Literal[False]) -> None: ...
@overload
def __init__(self, present: bool, value: T) -> None: ...
def __init__(self, present: bool, value: Optional[T] = None) -> None:
self.present = present
self.value = value
@classmethod
def just(cls: type[Q[T]], value: T) -> Q[T]:
return cls(True, value)
@classmethod
def nothing(cls: type[Q[T]]) -> Q[T]:
return cls(False)
present: bool
value: Optional[T]
but it doesn’t work.
Is there some way I can do this?
@classmethod
def just(cls, value: T) -> Self:
...
that doesn’t work, mypy complains that typing doesn’t have a Self attribute :(
I’m using Python 3.11
not sure if Mypy already supports it
I would actually do it like this:
@dataclass(frozen=True)
class Just(Generic[T]):
value: T
...
@dataclass(frozen=True)
class Nothing:
...
Maybe = Just[T] | Nothing
``` no booleans required, and will play nicely with pattern matching
OK
frick
should I try and reproduce it in a cleaner environment and then report it?
What is SearchStrategy?
that’s something from Hypothesis
but the problem seems to be that Mypy doesn’t think the class itself is callable
What if you pass Just[T]?
Also could you try pyright? VSCode is like the natural habitat for it
(Pylance)
(why are you using mypy with vscode btw?)
because Pylance is proprietary
yes but that one is apparently no longer under development according to the readme
they apparently want to focus on pylance
the extension you mean?
yeah
🤔 it was last released yesterday
definitely still under development, as it's just connecting to the pyright LSP
which is also what powers pyright for vim, emacs, sublimetext etc.
oh ok
then I misunderstood the readme
sorry
it sounded like that
anyways why is mypy a bad choice for VSCode?
it's not really "bad"
it's just that pyright is more interactive, and adopts features much faster
like, you can hover on a variable and see its type
it also includes some IDE features like autocompletion, auto-imports, and some rudimentary refactorings
@classmethod
def just(cls, value: T) -> Maybe[T]:
...
but this will not work if inherit from Maybe
why does this happen?
def bar(): pass
from types import FunctionType
b: FunctionType = bar
Expression of type "() -> None" cannot be assigned to declared type "FunctionType"
"object" is incompatible with "FunctionType"
?
bar is getting inferred as object here? I am using pyright
You should use typing.Callable
from typing import Callable
def bar(): pass
b: Callable[[], None] = bar
I am in a case where I don't want to accept any objects which implement an __call__ instead function objects made by def and async def
Not __class__, but almost everything else like __name__, __module__, __code__, __global__, etc...
What if you use the inspect.isfunction() typeguard?
Internally that works by checking isinstance with FunctionType, since the inference is already skewed it doesn't work
Callable has __name__
This works just fine ```py
from typing import Callable, Any
def accept(f: Callable[..., Any]):
print(f.__name__)
print(f.__module__)
print(f.__code__)
print(f.__globals__)
@accept
def foo():
pass
i guess Callable has __name__, because otherwise we will get a lot of errors when we use func.__name__ which is pretty common
i cant find declaration of Callable in typeshed...
only this:
https://github.com/python/typeshed/blob/main/stdlib/typing.pyi#L175
but it is not very useful
stdlib/typing.pyi line 175
Callable: _SpecialForm = ...```
https://github.com/python/typeshed/blob/main/stdlib/_collections_abc.pyi
this module is just importing from typing
https://github.com/python/typeshed/blob/main/stdlib/collections/abc.pyi
this is almost empty
does it need anything more than just saying that it's a special form?
well all the stuff is handled by the type checker
_collectios_abc.py, why it is returning False from abstract __call__?
it's abstract. Ignore the body
im surprised it doesnt raise
making it an explicitly relative import like import .things works but i don't prefer that stylistically
and import module.things fails at runtime
Well, that's just how Python imports work. This import only really works if your working directory is /module/ (if this is your working directory, then you don't need the __init__.py and you can change the directory somewhere in mypy settings)
try going into the directory above module and doing from module.stuff import things
I intend module to be a module, and I intend module.stuff to import module.things
Will that module be used from some other module?
is it because module/__init__.py is empty?
I think I ran into something like this yesterday
When you do import module, you're doing an "absolute" import. Python will search for module in files directly in your working directory, then in 3rd-party libraries, then in the standard library.
if I from module import stuff, things in __init__.py will that allow import module.things to work in stuff.py?
So if you have something like this:
foo/
bar/
__init__.py
hmm.py
nice.py # imports hmm
stuff.py # imports nice
__init__.py
baz.py
run.py
hmm.py
# run.py
import foo.bar.stuff
"^ will just fail"
import foo.bar.nice
"^ this will technically run, but the `hmm` module imported by foo.bar.nice will be the top-level hmm.py"
So if you want to import a module on the same level, you need to either specify the full path (i.e. from foo.bar.hmmm import thinking inside foo/bar/nice.py) or use relative imports
I think I want to do full path, then
I did an experiment to check this while you were typing all that and I think that was my issue
I thought full path wouldn't work because it failed, and it failed because __init__.py was wrong
Python's module system is kinda messed up 🙂
Well, maybe not messed up. But not very intuitive
poorly taught, too; everywhere I google tells me oh __init__.py should be empty it doesn't need anything in it
yeah it should be empty 
unless you want to explicitly re-export stuff directly from module
except no it shouldn't be empty, it should re-export its own contents to itself
This is common in libararies, e.g.:
https://github.com/uiri/toml/blob/master/toml/__init__.py#L6-L7
toml/__init__.py lines 6 to 7
from toml import encoder
from toml import decoder```
if __init__.py does not import module.things then outside scripts can't import module.things and module.stuff can't import module.things
aka if __init__.py is empty as recommended stuff.py cannot from module import things and must instead import .things
No it doesn't
Try this:
(working directory)
foo/
bar/
__init__.py
baz.py
__init__.py
hmm.py
# foo/hmm.py
import foo.bar.baz
print(foo.bar.baz)
``` (other files are empty)
For example, in the Starlette framework:
top-level __init__.py only contains a __version: https://github.com/encode/starlette/blob/master/starlette/__init__.py
If you want to import something from a submodule, you just do from starlette.responses import JSONResponse
i checked around: if __init__.py is empty, import foo.hmm works, from foo import hmm works, but import foo denies access to foo.hmm with AttributeError
also: this shit is confusing
and now i'm wondering what issue i was even originally having
and i think it was that i have a test module for testing my project but its name collides with the standard library test so isort gets confused
actually wait no it wasn't that
i was definitely getting an import error on that too
let me try and reproduce that
wait no that was the same thing
it was colliding with the standard library test so the import failed because it was trying to look in the standard library test
yes 😄
i'm pretty sure
when you do import foo.hmm, python imports foo.hmm, then imports foo and gives it a hmm attribute
which... yeah
is definitely an interesting approach
and, of course, that attribute persists
perhaps the reason this is so confusing is that the dot means different things when you're importing a module vs when the module is imported
yep
Python's import system is by far the most confusing and complicated of all languages I've seen
wait until you see namespace packages
!pep 420
anyway... this is a bit off topic for type hinting, probably
and i guess my desired behavior is to make the behavior consistent, a.k.a. make submodules automatically import themselves if a parent is imported (which is kind of everything considering that a "module" here is just a folder), and the way to do that is to import explicitly in __init__.py
anyway thank you for educating me a little about this mess
What is the difference between returning None, types.NoneType, typing.NoReturn, and typing.Never?
you basically never want to use types.NoneType in type hints
typing.NoReturn and typing.Never mean exactly the same thing
then why are there two
typing.NoReturn is a confusing name, and in particular there are uses for it outside of return values
So when do you want to use None and when do you want to return typing.Never?
anyway, you'd use -> None to annotate any of the following functions:
def f() -> None:
pass
def g() -> None:
return
def h() -> None:
return None
what about typing.Never
would it be
def f() -> typing.Never:
raise BaseException()
it's for functions that loop forever or always raise
def exit() -> None:
raise SystemExit
yup
ah so for an infinite loop too
def inf() -> typing.NoReturn:
while True:
pass
Alright I understand, thank you!
yup! and a reason this is useful is so that type checkers can understand stuff like:
def foo(x: int | str):
if isinstance(x, int):
loop_forever()
x += "asdf" # <-- we want the type checker to be able to figure out that x must be a str here
now I have both mypy and pyright installed
I've got this rough setup:
ClassT_co = TypeVar("ClassT_co", bound="Foo", covariant=True)
P = ParamSpec("P")
class Foo:
def __init__(self, func: Callable[Concatenate[Foo, P], Coroutine[Any, Any, Any]]):
...
class Bar(Generic[ClassT_co]):
def convert(self, f: Callable[Concatenate[ClassT_co, P], Coroutine[Any, Any, Any]]) -> Foo:
return Foo(f)
why is pyright complaining that f cannot be assigned to func?
Argument of type "(ClassT_co@Bar, **P@convert) -> Coroutine[Any, Any, Any]" cannot be assigned to parameter "func" of type "(Foo, **P@__init__) -> Coroutine[Any, Any, Any]" in function "__init__"
Type "(ClassT_co@Bar, **P@convert) -> Coroutine[Any, Any, Any]" cannot be assigned to type "(Foo, **P@convert) -> Coroutine[Any, Any, Any]"
Parameter 1: type "Foo" cannot be assigned to type "ClassT_co@Bar"
Type "Foo" cannot be assigned to type "ClassT_co@Bar"
Foo needs to be generic
because in its __init__, you have a P typevar which doesn't make sense in that context
if I want to subclass Foo, would I just pass ClassT_co as its generic argument?
class Baz(Foo[ClassT_co]): ...
yeah
did mypy became better at inference?
def foo() -> list[dict[str, int]]:
result = []
for _ in range(5):
result.append({'x': 1, 'y': 2})
return result
``` now i have no errors on `result = []` line and `reveal_type(result)` shows correct type
pretty sure that's not recent
yeah, youre right
it also works on mypy 0.900
is there a way to print an image in python
can anyone recommend an easy to understand tutorial on type-hinting and/or mypy? i really want to understand it. but i'm really dumb
have you tried anything listed in the pins?
where?
ty i will give the realpython site dev.to one a try i 've been to the others they don't work on my rock brain
i recently migrated from mypy to pyright ( MS pylance extension ) and i'm facing issues with attrs defined classes. so for example i have this piece of code: ```py
import attrs
@attrs.define(kw_only=True)
class Foo:
_bar: int
```, attrs converts the class to be initiated without the _ in bar argument, which would be Foo(bar=1) but i get typing errors when i do that... (https://imgur.com/ycOgYAI.png)
i get errors if i change bar to _bar ( TypeError: Foo.__init__() got an unexpected keyword argument '_bar')
Pyright uses typing.dataclass_transform to support attrs, and doesn't have a specific implementation, so it deliberately does not support this along with a bunch of other attrs-exclusive features. https://github.com/microsoft/pyright/issues/3192
oh, that sucks. i'm using attrs because i need the kw_only argument support in 3.8 ( which dataclasses added in later versions ), guess i'll just make that classvar a public one
how can i type hint
"a tuple of an varying amount of integers"
tuple[int] is for example a tuple of a single integer
def return_zeroes(a: int) -> tuple[*int]
if a == 0:
return tuple(0)
elif a == 1:
return tuple(0,0)
elif a == 2:
return tuple(0,0,0)
else
return tuple()
tuple[int, ...]
>>> tuple(0,0,0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: tuple expected at most 1 argument, got 3
you should do (0,0,0), it works, it is faster, and it is more readable
Or (0,) * a since that's what they seem to want to do with the ifs
Maybe add one before multiplying
Would someone be willing to explain the TYPE_CHECKING constant to me? I read the pep484 part about it but still have questions
!d typing.TYPE_CHECKING
typing.TYPE_CHECKING```
A special constant that is assumed to be `True` by 3rd party static type checkers. It is `False` at runtime. Usage:
```py
if TYPE_CHECKING:
import expensive_mod
def fun(arg: 'expensive_mod.SomeType') -> None:
local_var: expensive_mod.AnotherType = other_fun()
``` The first type annotation must be enclosed in quotes, making it a “forward reference”, to hide the `expensive_mod` reference from the interpreter runtime. Type annotations for local variables are not evaluated, so the second annotation does not need to be enclosed in quotes.
i think the example here explains it well
is there anything that that doesnt clear up?
Yeah. Excuse me if my question is kinda dumb or should be like common knowledge or sth but im pretty new to programming/python.
I dont really understand why/when i would use this. from my understanding the
if TYPE_CHECKING:
import expensive_mod
line is only true while type checking and not at runtime.
So when i run the program it will not import the expensive_mod?!
Yes, at runtime it's just False
But the type checker thinks it's True
!d typing.TypeVarTuple
class typing.TypeVarTuple```
Type variable tuple. A specialized form of [`type variable`](https://docs.python.org/3/library/typing.html#typing.TypeVar "typing.TypeVar") that enables *variadic* generics.
A normal type variable enables parameterization with a single type. A type variable tuple, in contrast, allows parameterization with an *arbitrary* number of types by acting like an *arbitrary* number of type variables wrapped in a tuple. For example:
Seems immensely useful
its hopefully gonna get much more useful after they fully specify all of its behaviour
and Map
why doesn't this work
def get_database_type(python_type: type) -> str | None:
return {int: "INTEGER", float: "REAL", str: "TEXT"}.get(python_type)```
error:
sounds like pylance is inferring the type type[int] | type[float] | type[str] for the key type
try ```py
_db_types: dict[type, str] = {your_dict_thing}
This is to avoid import loops and other side effects that rigorous type checking imports might otherwise cause
Usually you don't need to worry about it
How would I typehint a function returns that returns a list that has N integers where N is inputed num
def n_digits(num: int | str) -> [int * num]:
...
n_digits(3) -> [1, 1, 1]
n_digits(8) -> [8, 8, 8, 8, 8, 8, 8, 8]
you can't specify the length of a list

if you wanted to (not that you can actually do this like this) use a tuple
youd need something like rust's const generics
is it not possible to type hint multiple things when unpacking unless i unpack one thing at a time?
# this is doable
varA: str = some_func( ... )
varB: float = some_other_f( ... )
# but what about this?
varA: str, varB: float = big_func( ... ) # big_func returns two things, a str and a float
Yeah you can't do that. Usually you don't need to do this though
Well, you can do
varC: tuple[str, float] = big_func()
But again, the type is probably inferred correctly if big_func has a type annotation
hey, i was wondering if it was possible to create a typehint for a callable which takes a non-fixed amount of arguments and the annotation for the first argument should be a specific one, others can be typing.Any/whatever provided
maybe something with callable protocols, like def __call__(self, x: SomeType, *args: Any):
tysm this worked for typing! i implemented it using a typing.Protocol subclass py class AppCommandCallbackT(typing.Protocol): def __call__(self, inter: ApplicationCommandInteraction, *args: typing.Any) -> typing.Any: ... but now it expects me to provide the first argument name as inter, lemme provide the full context. im actually trying to make a decorator that accepts an async function with the type above. so if i try something like ```py
@client.with_slash_command(name="test", description="")
async def foo(a: wyvern.AppCommandInter, b: str) -> None:
...
instead of
@client.with_slash_command(name="test", description="")
async def foo(inter: wyvern.AppCommandInter, b: str) -> None:
...
AppCommandInter is just an alias for ApplicationCommandInteraction here*
making inter in the protocol a positional-only argument fixed the issue, thanks!
you could use typing.Callable and typing_extensions' Concatenate too
mind showing an example for how it would work with the case above?
non-fixed amount of arguments and the annotation for the first argument should be a specific one
i looked into callable but it accepts either fixed number of args or no annotations for args at all
you set Callable's first argument to Concatenate, and inside Concatenate you add the types that you would inject to your callback. After that, you add a ParamSpec type
from typing import TYPE_CHECKING, Any, Callable
if TYPE_CHECKING:
from typing_extensions import Concatenate, ParamSpec
P = ParamSpec("P")
def decorator(f: Callable[Concatenate[type1, types2, ..., P], Any]) -> Any:
...
in this case, you'd replace type1 with AppCommandInteraction, followed by the ParamSpec which would be the non-fixed arguments that would get called
see also: https://peps.python.org/pep-0612/
oh, i find protocol much easier but i'll take a note of this, thanks!
.bm
Click the button to be sent your very own bookmark to [this message](#type-hinting message).
When join types?
protocol __call__ also supports @overload, which is impossible to express with Callable[]
would type hinting self in methods be alright, to be consistent?
It would look a bit, extreme however unless theres a good reason not to; why wouldnt you
oh, its not even possible is it. At least it doesnt seem to recon its own type by name.
meaning:
class A:
def func(self : A, var : int):
print(var)
will give "NameError: name 'A' is not defined"
Python Enhancement Proposals (PEPs)
You could directly typehint typing.Self
You need to use quotes or from __future__ import annotations to use A, since that hasn't yet been defined when the inside of the class statement is being executed.
But unless you need to, no point annotating self, type checkers know what it is already.
It awfully sounds like its not even worth it then
yeah, it's not really worth it
Is there any tool for static analysis to populate typehints?
jelle’s autotyping (when combined with pyanalyze) is probably the best thing there is. or maybe get a language model to do it
hey everyone,
I want to ask whats benefit of mypy? I saw that its better for testing but what about performance. I saw that good example but I am bit confused how to implement my all codespace as well.
And the most important thing ; do you advice to use it?
i wouldnt currently recommend using mypyc its too early alpha and has a lot of memory safety issues
its however good for making sure your code works before you even run it
mypyc is stupid sometimes, it gives weird errors at runtime almost without explanation and it has a lot of edge-cases and exposed implementation details
What is typing
I recommend using Cython for the majority of use-cases one might consider mypyc
As usual with these kind of things a better algorithm or data structure will probably give you a better performance increase than just compiling it
How can i type hint lambda
def my_func()-> Lambda:
...
!d typing.Callable
typing.Callable```
Callable type; `Callable[[int], str]` is a function of (int) -> str.
The subscription syntax must always be used with exactly two values: the argument list and the return type. The argument list must be a list of types or an ellipsis; the return type must be a single type.
There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types. `Callable[..., ReturnType]` (literal ellipsis) can be used to type hint a callable taking any number of arguments and returning `ReturnType`. A plain [`Callable`](https://docs.python.org/3/library/typing.html#typing.Callable "typing.Callable") is equivalent to `Callable[..., Any]`, and in turn to [`collections.abc.Callable`](https://docs.python.org/3/library/collections.abc.html#collections.abc.Callable "collections.abc.Callable").
import typing
def my_func()-> typing.Callable:
...
Does not seem to work nevermind it works
You'll want to parametrize it for accurate typing
if you add overloads to youre function specifying each possible input-type and return-type, do you then make the final implemention's return type Any, empty, or a Union of all possible return types?
either Any or a union of all the return types
Any it is! thanks
wait I just though why dont we do def func(a) -> ...: to signal that its implemented somewhere else?
the website? reddit?
thanks brother
Can anybody verify that builtin types like: list, tuple, etc. are subscriptable in Python version 3.8.10 (using from __future__ import annotations)?
Cause it says that it was introduced in Py 3.9 but works from 3.7 and beyond using the future imports but as you can see it raises an error.
Does anybody know whats wrong here because I got many similar errors where ppl say it works with future imports but dosent actually work on my PC. Could it be that I need to enable some feature or maybe this dosent work withing packages? just guessing...
File "C:\Users\Shner\Desktop\poetry\pandasdb\pandasdb\table.py", line 15, in <module>
test = list[str]
TypeError: 'type' object is not subscriptable
Process finished with exit code 1
the future import only affects code that is syntactically within an annotation
it doesn't make anything subscriptable, it just makes it so annotations aren't evaluated
I have that at the top of all the modules in the package, so what could be the problem?
Do I also need to add it to the module that imports the objects from package\table.py? (i.e the tests)
the code in your sample is not in an annotation
do you mean I need to add quotes to list[str]?
then can I still save it to a variable ?
wait does annotations mean quotes?
no
what does it mean literally?
at this point I will just import the types..
syntactic positions like a: annotation, def f(x: annotation)
oh so the actual type-hint, my bad 😅
btw I need some advice, I'm currently converting all the str | int to Union[str, int] and also list[str] to List[str], all this to make my package compatible with Python 3.7 and upwards (currently only works in 3.10).
So basically I'm gonna do two different versions, one for 3.7 - 3.10, and another one for 3.10 upwards. And the only thing that I'm changing are the type hints, all the functionality is exactly the same,
so at this point I'm thinking I might as well remove all the type hints from the modules and create a separate files that annotate the functions and methods (kind of like std library does, although I dont know how it works exactly), any thoughts on how yall would approach this?
so I would need to add quotes to all the type hints, which wouldnt be necessary for 3.10
the thing is that most ppl using my package are data people, and they dont really update their Python as long as Pandas works lol
the future import automatically turns annotations into strings, so you'll only have to do that for things like the test = list[str] as that's just doing anormal assignment at runtime instead of an annotation
I see now, but I need to create an alias since I use it everywhere, TypeAlias is available in 3.10+.
So how can I do the following in 3.7/8
PrimitiveTypes = Union[str, int, float, bool, None]
TableRow = tuple[PrimitiveTypes, ...]
you have to manually use a string for those cases
potentially annotating with TypeAlias (which will be stringified automatically by the import)
use typing_extensions.TypeAlias
for versions below 3.10 you can import it from typing_extensions under an if TYPE_CHECKING
holy shit!! it works now!
from typing import Union, Generator, Callable, Any, overload, Literal, TYPE_CHECKING
from typing_extensions import TypeAlias
PrimitiveTypes = Union[str, int, float, bool, None]
if TYPE_CHECKING:
TableRow: TypeAlias = list[str]
the weird thing is that I can actually do from typing import TypeAlias but only within if TYPE_CHECKING...
and just to confirm, can I just do: TableRow = list[str] cause its kina obvious its a type alias?
btw I never import TypeAlias, so I dont really know how it works..
TYPE_CHECKING is just False at runtime
is it defined that way in the module? just TYPE_CHECKING = False?
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
False
cool
I have a problem
I’m trying to make a class that inherits from classmethod, but mypy yells at me when I inherit from me because I don’t give the generic parameters—but classmethod doesn’t have the subscript functionality. Is there a Classmethod in typing or something?
…nope
if TYPE_CHECKING:
Classmethod = classmethod
else:
Classmethod = defaultdict(lambda: classmethod)
``` or something like that
i generally prefer
class MyClassmethod(classmethod[Foo] if TYPE_CHECKING else classmethod): ...
although i dont think mypy will work with either of these
might be worth opening an issue on the cpython issue tracker about this and saying that classmethod should support subscription
Is it possible to typehint this properly or no ```py
from typing import Any, Callable, Coroutine
def create(channel_id: int | None = None) -> Callable[..., Any] | Coroutine[Any, Any, int]:
if channel_id is not None:
async def _underlying(channel_id: int) -> int:
return channel_id # placeholder
return _underlying(channel_id)
def subscribe(func: Callable[..., Any]) -> Callable[..., Any]:
return func # place holder
return subscribe
@create()
async def foo(bar: int) -> int:
return bar
await create(123)
I'm using Any as a placeholder rn
Oh yea, got it with overloads
nvm then ig
Coroutine is the return type of an async function
so an async def _underlying(channel_id: int) -> int would have the type of Callable[[int], Coroutine[Any, Any, int]]
or you could use Awaitable (Callable[[int], Awaitable[int]])
I'm returning _underlying(channel_id)
I don't know I just thought it would be cool to do something like that
I didn't know it would actually work until I tested, even though it was very obvious kek
if you have some sort of flag argument, and the body of the function branches on the flag as its first step, usually it's better to make two functions
that's just a simpler solution
anyone aware of a detailed description of overloads
i'm having trouble with defining 2 interacting parameters that control type output
i have something that stores strings and has a get method like
def get(self, key, default: _D|None=None, convert: Callable[[str], _T]|None = None) -> _D|_T| str | None
as far as i understood i need to declare at least 4 overloads or more
to make it understand that the return type union is limited in a way that (None/_D) match to the type of the default value and str/_T match whether convert was passed,
i tried a number of permutations and keep getting met with mypy overloads either not being able to produce all versions or missing some,
i'd like to understand the underlying logic so the usages of the api can imply the reduced types in use on a api call
@overload
def get(self, key: str) -> str | None: ...
@overload
def get(self, key: str, default: _D) -> _str | _D: ...
@overload
def get(self, key: str, default: _D, convert: Callable[[str], _T]) -> _str | _T | _D: ...
@overload
def get(self, key: str, convert: Callable[[str], _T]) -> _str | _T | None: ...
``` does this sound right?
Perhaps you could simplify it like this py def get(self, key, default: _T | None=None, convert: Callable[[str], _T] | None = None) -> _T | str | None then you will need these overloads: ```py
@overload
def get(self, key: str) -> str | None: ...
@overload
def get(self, key: str, default: _T) -> _str | _T: ...
@overload
def get(self, key: str, default: _T, convert: Callable[[str], _T]) -> _T: ...
@overload
def get(self, key: str, convert: Callable[[str], _T]) -> _str | _T | None: ...
@trim tangle that's a good start, now what if i also wanted to call that one from a helper that passes convert/default over as parameters
wdym?
ah
well that's kinda complicated 🙂
maybe you could provide some more details/context?
i just found a permutation that works thanks to your input ( i needed to add a few cases for none type vs not passed)
i'll clean up some more then link the pr i created for iniconfig
Why do you need convert though 🤔
its part of the apis that where coined over 10 years ago and are still in use - its pretty convenient to do something like get("somekey", default=1, convert=int) when interacting with inifiles at a lower level
(no schema tools involved)
ah, ic
I guess that mostly stems from Python not having convenient ways of working with nullable values
(those apis where first created somewhere between 2007 and 2009 so its slightly worse ^^)
here's a super flexible and over-engineered solution 🙂
@dataclass(frozen=True)
class GetHandler(Generic[T, D]):
on_missing: Callable[[str], D]
convert: Callable[[str], T]
def identity(x: T) -> T:
return T
passthrough: GetHandler[str, NoReturn] = GetHandler(lambda key: {}[key], identity)
default_none: GetHandler[str, None] = GetHandler(lambda key: None, identity)
default_key: GetHandler[str, str] = GetHandler(identity, identity)
def with_default(default: D) -> GetHandler[str, D]:
return GetHandler(lambda _: default, identity)
###
def get_complicated(self, key: str, handler: GetHandler[T, D]) -> T | D:
if key in self._data:
return handler.convert(self._data[key]
else:
return handler.on_missing(ley)
def get(self, key, default=None, convert=identity):
return self.get_complicated(key, GetHandler(lambda _: default, convert))
no overloads required, and you can easily pass the default and converter through
but yeah it's not a thing you do in Python
hmm, galaxy brain has a seizure moment
basically its a pain that the type narrowing for parameter combinations has to be exploded (so having 2 parameters with effect means one might have to have 4-6 overloads depending on downstream usage)
that's generally a problem with boolean/other flags that change the return type
well, that kinda stems from the fact that there are really 2^N possibilities
(or more if you have more possible classes of values)
and if I as a human am calling a function with 6 boolean flags, there are indeed 64 ways to configure it
but yeah, Python's type system is quite lacking in some more powerful transformations that TypeScript has
im curious about that, got a link for the beginner by chance
About typescript?
about the powerful transformations
it wouldbe nice to have partially conditional return types
In typescript, you can type a function that converts a object between snake and camel case
You can also build "literals" using template strings
sounds like thats basically touring complete typing
Almost.
i'd love to be able to do things like type a wrapper object based on what it wraps
No proxy objects yet
(particular use-case would be api specs, have a sans-style spec of the transforms, and then dynamic sync/async wrappers one can use with the spec against requests/urllib/httpx/aiohttp)
are there recommended api consumer tools for that these days?
(i have a lot of openapi-code-generator hell i'd love to drop but the python-side for the clients is not quite there)
hmm, sorry for the ot
So an async version, then a sync wrapper?
You can at least type your args and kwargs with unpack and typeddict
@rare scarab no, more like the wrapper that uses the api would need dynamic typing to map the request/response parsing/creation of the sans api onto the types of the used api
so one woudl have something like
api = RequestsApiClient(mylib.MYAPI); api.something(...)
(so one would need to infer the methods of the api object based on myapi)
That defeats the point of static typing
@rare scarab its meta-typing in a sense - a structural wrapper that replicates a behavior combination over all methods of object passed in, its static in the sense that as the types are know, their methods are known,
no it's actually Turing complete
there are interpreters for Brainfuck and SQL in typescript's type system
basically if i have a api spec type that for example has methods in the form of
class MyApiSpec:
def get_user(self, id: uuid) -> UserRequest[User]:
return RequestWithReposneParser(parse_user, f"/users/{id}))
then i want to be able to auto-type proxies so that they behave as if thy where defined as
class MyApi:
# type seen but not actualy implemented
def get_user(self, id: uuid) -> User:
request = self._spec.get_user(id)
response = self.__client.request(request)
return request.response_parser(response)
def __getattr__(...):
#sweet magic here to make/fake the methods
What if a new version of the API comes out which requires you to send two requests instead of one to achieve the workings of get_user?
Like, first you get a Personal Data Permit and then fetch the user with it
Or maybe you are making a nicer higher-level wrapper for this API and you want it to work with both sync and async
if get_user needs 2 requests, then at the level i showed it would turn into 2 calls and a breaking change
both sync and sync higher level wrappers are not considered as that would need to deal with entities, cached/local data and their interaction + interaction patterns - it would need research and quite the amount of good luck
currently i'm of the opinion that the high level wrappers need to pick a coloring (either sync or async) - creating tools that enable to flip around that coloring is outside of what i can sensibly spend time one
someone that has more deep experience with making one of those may disagree, personally im not aware of a attempt
You can do that for single functions, but I don't think you can generate names like that on classes
currently its nto possible as the type system doesnt have "parameter based annotation generation"
I missing something basic but my google fails me.
How can I check for at type so that the type checker accepts it?
def add_text(self, text: str | list[str]):
if type(text) == list[str]:
self.add_text_list(text)
...
def add_text_list(self, text: list[str]):
...
Argument of type "str | list[str]" cannot be assigned to parameter of type "list[str]"
or just do if not isinstance(text, str):
although saying that you can probably just check isinstance(text, list)
Well isinstance does not work with list[str] but yeah, inverting the check
no im saying isinstance(text, list) should work
Why does the type checker work like this?
doesnt need to be list[str]
which part?
I mean why cant isinstance check for list[str] ?
at runtime lists don't know what they are lists of
i misread sorry
it being list[str] is purely a concept at typechecking time
what should isinstance([], list[str]) return?
and for a really long list, should we check all elements to see if they are strings?
Ah, of course, now it makes more sense
didnt python 3.4 typing or something actually allow the isinstance checking?
did that have O(n) behaviour?
typing has only been in the stdlib since 3.5. Maybe some very early version did this but not that I'm aware of
yeah im talking about an early version
If you're interested, I have a short explanation of why that's a complicated thing conceptually. It's actually an impossible check, purely speaking 🙂
https://decorator-factory.github.io/typing-tips/faq/runtime-checking/
Thanks but I think I got it. Type checker and runtime needs to work together, and runtime of course has no concept of a list[str] since lists are heterogeneous
yep
(Now writing a python script to transform pybind11-stubgen generated stubs into something that works)
How would I be able to restrict the decorated functions params typehint?
I have Events.WILDCARD[str] basically, I now want to type error whenever the functions signature doesn't match that type for the arguments
Oh nvm got it to work
But I'm sure there is probably a better way out there
what's Events/Event?
if i have a variable that can be an instance of an object or None, is the None implied if i set it as default
(is this valid or do i need the Instance | None typehint?
variable: Instance = None
you need | None
thanks
dict[str:int]
is there a way to type hint the expected keys of dict
(turn it to something like this )
dict['a': int, 'b':int]
or even
dict['a': int, 'b':str]
or this something that must be specified in the docstring outside of type hinting
!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...
use this
there a type safe way of module level properties?
or preferably a maintained library?
yeah you probably can if you write your own decorator for it
did whoever think of including ... in Python intend for its use as it is used in typing? because that’s one hell of a good way to use it!
its dict[str, int] btw
dict['a': int, 'b': int] i love this syntax
Why was the extra __class_getitem__ method introduced? Can’t a class just implement __getitem__?
!pep 560
it would have to be on the metaclass, and introducing a new metaclass can cause problems
I see
Isn't this supposed to be done like dict[str, int]?
why not replace dict literals with slices 🙃
it’s a real bummer you can’t parameterise Self
It is, see https://github.com/python/typing/issues/548
(It's hard)
from typing_extensions import Self?
that’ll be cool!
or if you have class named Test
def f(x) -> "Test":
...
that's actually not the equivalent of Self
because of subclassing
i mean self wouldnt even work there cause its unbound
Any PEPs for standardizing __orig_bases__?
Wdym standardising it?
It's in the same pep as class getitem iirc
It's just the return of mro_entries
I mean its an implementation detail yet
No it's not an implementation detail
It's probably cause it's really hard to type reliably
Isn't it just a tuple?
And 99.999% of people literally never need it
Yeah but whether or not it's there is really hard to tell
Not every type has it
Python Enhancement Proposals (PEPs)
I guess we could probably add it to object but I don't think anyone has asked for it
Wouldn't it be on type?
oops yes
I ask for it now😄
I think we've added a few other attributes that are only sometimes present
open an issue on GitHub
Explains why I noticed more items in IDE suggestions
e.g. typeshed has object.__doc__
why is it typed as str | None though?
If an object has no __doc__ it should raise AttributeError, no?
it's None on functions with no docstring
is it to avoid incompatibility issues in subclasses?
but it's kind of weird we have it on object, I think we tried to remove it but ran into some trouble
The descriptor methods aren't present either
ok I have a problem
I moved my code into a module and apparently that makes mypy stricter
so now I get errors everywhere I didn’t before
like this one
for context: Just is a subclass of Maybe. The only thing different about the type layout of Just vs Maybe is that Just.value: T.
mypy should know that self.value == other.value is a bool
…but it says this: Returning Any from function declared to return "bool"
Well mypy is actually correct, __eq__ can return any value. Numpy arrays are one of the more well known objects that don't return bools.
NotImplemented isn't an exception.
I thought NotImplemented is different from NotImplementedError()
They're unrelated yeah.
it generally doesnt
NotImplemented is typed as Any / special cased in these methods.
im pretty sure you get a syntax warning or something nowadays if you do ```py
NotImplemented == True
Should I just type the method as -> object?
oh maybe you dont 🤔
Do return bool(self.value == other.value)?
Or should I set the parameters to SupportsRichComparison)
oh ok
that shuts it up, thanks
No you don't need rich comparison, but a __eq__() protocol would work.
I thought that wouldn’t work
Yes, but this would add the constraint that it returns a boolean.
oh right
how do other containers do it?
do they coerce it to a bool?
is __eq__ always expected to return something with __bool__?
i wanna say you cant ever get a builtin container to return not a bool
I think they don't check, just let exceptions bubble up.
Everything also has a __bool__ method.
!d object.__eq__
object.__eq__(self, other)``````py
object.__ne__(self, other)``````py
object.__gt__(self, other)```
These are the so-called “rich comparison” methods. The correspondence between operator symbols and method names is as follows: `x<y` calls `x.__lt__(y)`, `x<=y` calls `x.__le__(y)`, `x==y` calls `x.__eq__(y)`, `x!=y` calls `x.__ne__(y)`, `x>y` calls `x.__gt__(y)`, and `x>=y` calls `x.__ge__(y)`.
A rich comparison method may return the singleton `NotImplemented` if it does not implement the operation for a given pair of arguments. By convention, `False` and `True` are returned for a successful comparison. However, these methods can return any value, so if the comparison operator is used in a Boolean context (e.g., in the condition of an `if` statement), Python will call [`bool()`](https://docs.python.org/3/library/functions.html#bool "bool") on the value to determine if the result is true or false.
By convention, False and True are returned for a successful comparison. However, these methods can return any value [...] Python will call
bool()on the value
could you give me an example of a numpy object that doesn’t return bool?
I want to test this
and I’m unfamiliar with numpy
Out[23]: array([False])
!e basically it vectorizes it, so you can do like,
import numpy as np
arr = np.arange(10)
print(arr)
print(5 > arr)
@buoyant swift :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | [0 1 2 3 4 5 6 7 8 9]
002 | [ True True True True True False False False False False]
ok it returns a bool
good to know that calling bool is usual behaviour
I want my container to feel as vanilla as possible
bool(numpy_array) only gives an answer if you have a 1 element array
does anyone know why mypy gets stricter once you go into a module? I had mypy.ini say strict = True before…
yes, and as you can see here the builtin list just passes on that error
so me calling bool would match builtin behaviour
I want my Maybe type to feel like a list that can only have one element
it doesn't in general
hm I guess my settings weren’t coming through
weird
ok so this is a good reason not to use pyright
it gets stuff wrong
like in this case
I would expect that I have something wrong about variance
but No!
both the bottom end of the type hierarchy and the top end don’t shut it up!
I tried NoReturn and object…
what I’m trying to express with
@overload
@classmethod
def _class_call(cls) -> Nothing[G]:
...
is that the Nothing can contain literally anything! It doesn’t care about its contained value…
it’s basically both covariant and invariant
in Haskell we have Functor and Covariant for functors that do both ways, which is kinda different, but you can still infer that they don’t care about their type argument
sorry
Contravariant
phantom :: (Functor f, Contravariant f) => f a -> f b
but no
TypeVar cannot be both covariant and contravariant
anyways mypy gets the weird case right
it doesn’t complain about G being incompatible with G
and it correctly works on one end of the type hierarchy and doesn’t on the other
If it’s both covariant and contravariant, is there any operations you can be sure it can do? Wouldn’t that just be object?
the point is that if it’s both its type argument is literally irrelevant
hm maybe I should make Nothing inherit from Maybe[NoReturn]
that worked fabulously
it still doesn’t shut up pyright
Type "NoReturn" cannot be assigned to type "G@_class_call"
yes it can be you silly idiot
it has no complaints about this
there's slight precedence for it (in the sense that Any is top and bottom) but I don't believe there's a lot of motivation to support phantom variance in python
and inhabitation checks are also quite lackluster as you've seen (for similar reasons; the primary use case is diverging functions)
ok but I hope you agree this is a pyright bug
I agree it's a limitation and you're free to post a gh issue about it
who knows, maybe eric has some extra time on his hands 
ok sorry about being so harsh about it
I’m just used to a robust type system so I get unreasonably upset at things like this
really sorry, again
I guess the difference is that Python itself is ultimately dynamic, so it’s fine if the type system can’t handle all cases.
yeah
ok I made an issue
https://github.com/microsoft/pyright/issues/4309
Note: if you are reporting a wrong signature of a function or a class in the standard library, then the typeshed tracker is better suited for this report: https://github.com/python/typeshed/issues....
just fyi i think the preferred variable here is Never but that is besides the point
they are the same thing
I am using 3.10 so I don’t have Never atm
can always use it from typing_extensions
ok
(using pyright) ```py
def convert(converter: Callable[[str], T]) -> Callable[[Callable[[list[T]], PT]], Callable[[list[T]], PT]]:
def decorator(func: Callable[[list[T]], PT]) -> Callable[[list[T]], PT]:
func.converter = converter
return func
return decorator
@convert(lambda x: int(x))
def part_one(lines):
reveal_type(lines) # Type of "lines" is "Unknown"
return ''
is there a way for the type to be inferred automatically
or would I have to explicitly typehint it
Can you make your own typehint?
You have to explicitly typehint it, I think
What exactly do you mean?
Lets say I have a class:
class Bob:
def __init__(self): pass
Can I do
def apple(x: Bob) -> None:
pass
or does that not work?
yes you can
Okay, thanks 🙂
You would have to always explicitly typehint it. You can modify the argument types and the return type of the function for the outside world but even that wouldn't work with lambdas.
Got it, thanks
Is TypeAlias not generic?
in Python less than 3.10
For this
SimpleAdapter: TypeAlias = ct.Adapter[T, T, U, U]
I get this error:
Bad number of arguments for type alias, expected: 0, given: 2 [type-arg]
but i dont get this error (nor the error which asks me to explicity use TypeAlias) in mypy on 3.10
It's not a type, it's a marker to indicate that a variable is a type alias. How is ct.Adapter defined?
It is a generic class
And I am using a TypeAlias to reduce the number of type params as they are same
That should work fine.
class Adapter(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes]):
Ah. You need to inherit from Generic, that class isn't generic.
but subconstruct is
why is this an error only now?
Ah that is still generic, this is quite strange.
TypeVars makes no sense in outer scope
You should use typevars only inside generic classes or generic funtions
if TYPE_CHECKING:
class SimpleAdapter(Adapter[T, T, U, U]): ...
else:
SimpleAdapter = Adapter[T, T, U, U]
this does just look like a mypy bug lol
ok so, somewhere in a stub file:
class Adapter(Subconstruct[SubconParsedType, SubconBuildTypes, ParsedType, BuildTypes]):
def __init__(self, subcon: Construct[SubconParsedType, SubconBuildTypes]) -> None: ...
and still when I use it like this:
class Log2(SimpleAdapter[int, float]):
def __init__(self, subcon: Any, factor: int):
super().__init__(subcon)
I get an error:
Too many arguments for "__init__" of "object"
where SimpleAdapter is now @tranquil turtle 's snippet.
and also why does this setting in pyproject.toml
[tool.mypy]
python_version = "3.7"
still cause mypy to output different errors when ran under 3.7 and 3.10?
one more, errors with Argument 1 to "__init__" of "_VSTPluginProp" has incompatible type "int"; expected "_VSTPluginEventID" but only on Python <3.10
seriously wtf mypy
that's always a phrase I start my day with
I have never understood the difference between an Iterator and an Iterable, could someone explain it to me?
Iterators are objects that have a __next__ and __iter__ methods, meanwhile Iterables are objects that only have an __iter__ method
an iterable is something that has values that could be produced in an order. An iterator is the object that is actually producing them.
- nedbat
Ah thank you
lmao I just noticed I mentioned Iterable twice
Iterator's __iter__ function should always return itself
How can you specify that a function takes arguments according to a ParamSpec variable?
ah I see
you do
P = ParamSpec("P")
T = TypeVar("T")
def with_args(*args: P.args, **kwargs: P.kwargs) -> Callable[[Callable[P, T]], T]:
def with_func(func: Callable[P, T], /) -> T:
return func(*args, **kwargs)
return with_func
I have a problem with parameter specs
I have the following abstract method:
@abstractmethod
def call(self: Maybe[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Maybe[G]:
pass
and I inherit from Maybe[T] (T is covariant) in Just
So I type my method
def call(self: Just[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Just[G]:
...
But mypy insists that these signatures are incompatible
that looks like a bug
OK Imma report it
also my issue with pyright I had earlier isn’t an issue (apparently)
since mypy doesn’t recognize it I reported it to mypy instead, as it is their mistake and not pyright’s (apparently)
I think you need to use Concatenate?..
oh wait, I misread, sorry
just ignore me
Any way to avoid re-annotating the impl func?
Yeah you can just use Any 😉
ohh nice
can i annotate return type as any too?
or would that erase the type annotation of the wrapped / decorated function
it shouldnt do
also can i merge the int and str overloads?
yeah that can just be a union
will pylance be faster if i hardcode the type hints?
so it has to spend less time inferring return type
Probably not but I'm not an expert in the internals of pylance. Remember that pylance still needs to go through the entirety of the code to typecheck stuff
So I doubt hardcoding typehints will make your code significantly faster.
why is this return type inferred as noreturn?
because it always raises?
i get random false positives a lot and they don't go away until i resave the file(s)
Yes, I have the same problem. It's a bug that we should report but I can't find a clear way of reproducing it.
yes very random
started happening a month or so ago
Exactly.
What LSP are you using?
Ok
?
I have an issue I don’t know how to reproduce well
I have a class ```py
class Maybe(CallableABC, Generic[T]):
...
@abstractmethod
def call(self: Maybe[Callable[P, G]], *args: P.args, **kwargs: P.kwargs) -> Maybe[G]:
pass
...
def call(self: Maybe[Callable[P, G]], *args: P.args, **kwds: P.kwargs) -> Maybe[G]:
return self.call(*args, **kwargs)
...
where `CallableABC` is an instance of a metaclass that defines a custom `__call__`.
Now there is one bug here I know how to reproduce: Mypy complains that `*args` is the wrong type, that it should be “`<nothing>`” (whatever that is)
…but also, Pyright has a bug here: It complains that “Arguments for ParamSpec "`P@__call__`" are missing”.
…but when I try to reproduce this in a clean environment, only the Mypy error shines through. I suspect it has something to do with the `CallableABC` metaclass…
is there any support at all for a container changing the type of its contents?
can you explicitly mark a method as type-unsafe?
(for consumers of your library)
wdym by unsafe?
wdym
a function that cannot be typed correctly because any use of it will change the container’s internal type, which apparently there is no support for currently, is my specific use case
i.e. having an imap (in-place map) function on a subclass of list that could transform the type
ah
interesting
i cant think of any way to do that
does any other language support this
¯_(ツ)_/¯
sounds kind of like linear types
class SuperList(list[T], Generic[T]):
def imap(self, fn: Callable[[T], T]) -> None:
...
transforming the container sub-type is not really a thing, so you'll just use Any or some other kludge if you really need it anyway
ok
would a mutable container type that can only have one entry be invariant, or could it be covariant?
the example in the mypy docs doesn’t really apply because you can’t add another element, and it says that most mutable containers are invariant
invariant since you can set the value. def boom(box: Box[object]): box.set(object())
class Box(Generic[T]):
def __init__(self, value: T) -> None:
self._value = value
def get(self) -> T:
return self._value
def set(self, new_value: T) -> None:
self._value = new_value
Consider that you have a foo: Box[Chicken].
If you could pass it into this function:
def put_cat(box: Box[Animal]) -> None:
box.set(Cat())
``` that would be a problem
now if you do b = Box[int](1); boom(b)
yeah
your box no longer has an int
ok thanks!
lol nice, we came up with the exact same example
well you had already read this so
https://decorator-factory.github.io/typing-tips/tutorials/generics/custom-variance/#invariance-box
our shared subconscious
except you are trying to put animals in boxes, poor kitty
at least the box doesn’t have radioactive gas inside it
I was trying to make a Schrödinger’s Cat joke
it’s the only other intellectual cat joke I know
yeah I realized
is it by design that mypy & pyright always assume that the type of type(self) is specifically the specialized generic type that was declared as the parameter type while at runtime it’s always the unspecialized generic type that can still be specialized? wouldn’t it allow for more flexibility if
@dataclass
class Spam(Generic[T]):
x: T
def eggs(self: Spam[G]) -> Spam[tuple[()]]:
cls = cast(type[Spam[tuple[()]]], type(self))
return cls(())
worked w/o the cast?
on an unrelated note, I feel that the fact that Pyright warns about type parameters that are only used once in a function is kinda annoying since apparently it can make a difference on invariant types
can it?
how to state that you have a class object? Like Union[MyObj, int] I believe says you either have a instance of MyObj or an int, but what if I want to say that the argument is a type variable -- i.e. it is either int or the class of MyObj
int has the type of type[int]
the class of MyObj isnt a type variable
so that would be type[MyObj] | type[int]
maybe you could give an example of what you want?
yeah, if you have something typed as self: Foo[object] and Foo[T] is invariant in T, it can refuse to bind in other methods where self: Foo[U], where usage of self: Foo[S] would’ve communicated it can work for any type
just ran into this
I think that might be what I want
if I say type[MyObj] does it just refer to MyObj's class, or is there anyway to refer to MyObj or any subclasses of it?
it refers to any subclasses of it too afaicr
yeah it does
y
? wdym
oh ok
how would I typehint the class instead of an instance, so something like this:
class Tile:
def __init_subclass__(cls, id: int) -> None:
cls.id = id
class T1(Tile, id=1): pass
class T2(Tile, id=2): pass
id_to_tile: dict[int, ???] = {1: T1, 2: T2}```
ty
I’m getting an unclear error with mypy.
I have these methods:
def __or__(self: MMaybe[G], other: MMaybe[G]) -> MMaybe[G]:
return self.alternatively(other)
def __ior__(self: MMaybe[G], other: MMaybe[G]) -> MMaybe[G]:
self.ialternatively(other)
return self
but mypy complains: Signatures of "__ior__" and "__or__" are incompatible [misc]
am I doing something wrong?
Is that the full error?