#type-hinting
1 messages ยท Page 10 of 1
no need to specify stuff that wont change the bahviour from the user perspective
Yes, exactly as @oblique urchin said.
and this way it is also more flexible for the devs
None in this case. However, in general case, I would go for more specific hints
That's a bit extreme, yes
this can however be useful for some things like version tuples
cause then you can statically determine if some things will be Never
@mellow talon here
It sounds like you're looking for something similar to beartype (https://pypi.org/project/beartype/)
@brisk hedge just looking a decent approach (ideally without third party packages) to verify a given argument in a string matches what the argument is expecting
I still don't get the shtick of this package
it does "O(1) type checking" by only checking one random element of a nested collection, which sounds just... wrong
and unstable
i mean its obviously not perfect but i think heterogenous things showing up randomly where they should be homogenous is very rare
so it has to catch 99% of issues
I think I've had more instances where some little pieca shit creeped in into a list because of some edge case
but I haven't been taking notes of that
If you're passing an entirely wrong collection, that will probably come up during basic testing, in which case you can turn on some thorough checking anyway
fair
i mean imo runtime type checking is just a bad idea
but i think this has to be the best implementation of the idea
!pypi typeguard
this one also seems working, although last commit was in 2021
well, you gotta think of the children people who don't use type hints or a type checker
and provide some half-sensible error messages
for example, IIRC the creator of beartype is against static type checkers?..
oh they are?
lol
itd be weird to have this section if they were
the guy's style is a bit... unbearable
not a fan of the paw puns? ๐
maybe I'm misinterpreting this ๐คทโโ๏ธ
not tot say that it's an invalid opinion
it is certainly an opinion
How beartype can check that my custom collection contains only items of required type?
Or check that my lambda is actually returning bool and not a float?
Lmao. In this example fast C-function is wrapped in two slow python functions, one of them does nothing
it doesnt
it cant
well until it calls it
unless it introspects the bytecode
omw to solve the entscheidungsproblem
I think it is possible to look into consts of code of wrapped func, find there another code object, and then wrap it
Hmm. There is no difference between "Any" and "object" from beartype's point of view
I discovered beartype about a year ago and started using it everywhere (I even started writing a companion library that uses it). Now though I'm starting to wonder what the value really is in it? Type checking/introspection is something (IMO) that is for developers when writing the code. If for whatever reason the wrong type gets passed during runtime is it really better for beartype to blow things up for you cause it immediately knows the type is wrong when you call the function vs your code blowing up naturally because what you got was totally different than expected?
Funny enough, the question I was coming here to originally ask before seeing beartype was what is the value of protocols if something doesn't have to implement everything in it like a Java/C# interface? Isn't half the point that you know what's available?
they do have to implement everything ๐ค
Make several protocols implementing each combination of methods, all in a union
My understanding was that defining a Protocol class in newer versions was more for convenience for IDE/type completion, vs subclassing an ABC at runtime if you don't implement all of the abstractmethods python itself will yell at you
I may be wrong though
classes aren't really as easy to make than a dict or javascript object literal. You'd might as well just inherit from a base class with default implementations
i heard that it only checks one item of a collection, and that's a dealbreaker for me
See this is the part I don't understand about the PEP: https://peps.python.org/pep-0544/#protocol-members
from typing import Protocol
from abc import abstractmethod
class Example(Protocol):
def first(self) -> int: # This is a protocol member
return 42
@abstractmethod
def second(self) -> int: # Method without a default implementation
raise NotImplementedError
How is this different from just using ABC?
Python Enhancement Proposals (PEPs)
if i have type checking, i don't want it half-assed. give me all the guarantees.
you can inherit Example.first in a subclass, you cannot inherit second
but to be compatible with Example a class must implement the same interface ignoring the abstractmethod
and its different because abcs are (for typecheckers) concrete classes and protocalls are duck typed
the two however arent really that different at runtime
I'm confused now, if you have an ABC with a non-abstract method defined you can simply inherit that in a subclass can't you?
from abc import ABC, abstractmethod
class Parent(ABC):
def foo(self):
print("foo")
@abstractmethod
def bar(self):
...
class Child(Parent):
def bar(self):
print("bar")
c = Child()
c.foo()
c.bar()
Is the point of protocol that you could have some other class that only has foo and it's considered a Parent type?
If so, why would you want that?
yes
no
i must have explained something badly
No worries, I appreciate the help
I have a sense there's subtlies here that I'm not getting
protocols and abcs are basically the same thing at runtime (protocol's actually are abcs) but at type checking time type checkers dont know about the abc registry and so cant tell if one class implements the abc required to be safely passed into a function, protocols solve this by not using the abcclass.register method at all and rely on structural checks to see if a class is a sub type of a protocol
but i think strictly speaking protocols are structural and abcs are concrete and whatever the word for using the abc registry would be
Ok, so it sounds like this is for the type checkers/to not have to explicitly declare/subclass everything. Aka a nicer dev experience
yes ๐
Gotcha. I've previously come across blogs/articles talking about how "you don't even have to implement every method defined on the protocol class" which made me really go ๐ค cause what would be the point
it also handles the case of 3rd party code nicely (though I haven't used it for that, personally)
Python Enhancement Proposals (PEPs)
(namely the example about typing.Iterable / typing.Iterator needing to be subclassed from every time w/o this)
does anybody know if yield from iterable will be deprecated in the future?
no, why would it be?
I recall reading something like that, or maybe it was a suggestion
perhaps I misunderstood
it's very unlikely for any major language feature to get deprecated. One Python 3 was enough
also Im asking because apparently what most ppl use it for wasnt how it was intended to be used
yeah it was partly designed for cooperative coroutines, which are now done with async def. But it's still useful for replacing for ...: yield ..., and that's not going to go away
it's still useful when defining __await__
found it, so Im not very familiar with the async module but it says:
"Support for yield from was deprecated and removed so it should be removed"
if if you're using generator-based coroutines for something else, like a state machine
I think they're referring to asyncio no longer supporting yield from-based coroutines?..
definitely not removing yield from from the language
glad to hear that
I was confused when I first saw, but that makes sense ig
is this typeable some way?
from ctypes import py_object
from typing import TypeVar
T = TypeVar("T")
def to_pyobject(obj: T | py_object[T]) -> py_object[T]:
...
seems to be a fairly common pattern (wrap an argument with a container, if it isn't already that container)
do you say: "all the methods and functions are fully typed" or:
"all the methods and functions are fully type hinted"?
doesnt this work as is
from ctypes import py_object
from typing import TypeVar
T = TypeVar("T")
def to_pyobject(obj: T | py_object[T]) -> py_object[T]:
if isinstance(obj, py_object):
return obj
else:
return py_object(obj)
x = to_pyobject(py_object(1))
reveal_type(x)
main.py:14: error: Need type annotation for "x" [var-annotated]
main.py:14: error: Argument 1 to "to_pyobject" has incompatible type "py_object[int]"; expected "py_object[<nothing>]" [arg-type]
main.py:15: note: Revealed type is "ctypes.py_object[Any]"
Found 2 errors in 1 file (checked 1 source file)
works in pyright
maybe an overload will make mypy happy?
yeah overload works
from ctypes import py_object
from typing import TypeVar, overload
T = TypeVar("T")
@overload
def to_pyobject(obj: py_object[T]) -> py_object[T]:
...
@overload
def to_pyobject(obj: T) -> py_object[T]:
...
def to_pyobject(obj):
if isinstance(obj, py_object):
return obj
else:
return py_object(obj)
x = to_pyobject(py_object(1))
reveal_type(x)
main.py:23: note: Revealed type is "ctypes.py_object[builtins.int]"
Success: no issues found in 1 source file
result is an object with an attribute status that is either approved or pending. Is there some way to throw some lint error in VSCode when I do something like if result.status == 'abc' because result.status will never be abc, similar to how typescript does it
def verify_otp(code):
result = verify.verification_checks.create(to=to_phone_number, code=code)
return result.status
Hi, I have a decorator into a class, and I want to type hint it correctly
currently, I have :
_RT = TypeVar("_RT") # Type for Return Type
_P = ParamSpec("_P") # Type for Args
class MyClass:
@staticmethod
def as_cleaner(func: Callable[_P, _RT]) -> Callable[_P, _RT]:
"""A decorator to tell that a method should clean the cache before executing."""
@wraps(func)
def inner(self: Self, *args: _P.args, **kwargs: _P.kwargs) -> _RT:
self.clean()
return func(self, *args, **kwargs)
return inner
@as_cleaner
def some_method(self):
pass
but because I grab self from the functions args, I have to put it back into the func arguments
Then I have a type issue with the return func(self, *args, **kwargs) part
Is Self meant to be a MyClass (or a subclass) here?
in that case, typing.Self won't really work
You need to make a separate TypeVar for the self type, with the appropriate bound=, and annotate the decorator using typing.Concatenate[_SelfType, _P] isntead of _P
https://mypy-play.net/?gist=71f956be8b3aed71bd0c7d63b3161526
See for instance with mypy
ok I see, thanks
If MyClass has a Generic type, I should bound with MyClass[Any] ? (The typevar used in generic has no constraints)
yes
Can anyone help me with this grammar question pls
id say the former
From the documentation it would seem as if the only way to have exhaustiveness checking is by having the parameter be an Enum or its subclass.
https://typing.readthedocs.io/en/latest/source/unreachable.html#assert-never-and-exhaustiveness-checking
Are there other ways to check for exhaustiveness on a parameter when it isn't an Enum (or its subclass)?
use a literal?
you mean?
What cases do you mean when it's not an Enum?
If it's a union, assert_never should work as well
@dataclass(frozen=True)
class AI:
name: str
smartness: int
@dataclass(frozen=True)
class Human:
name: str
guest: bool
Player = AI | Human
def print_player(p: Player):
match p:
case AI(name, smartness):
...
case Human(name, guest):
...
case _:
assert_never(p)
Let's say you match on (int, str) You know that int is always between 1 and 5 and I have covered all of them, how would you handle it?
I actually realise that using a IntEnum is much nicer here.
Interesting
If it's something you know but the type checker can't prove, use assert False or raise AssertionError
ah
I think assert_never is kind of a kludge tbh...
if a branch is unreachable, type checkers theoretically should already know that
although I guess it does raise a runtime exception?..
True, if all the variants of an enum have been mentioned the type checker should be able to say that ok this is unreachable
I like to think that you are guaranteeing the type checker that it is what it is by pointing a gun to your foot and saying
"see, I am not lying now, trust me"
The reasoning here is a bit different: it's saying "please, check that it's unreachable"
I.e. without assert never, you won't get a typing error
Oh yeah that's right, stuff like when you return an object, or you just do a side effect
Exactly.
I meant the case where you have to put an assert_never because you've returned something non-None from all possible cases of a match, but mypy thinks you can still return None
or was it resolved?
I didn't mean to suggest that when writing that doc, maybe we should add a sentence mentioning other possibilities?
e.g. literals, unions, True/False
What would you call an indexed type? Like List[int] or Dict[str, str]?
generic?
ah yeah
!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...
what would you call the type "inside" of the generic?
you use type-vars there
I'm curious about the technical term, not the implementation or how you'd use it :)
I'm trying to write a comment describing a mypyc bug w/ __annotations__ preservation
Here's a quick example
from typing import TypeVar, Generic
T = TypeVar("T")
class Foo(Generic[T]):
def __init__(self, x: T):
self.x = x
A: Foo[int] = Foo(2)
I'd just call these the generic parameters or simply the type vars
In list[int], I'd say list is a generic type, int is a type parameter, and list[int] is a specialization of the generic type
Awesome, thanks!
are tuples of types a valid type object in python
Yup, that would be really helpful.
yes
no?
so if i want to typecheck a tuple with an int and a string, i can't do (int, str)?
no
i have to use NamedTuples?
tuple or Tuple
tuple[int, str]?
Yes
Tuple is deprecated since 3.9
but compatible with all stable versions
ye
if you're making a library, there are few good reasons to be incompatible with 3.9
it's okay to use Any anytime the cost of making sure the type checker knows everything is the right type is too high
for instance, with the union value type, if you do config["key"] + 1 you'll get a type error, because it could be str. so instead you'd need to do assert isinstance(config["key"], int); config["key"] + 1
repeat a couple times and you see how you're not really being helped by the type checker. in such cases, using Any effectively switches the type checker off and lets you go about your day
there's also a third option, which is to use a TypedDict
Generally, when working with foreign data, I would first parse it into something meaningful and typed. Otherwise you're trusting that the config file follows your assumptions
Especially if you have a fixed set of keys
so for example:
@dataclass(frozen=True)
class Player:
name: str
health: int
buffs: list[Buff]
def parse_buff(obj: Any) -> Buff:
...
def parse_player(obj: Any) -> Player:
...
@rustic gull yes, if you know the datatypes, parse into something else or use a TypedDict
but i'm lazy and i often just Any to switch the type checker off in cases where i don't feel like the additional safety really helps me
import pyautogui as pag
import keyboard
import time
from colorama import Fore, Back, Style
print(Style.BRIGHT + Fore.LIGHTRED_EX +
":: Click enter when your mouse is over the 'search' ::")
if keyboard.read_key() == "enter":
search = pag.position()
print(f"Cords captured: {search}")
time.sleep(1)
time.sleep(1)
print(Style.BRIGHT + Fore.LIGHTRED_EX +
":: Click enter when your mouse is over the Views Button ::")
if keyboard.read_key() == "enter":
views = pag.position()
print(f"Cords captured: {views}")
x=1
while x == 1:
if keyboard.read_key() == "f":
x += 1
pag.moveTo(search)
pag.click
time.sleep(1)
pag.moveTo(views)
pag.click```
For some reason the loop works great the first time and then the second time it makes me click any key on my keyboard to run the loop again.
How do I support 3.8 here ๐ https://github.com/ionite34/einspect/actions/runs/3856793832/jobs/6573449404
TypeError: '_ctypes.PyCArrayType' object is not subscriptable
I have a class that inherits and subscripts ctypes.Array, which apparently is not subscriptable in 3.8
import ctypes
from typing import TypeVar
_T = TypeVar("_T")
class Something(ctypes.Array[_T])
...
from __future__ import annotations moment
if TYPE_CHECKING. There's something about this in the mypy common issues docs
the future import doesn't help here because this is not an annotation
that is true i am blind
you mean like
wait but how? The ctypes.Array[_T] needs to be defined at runtime right?
if TYPE_CHECKING: _Base = Array[_T] else: _Base = Array
the typevar seems to break for typing purposes in that case
from typing import TYPE_CHECKING, TypeVar
_T = TypeVar("_T")
if TYPE_CHECKING:
List = list[_T]
else:
List = list
class Foo(List):
def get(self, i: int) -> _T:
return self[i]
ls = [1, 2]
f = Foo(ls)
reveal_type(f.get(0))
error: A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var]
note: Revealed type is "<nothing>"
whereas this was fine originally
from typing import TypeVar
_T = TypeVar("_T")
class Foo(list[_T]):
def get(self, i: int) -> _T:
return self[i]
ls = [1, 2]
f = Foo(ls)
reveal_type(f.get(0))
note: Revealed type is "builtins.int"
hm I guess it implicitly turns into List[Any]. Not sure how to solve that
py if TYPE_CHECKING: class Foo(Array[T]): ... else: class Foo(Array): ...
how do i create a type representing a tuple of int and str
value: tuple[int, str] = (123, "hello")
welp, doesn't seem to be a better way so
thanks
is there a way to type an exact class instance and not accept subclasses?
Like here I want to accept only an object with type is object
def fn(x: final(object)): ...
no
ExcatlyObject = TypeVar('ExcatlyObject', object)
def fn(x: ExactlyObject):```
this won't accept subclasses of object, only exact object instances
no
if I understand your question correctly
wdym
a TypeVar can't have only one constraint. even if it did, it would still accept subclasses
rats
im 0 for 2 today on type hinting
I feel like the docs are a little misleading though
it's confusing. the TypeVar has to be solved to exactly str or bytes, but after that you can still pass subclasses
python should have Exactly[bytes]
I feel like there's not really a good use case for that
theoretically any method that can operator on a bytes should also accept anything that subclasses bytes
since things that subclass bytes should share the same behavior as bytes (as far is relevant to a function that accepts bytes)
I guess I could see it being useful in some kind of serialization context where having extra data will break the remote system
like a proto rpc that accepts an exact set of parameters - no more no less
it's also difficult to make this type-safe; anything typed as bytes could not be safely passed to Exactly[bytes]
x: Exactly[bytes]
x = b'hello' # ok
x = bytearray() # err
def f1() -> bytes:
return f2() # ok
def f2() -> Exactly[bytes]:
return f1() # err
x = f1() # err
x = f2() # ok
# this would require a lot of changes in typeshed:
class bytes:
def split(...) -> Exactly[list][Exactly[bytes]]: ... # weird
def hex(...) -> Exactly[str]: ...
def title(...) -> Exactly[bytes]: ...
class str:
def encode(...) -> Exactly[bytes]: ...
class stdin:
def read(...) -> Exactly[bytes]: ...
class CodeObject:
co_code: Exactly[bytes]
...
``` This would require to change almost all annotations in typeshed (and other places) for almost no benefit
it also violates the liskov substitution principle
TIL
This just looks like
bytes cannot be a subclass of bytearray because it is not possible to implement mutating method (such as .extend) for immutable class.
LSP explains why bytearray cannot be a subclass of bytes.
So, bytes cannot be a subclass of bytearray and vice versa. This also applies to frozenset/set.
!pep 688
I was struggling a lot when i was literally implementing mutable and immutable point classes because i couldnt realise which class should be base. Now I realized that none of the classes can be base
why? strings has no buffer method?
Hi, how can I tell a method from a protocol to return a list of object that also match this protocol ?
I got this :\
https://mypy-play.net/?mypy=latest&python=3.11&gist=17236692ed530d07e0b6967a2fd21a04
Some random thoughts: ```py
TBool = TypeVar('TBool', Literal[False], Literal[True], default=Literal[True])
class MyMutableOrImmutable(Generic[TBool]): # True for mutable, False for immutable
# for mutables:
@overload
def mutate(self: Self[True]) -> SomeResult: ... # we can mutate mutable instance
# for immutables:
@overload
def mutate(self: Self[False]) -> Never: ... # but cannot mutate immutable instance
# or maybe something like this?:
@overload
def mutate(self: Self[False]) -> ThisDoesNotExist: ...
# or this or just leave only first overload without second, so typechecker will not be able to find correct overload for Self[False] and will throw error
mutable = MyMutableOrImmutableTrue
immutable = MyMutableOrImmutableFalse
mutable.mutate() # ok
immutable.mutate() # err
idk how typechecker can realise this, but it should work like this:
immutable = mutable # ok
mutable = immutable # err
static methods don't really make sense on protocols
a protocol describes how an object works, not its class
Why do you need this Protocol?
To assert a class we use respect a pattern
but I guess I should use abstract for this kind of things ?
oh yes it make also send like this
I don't really know when I should use protocols / abstract classes
ok didn't see this part in the doc, mybad
https://peps.python.org/pep-0544/#self-types-in-protocols
thx
Python Enhancement Proposals (PEPs)
If you have a pair of a class and an interface with the names I{X} and X or X and XImplementation, consider thinking why you need this interface.
Or whether you do at all
in newest mypy you also can use Self: https://mypy-play.net/?mypy=master&python=3.11&gist=69210a85186d6ef117edb237ca687678
why mypy-master is three times slower?
I will be able to create multiple implentations of the Language class (depending on what translator I should use)
but I want my type checker to tell me if a new implementation respect the things I need -> usage of protocols
i dont think you need classes for that. You can store language name in instance attribute
here there is juste the protocol + the actual implementation (btw, I should name LanguageProtocol, not LanhuaheImplementation)
I hide a lot of things in the exemple
How else would you implement a language?
I can use Self here ? because the available_languages staticmethod return new instances, not a list of itself ๐ค
well mypy don't complain
but it seems counterintuitive
it might have to git clone?
wdym ?
You can do something like this: py @classmethod def create_some_instance_for_me(cls) -> Self: ... Typechecker sees this: ```py
@classmethod
def create_some_instance_for_me(cls: type[T]) -> T: ...
.
Why it is gitcloning every time? I dont think mypy guys are commiting every second :)
I think it is because mypy-master is not compiled with mypyc, so it is slower
oh mypyc might be it
I think you might be introducing an interface prematurely. I would wait until you have another example implementation.
Or at least a conceivable second implementation for which there's a use case
Otherwise you'll make an interface for every class, and there's going to be 200 layers of indirection
I have a translate feature
and I will be able to use google translate, yandex, etc...
each translator will have their own classes, separeted in their own seperated files
atm, there is only 1 because it is under dev, that's all
Ah, I see
class LanguageProtocol(Protocol): ...
class Language: ...
class LeftToRightLanguage(Language): ...
class RightToLeftLanguage(Language): ...
class HieroglyphLanguage(Language): ...
class English(LeftToRightLanguage): ...
class Russian(LeftToRightLanguage): ...
class Chinese(LeftToRightLanguage, HieroglyphLanguage): ...
class Egyptian(RightToLeftLanguage): ...
Here I don't think Protocol is needed ๐ค does it ?
cause all are a Language subclass
yes
i still dont think you need protocol in your case because you can subclass same base class in every place
My language will be in seperated files, that should be independent of the rest of the code
So I "can't" have a common parent class imported in all the implementations
ofc I technically can; so yes maybe protocol is not needed
just define Language class in its own file and import it everywhere
But when I create a new Language (for another translator), I want the code to avert me directly if there is something wrong with it, that's all
but yes you're right, while I define all the differents implementations by myself, I don't need to use Protocol ๐ค
The use case of protocol is when it's "using" function / class that are already defined ig
I will maybe refactor some things
You will get all errors you need.
If you violate LSP, typechecker will say you that.
If you forgot to subclass Language baseclass, typechecker will give you errors in places where you use that new class.
Actually I think you have to explicitly turn it on.
In pyright it's reportIncompatibleMethodOverride
In mypy it is enables by default AFAIK
I dont think you even can disable it in mypy because it is very crucial part of typing system.
I tried pyright once and it was complaining about a lot of places in my code (when mypy was ok) so im using only pyright's LSP (language server protocol) for autocompletion
how do I type hint argument 2 of ctypes.cast
https://github.com/python/typeshed/blob/main/stdlib/ctypes/__init__.pyi#L138
stdlib/ctypes/__init__.pyi line 138
def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...```
_CastT is a TypeVar with bound _CanCastTo but it's a typeshed only class
What is typing.ClassVar?
is annotating a decorator like this OK?
def timer(function: Callable) -> Callable:
def wrapper(*args: Any, **kwargs: Any) -> Any:
return_value = function(*args, **kwargs)
return return_value
return wrapper
its not great
cause you lose all the type safety of the original function
you dont need to
you can use generics
P = ParamSpec("P")
R = TypeVar("R")
def timer(Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
return_value = function(*args, **kwargs)
return return_value
return wrapper
how to i create a function type with signature
something like ```py
def sort(iterable=(), comp: (a: Any, b: Any) -> bool):
...
but valid python code
Callable[[Any, Any], bool]
do you need the parameters to be named a and b?
Can you type that?
probably no
class Foo(Protocol): def __call__(self, a: Any, b: Any) -> bool: ...
that should work with all callables right
for expressing the type? yes, pretty much
can i express more complex signatures using generic callable
or i have to use protocols
Callable only supports positional-only arguments, ParamSpec, and "accept whatever" (with ...)
so yes, in general you need protocols when you want a more complex signature
in practice callbacks usually only take positional args though
there was a pep that would actually make the syntax you used valid but it got rejected :)
!pep 677
still salty about that
hi, i've an enum that looks like this
class ClientEvent(Enum):
ready = "ready"
and a dict of type events: Dict[str, List[CoroFunc]]
somewhere in my code i'm doing events[event.value] = [func]
but this results in a pyright error because Enum.value returns Any while the dict only accepts str as keys,
is there a way to type hint it as str? (note: i can't subclass str, Enum on the enumeration class)
preferably without modifying the type of the dict
how
what error are you getting?
the fact is that i'm also overriding a variable with another variable that can only be Union[str, Any] (the Any is taken accessing the enum member value) but pyright it's taking the type of the first variable, tough the key assignation is below the variable override
can you show the full code?
Can you give a minimal reproducible example?
Type "AnyEvent | str" cannot be assigned to type "str"```
where `AnyEvent` is an union of enums
im assuming you have events = {} but im currently not entirely sure
ok
def add_listener(self, func: CoroFunc, name: Optional[Union[str, AnyEvent]] = None) -> None:
name = (
func.__name__
if name is None
else name
if isinstance(name, str)
else "on_" + name.value
)
...
self.extra_events[name] = [func] # this is the line from where it's originating the error; type(extra_events) = Dict[str, List[CoroFunc]]
do you explicitly type self.extra_events as dict[str, list[CoroFunc]]?
yes
well i dont see how youre getting that error then
name cannot be anything other than str there
That's not a reproducible example since some types are not defined. Can you write your code in the pyright playground and then share the link with us?
https://pyright-playground.decorator-factory.su (pyright playground)
i can't paste all my code on pyright playground, it's too long
A minimal example is preferable
I.e. the minimum amount of code needed to produce the error you see
Looks like if you just add a if name is not None: check, those errors go away.
The type of name then just becomes str | Unknown
i think you need to rename name = ( to name_
so this not a problem with my typehints
I see you already have a None check, not sure why it didnt pick that up
Good call
because its reassigning name which is str | None | AnyEvent and it then doesnt get renarrowed
but before adding AnyEvent it was named name = (... and pyright wasn't raising any error
Where is it documented that tuples get unpacked in this context?
!e ```py
from typing import Literal, get_args
print(get_args(Literal[[1, 2, 3]]))
print(get_args(Literal[(1, 2, 3)]))```
@summer berry :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | ([1, 2, 3],)
002 | (1, 2, 3)
i don't know where it's documented, but note that it's how subscript works
!e
import ast
print(ast.dump(ast.parse("x[(1,2)]")))
print(ast.dump(ast.parse("x[1,2]")))
@hallow flint :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | Module(body=[Expr(value=Subscript(value=Name(id='x', ctx=Load()), slice=Tuple(elts=[Constant(value=1), Constant(value=2)], ctx=Load()), ctx=Load()))], type_ignores=[])
002 | Module(body=[Expr(value=Subscript(value=Name(id='x', ctx=Load()), slice=Tuple(elts=[Constant(value=1), Constant(value=2)], ctx=Load()), ctx=Load()))], type_ignores=[])
It'd be the same reason why x = 1, 2 produces a tuple, the parenthesis are redundant?
in general, , is used to create tuples.
the parentheses are only needed when creating an empty tuple
it's convention to use them for clarity (as regular grouping), but they're not strictly necessary
Here's the bit in the docs about it:
https://docs.python.org/3/reference/expressions.html#subscriptions
If the expression list contains at least one comma, it will evaluate to a tuple containing the items of the expression list. Otherwise, the expression list will evaluate to the value of the listโs sole member.
Thanks
Similarly here's the __setitem__ mention:
https://docs.python.org/3/reference/simple_stmts.html#assignment-statements
An assignment statement evaluates the expression list (remember that this can be a single expression or a comma-separated list, the latter yielding a tuple) and assigns the single resulting object to each of the target lists, from left to right.
I was thinking that they're separate arguments rather than a tuple at first
That would be nice but unfortunately it's too late for that.
!pep 637 for a related proposal
is there a difference between
A = str | int
and
A = TypeVar('A', str, int)
?
they are quite different. The latter is only needed in some fairly obscure cases
what's the difference?
str | int is a union, a type that can be either str or int. For example, a function that returns str | int may return either a str or an int.
The TypeVar is called a TypeVar with constraints. TypeVars in general are used for generics, functions or classes that broadly support multiple types, where the same type you put in is the one you get out. For example, a function make take an argument of type A = TypeVar('A', str, int) and also return A, and this means that if you pass a str, you also get a str out
isn't this similar to overloads?
yes, it works in nearly the same way
the only difference between the two is that overloads are covariant whereas constraints erase to be invariant in returns eg ```py
identity: (AnyStr in (str, bytes)) -> AnyStr = lambda x: x
reveal_type(identity(StringSubclass())) # type is str
that and they are really annoying to solve in mypy
wouldn't you see the same behavior there if you had overloads (str) -> str and (bytes) -> bytes?
oh wait yeah you would
I think the behavior if you pass Any may be different in some type checkers, e.g. pyright will just pick the first overload if the arg is Any
In the init method of AttributeProperty, why does MyPy say that value_type and default_value are incompatible types for the superclass's init method? It seems to compare each of the constraint types to TAttr instead of just seeing that it is Optional[TAttr]
from typing import TypeVar, Generic
T = TypeVar("T")
TAttr = TypeVar("TAttr", int, float, bool, str)
class Property(Generic[T]):
def __init__(self, value_type: Type[T], default_value: Optional[T] = None) -> None:
super().__init__()
self.value_type = value_type
self.default_value = default_value if default_value is not None else value_type()
class AttributeProperty(Property[TAttr]):
def __init__(self, attr_name: str, value_type: Type[TAttr], default_value: Optional[TAttr] = None) -> None:
# MyPy complains that both value_type and default_value are incompatible types
super().__init__(value_type, default_value)
self.attr_name = attr_name
Make AttributeProperty generic as well.
I don't think that's necessary
Hm...
pyright doesn't complain
should it be covariant or something?
mypy typechecks functions that are generic over a TypeVar with constraints by checking it once for every constraint
I guess that's interacting poorly here with the inheritance from another generic class
probably a bug, but general advice is that TypeVars with constraints are weird and you should probably avoid using them
Hello, I need a Literal with all the ISO 3 letters country codes, my naive solution is just
COUNTRY_CODES = [country.alpha_3 for country in pycountry.countries]
class ...:
country: Literal[*COUNTRY_CODE]```But apparently star expressions aren't allowed in subscriptions. How could I work around that?
they are in 3.10+ but youre out of luck anyway cause this wont work
typecheckers (unless its pyanalyze) wont be able to figure out the type of country at type time
I don't really mind, it is for pydantic
according to intellij, it isn't supported in 3.10
PEP 646 added that, right?
until then you van use Literal[tuple(COUNTRY_CODE)] at runtime or for pyanalyze
noted. Bounding the typevar to a union here seemed to work fine TAttr = TypeVar("TAttr", bound=int | float | bool | str)
hello, i have an typealias py EventListenerCallbackT: TypeAlias = typing.Callable[[Event], typing.Awaitable[typing.Any]] that i use for typing function passed to a decorator ```py
def listener(
self, event: type[Event], *, max_trigger: int | Undefined = UNDEFINED
) -> typing.Callable[[types.EventListenerCallbackT], EventListener]:
def decorator(callback: types.EventListenerCallbackT) -> EventListener:
self.event_handler.add_listener(
lsnr := EventListener(type=event, max_trigger=max_trigger, callback=callback, bot=self)
)
return lsnr
return decorator```
but this doesnt seem to work when i annotate the first argument of the function with a subclass of Event py @bot.listener(wyvern.StartedEvent) async def foo(event: wyvern.StartingEvent) -> None: print("Starting bot...")
is this behaviour intended? if so, what's the reasons and are there any workarounds other than type ignoring?
Argument of type "(event: StartingEvent) -> Coroutine[Any, Any, None]" cannot be assigned to parameter of type "EventListenerCallbackT"
Type "(event: StartingEvent) -> Coroutine[Any, Any, None]" cannot be assigned to type "EventListenerCallbackT"
Parameter 1: type "Event" cannot be assigned to type "StartingEvent"
"Event" is incompatible with "StartingEvent"PylancereportGeneralTypeIssues``` this is the error by pyright
Can we use a dictionnary keys as Literal for argument ?
MyDict = {"abc": 1, "def": 2}
Literal[MyDict.keys] # not this syntax, but something like this
# would be equiv to
MyType = Literal["abc", "def"]
Seems like a missed opportunity that this doesn't exist for TypedDicts
will a typing.Exact be possible? Currently the virtual subclasses get in the way of some valid type overloads
from typing import overload
@overload
def fn(x: float) -> float:
...
@overload
def fn(x: int) -> int:
...
def fn(x):
if isinstance(x, float):
return x / 2
elif isinstance(x, int):
return x // 2
raise TypeError
mypy:
error: Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader
Yes this is expected and no there's not really much you can do about it
With an intersection and a not type yes this would work
Hey guys, I'm not sure if this is the correct place, but here goes. I was trying to create a way to initialize settings (from yaml file) and make them easily available in the project. I was struggling to make it clean and to be sure that happens only once. I started discussing the matter with ChatGPT and it have me a nice idea:
TSettings = TypeVar('TSettings', bound=Dict[str, Any])
SETTINGS: TSettings = None
SETTINGS_INITTED: bool = False
def init_settings() -> TSettings:
global SETTINGS, SETTINGS_INITTED
if SETTINGS_INITTED:
return SETTINGS, SETTINGS_INITTED
if sys.implementation.name == 'cpython':
basis = sys.argv[0]
config_path = Path(os.path.split(basis)[0]) / "data/config.yaml"
else:
basis = sys.executable
config_path = Path(os.path.split(basis)[0]).parent / "config.yaml"
with open(config_path, "r") as f:
SETTINGS = yaml.safe_load(f)
SETTINGS_INITTED = True
return SETTINGS, SETTINGS_INITTED
SETTINGS, SETTINGS_INITTED = init_settings()
The problem is I'm not sure how this works - I can import SETTINGS in any module and it works great, but can someone explain the way TypeVar and bounding make it all happen? I've never used that before. Also any suggestions to improve that would be appreciated ๐
Although you can always just use a bound type var for this use case
Exact[T] is not type safe. Read this: #type-hinting message
I mean it doesnt fit nice in current type system.
It wouldn't work with 99% of existing code. To support it, we should change almost every annotation everywhere, otherwise you won't be able to use Exact in your code.
Exact[str] would be compatible with Literal[str] and LiteralString wouldnt it?
and to narrow to Exact[T] all youd need to do is assert type(x) is T
this made me realize that the Literal PEP specifically calls out that Literal[0] and Literal[False] are different (even though False is an int with value 0). Interesting edge case
But is False is 0?
The docs mention a typecodes attribute on arrays.
https://docs.python.org/3/library/array.html#array.typecodes
But it doesn't actually exist?
#bot-commands message
it's array.typecodes where array is the module, not the type
ah dammit, sorry
yeah giving the module and the type the same name isn't a great idea
the context is confusing as well
array.append(x) so your mind goes "obviously array is the type"
yeah, probably worth opening a docs issue on cpython to see if we can improve this
unfortunately renaming the module or the type isn't going to be an option
I think moving the typecodes above the class is going to help
what do you think?
yeah maybe, one issue is that array.array is clearly the most important thing here, so you'd want it to be first
but then how do you make sense of this context?
I just noticed you can do the following in python 3.8 with future imports, is this a bad idea?
if TYPE_CHECKING:
PrimitiveTypes = str | int | float | bool | None
yes, just use TypeAlias and quote the values
yeah, use TYPE_CHECKING sparingly
I have a decorator like :
def inner(_: Callable[..., C]):
def cog_getter(stuff) -> C:
other stuff
p = cast(C, property(cog_getter))
return p
as you can see, my decorator also act like a property, but I don't know how to type it
by forcing the return to be C, it is "fine" but is there a way to tell that the return type is a property while conserving the fact that this property return C ? Like a Property[C] or anything
I could also do :
@property
@my_decorator()
But I prefere having only 1 decorator, because here the order is important between decorators
no property isnt type checkable directly
youre kinda just screwed lol
see https://github.com/python/typing/issues/985 for more sadness
I forget the most important part ๐
oh :
that's annoying
well, I will use my "force type" option then, it is the closest to what I want
Something like this was almost good, but the type C seems not bounded to anything after that
def inner(_: Callable[..., C]):
@property
def cog_getter(stuff) -> C:
other stuff
return cog_getter
i think your best bet is just saying this is a function that returns a callable with a return type of C
i doubt that its that important to use the property's fget/set/del methods is it?
I have to admit that here, the only goal is to remove the ()
the property here act like an attribute but the instances can change
but yes, maybe the best option to have clean types is to use a classic function
def inner(_: Callable[..., C]) -> C:
return cog_getter # type: ignore
```id just do this
return property(cog_getter) # type: ignore ?
could do that if you wanted
I mean, here you return cog_getter assuming it is type C; I don't get it
when you access the property on an instance its type is C right?
wow ok I get it
lying to the typechecker cause stuff isnt typeable and losing a bit of information is perfectly fine
although saying that i think you probably could make this type check as a property
but its not worth it
yes right
thank you
It's my understanding that the proper return type hint for inheritance-friendly alternate constructors is 3.11's typing.Self . How was this done before 3.11?
typing_extensions.Self
or a bound TypeVar, the Self PEP explains how to do this
is there a way to type hint a class attribute and an instance attribute as different things
from ctypes import Structure, c_ssize_t, py_object
class PyObject(Structure):
ob_refcnt: int
ob_type: object
_fields_ = [
("ob_refcnt", c_ssize_t),
("ob_type", py_object)
]
like here accessing the class var PyObject.ob_refcnt -> _ctypes.CField
but the instance ob_refcnt -> int
Maybe annotate it as custom descriptor that returns different things in different cases?
is that a thing? (type hinting descriptors)
Yup! I've had great quite some success with it
Works nicely at least in Pyright
class CField:
@overload
def __get__(self, obj: None objtype: Optional[Type] = None) -> Self:
...
@overload
def __get__(self, obj: object, objtype: Optional[Type] = None) -> int:
...
def __get__(self, obj: Optional[object], objtype: Optional[Type] = None) -> Union[Self, int]:
... # Your implementation
@runic sleet See above
is there any way to appease pyright to mitigate the warning of df["col"].tolist() # warning: tolist() not a member of None where df is a pandas DataFrame and I suppose the [] operator yield typing.Optional[list] or similar?
you cab use df["col"].tolist() instead of df["col"]
you can also use the .notna() method to filter out the missing values before calling the tolist() method like this
df["col"][df["col"].notna()].tolist()
hope this helped
Some reason you need it to Be an actual list?
I think the problem is that pyright thinks df["col"] could be None
ye
Sure. I was asking why s/he wanted to convert the column to a list at all
I perhaps could have picked a better example but my question was how to appease pyright/hinting that the thing isn't None as I run into this quite a bit
I could do like a = df.col if a: b = a.tolist()
but that's a lot of typing overhead
we use a helper function ```python
def not_none(obj: Optional[T]) -> T:
if obj is None:
raise TypeError("object is unexpectedly None")
return obj
so you can use not_none(df.col)
b = [] if a is None else a.to_list() ?
Is this how you use multiple TypeVars?
def map_async_ordered(func: Callable[[T], T2], it: Iterable[T]) -> list[T2]:
from threading import Thread
output = {}
threads = []
for idx, item in enumerate(it):
# create a function that saves the output to the list
thread = Thread(target=lambda: output.__setitem__(idx, func(item)))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
return [output[i] for i in range(len(output))]
yes
yes
yes
Is it valid in Python 3.10 and beyond to use: [str] instead of list[str] as type hint?
no
Okay, thank you. And for what [str] stands for? The parser doesn't complains.
nothing, the parser doesn't really check annotations
Hmm, I mean the type-hint "parser"...
If [str] does not mean "a list of string" - what does it mean?
List expression not allowed in type annotation
ย ย Use List[T] to indicate a list type or Union[T1, T2] to indicate a union type```
Pyright will give you an error if you try that
Thank you!
Python's own parser just parses annotations as expressions and allows any expression. You need a static type checker (like mypy or pyright) to find issues like this one
P = ParamSpec("P")
T = TypeVar("T")
class Test:
def __init__(self, func: Callable[P, T]) -> None:
self.func = func
def do_call(self, *args: P.args, **kwargs: P.kwargs) -> T:
return self.func(*args, **kwargs)
How to type something like this ? Because in do_call, P has no "meaning"
Is there an other way than using Generic ?
is there a reason you are avoiding Generic?
Because I will rarely know what the ParamSpec are when typing with Test
so it will almost always be Any
But it make sens that Generic is needed
is that a problem?
you might be interested in
!pep 696
In fact not really, but I added the types by after, so I will need to add [Any] everywhere in my code
but it's not the end of the world
you shouldnt need to add any default for it
Any is the implicit default
well in this case its ... but w/e
if I add Generic[P] in Test()
Maybe it's juste because pylance is in strict mode but it complains
this will be interesting yes
yeah thats just strict
you can use pep 696 atm in pyright if you want to silence the error in one fell swoop
how ?
import ParamSpec from typing_extensions
oh right
and use it with the default argument
thx
lmk if you run into any issues or have any other questions about it (im the author)
it's fine
ig this is intentional (but don't get why) so I added a default to my TypeVar also
by defining a T return type after the ParamSpec
I think I guess why
so it's fine
Change the order of the types in the Generic, so the paramspec comes second.
My func is typed Callable[P, T] so P come first in any cases
But itโs ok to set a default value for T also
No, the important order is what's in Generic[] or Protocol - that defines the order typevars are bound when you subscript the class.
So if it was Generic[T, P], YourClass[int, [str, bool]] would then work, which would allow T to not have a default.
In fact I do this so thatโs why ig
MiscCommandCallback will need 2 args also and here P need to come first
are protocol same as abc?
๐ก Here's my FREE 7-step guide to help you consistently design great software: https://arjancodes.com/designguide.
When should you use protocol classes vs abstract base classes? Here's an example where I use both, talk about the trade-offs and give you a suggestion of when to use each of them.
The code I worked on in this video is available her...
PEP 702
hmm yeah
you'll have to add the warning manually to consider non-typing users and dynamic usage
I suppose you could make another decorator like @enforce_deprecated which you'd put on top
I'm using python3.11 with the latest release of MyPy. I'm using a 3rd party library from pip for a project of mine. This library uses the PyRight comment syntax # type: ignore -- Actual comment here instead of the MyPy / PEP 484 syntax # type: ignore # Actual comment here and thus results in this MyPy error:
error: Invalid "type: ignore" comment [syntax]
From what I can tell, it seems like MyPy treats comment errors as syntax errors.
Methods I've tried include:
- adding
ignore_errors = trueto thepyproject.tomlfile - using the
--follow-imports silentand--follow-imports skipflags - adding
# type: ignoreto every import of said 3rd party library
I would really appreciate it if someone could help me with this. It's really annoying to see 11 "syntax" errors in my project.
im pretty sure you cant do anything about this
yeah, you could report it to the project
you could switch to pyright but i feel like you should probably just report it
So when using MyPy I can never use libraries that use PyRight?
that sucks
guess ill have to switch 
maybe you should actually report this to pyright
that it doesnt warn you that this is invalid
id have to report it to mypy then
it's not that mypy shouldn't warn you, it's that pyright should
but you could make a report on mypy asking for a flag to disable this check
i dont think disabling this is possible
just cause it should be using the ast module
yeah
in that case yeah, pyright should certainly have a warning shown
How should I specify a callable that can take in any arguments, but return specifically only some Foo type? Doing Callable[Any, Foo] doesn't seem to be working since it expects a list there
Callable[..., Foo]
ohh thanks!
and would it be possible to somehow say what the first argument is, but ignore the rest?
I suggest to use protocols in such cases
I've seen Callable[Concatenate[Something, P], Foo], but I can't use a ParamSpec here since it's just for a variable, and putting ... here doesn't seem to work
Why can't you use a paramspec here?
it's just for an inline annotation like:
x: Callable[...] = ...
typevars or paramspecs can only be used in functions, you'll get an error saying that it's meaningless in this context or something like that
you'd get the same error with functions if you don't have paramspec in your return type
so like def foo(x: Callable[P, R]) -> R wouldn't work, but def foo(x: Callable[P, R]) -> Callable[P, R] would
I guess a protocol would work..
but that's super annoying
Concatenate should really support ...
Makes sense.
class MyCallback(Protocol):
def __call__(self, stuff: VerySpecificStuff, /, *args: Any) -> Foo:
...
I think we added at least runtime support for Concatenate[T, ...]
can't guarantee that your type checker will approve
how would I typehint the ???:
from typing import Collection, Optional, Any
class ValueNotAllowed(Exception):
def __init__(self, cls: '???', value: Any, contains: Optional[Collection[Any]] = None, name: Optional[str] = None) -> None:
...
def valid(cls: '???', value: Any, *, contains: Optional[Collection[Any]] = None, name: Optional[str] = None) -> '???':
try:
v = cls(value)
assert contains is None or v in contains
except Exception:
raise ValueNotAllowed(cls=cls, value=value, contains=contains, name=name)
return v
print(valid(int, '1')) # 1
print(valid(str, 6, contains=['test', 'yes', '6'])) # '6'
print(valid(str, 'z', contains=['a', 'b', 'c'])) # raise```
I would say def valid(cls: Callable[[T2], T1], value: T2, *, contains: Optional[Collection[T1]] = None) -> T1: ...
where T1 and T2 are both TypeVars
oh yeah that makes sense, thanks!
i have
def foo(*, spam: Optional[int] = None, ham: Optional[int] = None, eggs: Optional[int] = None) -> str:
...
if i wanted to make it so specifying spam and eggs is fine and ham and eggs is fine but specifying both spam and ham isn't fine, would i do
@overload
def foo(*, spam: int, eggs: int) -> str:
...
@overload
def foo(*, ham: int, eggs: int) -> str:
...
or do i need to specify all 3 in the overloads?
that looks fine
how does it know which foo to select?
well, if you call with foo(ham=2, eggs=1), the first overload doesn't match, so it checks second which matches
if you were to call foo(ham=2, spam=2, eggs=5) you'd get an error, because none of the overloaded implementations matched
even though on runtime, this kind of call would actually work, since the real implementation of the function can take all of these
but the type checker would complain
so, the type-checker simply goes over all of the defined overloads, and compares their signatures with what was passed in
is this how you type hint an expections/tuple of exceptions?
def retry_on_failure(exceptions: type | tuple[type, ...], debug_str: str) -> Callable:
def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def wrapper(*args, **kwargs):
while True:
try:
rv = func(*args, **kwargs)
except exceptions:
print(debug_str)
else:
return rv
return wrapper
return decorator
exceptions: type[Exception] | tuple[type[Exception], ...]
Also, Callable is lacking a lot of information
C = TypeVar("C", bound=Callable)
def retry_on_failure(exceptions: tuple[type[Exception], ...], debug_str: str) -> Callable[[C], C]:
def decorator(func: C) -> C:
@functools.wraps(func)
def wrapper(*args, **kwargs):
while True:
try:
rv = func(*args, **kwargs)
except exceptions:
print(debug_str)
else:
return rv
return wrapper
return decorator
I've never seen a typevar representing a callable, but this is pretty cool, Thanks!
typing aside, you might want to add some delay and not just hammer the function again and again
the delay is already embedded in another wrapper around requests.get ๐
but yeah, I should organize it better
btw what is this warning: Cannot find reference '|' in 'Callable' ?
I get it from the paramter call_on_failure
def retry_on_failure(exception: type[Exception] | tuple[type[Exception], ...], max_tries: int,
debug_str: str | None = None, call_on_failure: Callable | None = None
) -> Callable[[C], C]:
who's issuing the warning?
and what is your python version?
3.10
guess
its pycharm ofc
but when I do: Optional[Callable] = None then the warning goes away
well, pycharm's typing facilities are pretty questionable IMO ๐
yeah very qustionable
I've been finding bugs everwhere lately
I wonder if its possible to change linter with Pycharm
I think there's a mypy plugin
I just use VSCode because Pylance is amazing
is Pylance exclusive to VSC?
from my experience mypy is just way to strict, I wonder if anybody here is able to get the green check on a fairly big Python project
Pylance is a vscode extention, it uses pyright for type checking which should be possible to get working with any lsp compatible editor.
yeah I don't think pycharm supports LSP ๐คก
mypy can be configured to gauge the strictness to your desires
If you're attaching any linter or static analyzer to any existing project you will inevitably get red squiggly lines all over the code
No it does not :((
And adding it in is nontrivial.
omg, a talking oatmeal
More talkative than you expect.
is it possible to type hint collections.Counter?
it's just collections.Counter. though probably use from collections import Counter so it's less annoying
i mean like
def foo(x: str) -> Counter[str]:
...
something like that
does Counter implement __class_getitem__?
thx
how do you typehint the return of a coroutine function?
is it like Callable[[args], Awaitable[return]]?
yes
๐ is there a way to run compiled mypy tool in an "eager" mode (non-compiled). I want to add some debug prints to the python code...
you can install with --no-binary mypy to get a pure-Python mypy
Thank you! We have quite an elaborated install machinery in bazel.... and I was hoping for a flag instead of switching back and forth between two versions (which takes time).
I think both .so files and .py files are shipped in the compiled version.... so it seems like there should be a toggle ๐
that would require some dark magic in the import system I think
No toggle but it's quite easy to write a small tool that renames compiled binaries back and forth which is not going to be time consuming
I tried deleting all .so in the folder and it leads to a crash that I didn't quite understand where it came from...
By design the import system prefers compiled modules over pure Python modules. You'd have to tweak this preference for only one package probably.
ok thank y'all for the ideas!
how do you typehint keyword arguments with Callable?
color_select: Callable[[discord.User | discord.Member], ColorSelect]
card.color_select(player=self.player)```
Expected 1 more positional argumentPylance
can you show full code snippet?
well I mean thats basically all thats important
the typehint is in the parent class and some subclasses have a color_select method
could be just me, but I don't follow from the code you showed ๐
card can be either parent class or subclass
is whatever you're typing annotating with the color_select type alias a method? did you include self?
whenever I include Self it starts saying theres 2 more expected positional
so I think its bc I use player=self.player instead of solely self.player
but I wanna use keyword to have it remain clear
by the way, how do I typehint a Callable that takes in atleast 1 argument that I know the type of and 0 or more arguments that I dont know the type of
You need to use a callable protocol
can you give me an example
class AtLeastOne(Protocol[T]):
def __call__(self, the_argument: T, /, *who: object, **cares: object) -> Any: ...
technically i think as Jelle mentioned Callable[Concatenate[T, ...], Any] should work at runtime but afaik no type checkers actually support it
you need to do a similar thing for kwargs in callables
how would I typehint it wihtout actually defining it?
_I = TypeVar('_I', bound=discord.Interaction)
callable_with_interaction: Callable[[_I], bool]``` callable_with_interaction has to be a method that has a discord.Interaction param and possibly other paramaters that I dont care about here
I dont htink this is correct
tbh I dont really know what bound does but github copilot suggested this and it fixed pylance errors
this doesnt meet the needs of your original question
the only way you can do this is with the code i showed
oh well this is what I need, mustve asked incorrectly
or some form of it at least
oh wait so I could do callable_wiht: AtLeastOne
but a function of (interaction, something_else) -> Any wont be able to pass as callable_with_interaction
how do I make it pass lol
no
have you read the documentation for them?
;-;
could you give me an example of the answer so I can try and figure out why that is the answer
id like to explain it
but having a grasp of what a Protocol does in the type system is key to understanding it
alright, well I dont have a lot of time right now so I guess ill do that soon then
is there a way to type PYFUNCTYPEs for mypy
from ctypes import PYFUNCTYPE, py_object
binaryfunc = PYFUNCTYPE(py_object, py_object, py_object)
class Foo:
nb_add: binaryfunc
error: Variable "binaryfunc" is not valid as a type [valid-type]
I think you can do one of the:
- add
# type: ignore[valid-type]on thebinaryfunc = ...line - tell explicitly that this is a TypeVar with:
binaryfunc: TypeAlias = ....
The problem is that PYFUNCTYPE() is not a valid type annotation, so mypy doesn't understand that. It'd really need a specific plugin to handle Ctypes, since it generates lots of types dynamically. You might be able to use if TYPE_CHECKING to declare binaryfunc = PYFUNCTYPE, if that's a class.
Yeah, calls in general are not valid annotations
with mypy, how would i write a protocol with an open() method that accepts "rb" and returns a buffered reader? this is what i've written: py class _Openable(Protocol): def open(self, mode: Literal["rb"], /, *args, **kwargs) -> io.BufferedReader: ... and ive tried using it to match pathlib.Path and importlib.abc.Traversable, but both of them result in an error of the form: py Argument 1 to "update_hash_from_file" has incompatible type "Path"; expected "_Openable" [arg-type] Following member(s) of "Path" have conflicts: Expected: def open(self, Literal['rb'], /, *args: Any, **kwargs: Any) -> BufferedReader Got: ... # omitted other overloads @overload def open(self, mode: Literal['rb', 'br', 'rbU', 'rUb', 'Urb', 'brU', 'bUr', 'Ubr'], buffering: Literal[-1, 1] = ..., encoding: None = ..., errors: None = ..., newline: None = ...) -> BufferedReader
ah nevermind, my *args and **kwargs were conflicting with it
right, your protocol indicates that the object must accept arbitrary args and kwargs
yea that sounds right, i had recently written some mixins with *args **kwargs and instinctively applied it here thinking my protocol would be the subtype
Q&A time!
What are some patterns related to type annotations you wish more people knew about/would use?
I'd like to add some to my typing-tips page.
Some of my initial thoughts:
- Callable protocols, they come in handy when you want to name the parameters of the callable, or make it generic.
class SetPosition(Protocol):
def __call__(self, x: int, y: int) -> None:
...
- Sum type
@dataclass(frozen=True)
class Keys:
up: frozenset[str]
down: frozenset[str]
@dataclass(frozen=True)
class ChatMessage:
message: str
@dataclass(frozen=True)
class Rotation:
radians: float
InputEvent = Keys | ChatMessage | Rotation
do you already have something telling people not to use tuple[int] unless they really want a one-element tuple?
I think that's not a pattern, but yeah I've seen that. I'll remember to add this
and avoid using TypeVars with constraints
which I guess is an antipattern instead of a pattern ๐
You mean the AnyStr and such?
yeah those are kinda strange
yes, that's a TypeVar with constraints
Hmm actually, does this kind of thing not get caught eventually?
When you try to return an arbitrarily sized tuple
or pass it in
in many cases yes, but what if you're writing a stub file or annotations for a library?
ah
true
I've seen a lot of different wrong annotations, like callable, (int, str), etc.
dict[K: V] is another one
those would all immediately result in an error if a typechecker sees them though. The tricky thing about tuple[int] is that it's a correct type annotation, it's just probably not what the user wants
(because why would you want specifically a one-element tuple)
Maybe I should have one list of small things like these?.. not sure
"TOP 20 times PYTHON programmers got OWNED by TYPE HINTS, with FACTS and LOGIC | types DON'T CARE about your FEELINGS"
Make a 20 common mistakes while type hinting python blog and get to the top of hn and lobsters.
- using type hints
hey @oblique urchin I made a bit of a draft:
https://decorator-factory.github.io/typing-tips/patterns/callable-protocol/
do you think this format could work?
those would all immediately result in an error if a typechecker sees them though
Unless the type checker is PyCharm ๐คช
Does anyone know where I can ask a question regarding a syntax error? Have been at it for days, have multiple sources of resolutions but none seem to work ๐
Have you seen #โ๏ฝhow-to-get-help?
Thank you so much! ^
@trim tangle I've posted it in there! #1064995200080040026 message ๐
yes! feedback:
- naming arguments is risky because pyright at least will enforce that the parameter names match (unless you make them pos-only)
- the Endomorphism example felt hard to understand. I don't have a concrete improvement suggestion though
That's the correct behaviour though, because a user of this type can call them by names. But true, I think it deserves clarification
I think I'll defer this issue to a separate article because it's quite complicated
in practice a likely result is that your callback protocol won't work for types that look like they should work
because even if the param names are meaningful, you probably still call your callback with positional args
I think I'll also defer this to an article, maybe something like "Why doesn't my perfectly fine function match my callable protocol?" in the FAQ section
actually it's similar to changing parameter names in subclasses
mypy lets you do that
which is technically unsound but perhaps more useful practically
or maybe it's just for protocols?
hmm, maybe it's a remnant of the time when we didn't have /?
I updated the article
Is there a reason why None is used everywhere for typing the unit type instead of NoneType?
Ok - I just found something in pep-484
When used in a type hint, the expression None is considered equivalent to type(None).
I guess considering the practicality its enough reasons for me.
at the time NoneType didn't even exist under an accessible name
I only learned of this fact a few days ago
I was trying to add None to an isinstance call, but I guess that is not idiomatic anyway
i dont understand the first and third points in the bullet list
is the first one meant to be about not having named parameters..?
anyone aware of examples on how to type dataclasses in such a way that specific attributes are managed by a descriptor that validates/converts such that init parameters have more avaliable types than what wil lbe stored and returned from the descriptor?
I think it's an open problem in dataclasses/attrs
ye
and the third one... I'll have to add some more stuff
Attrs has a converter option for this purpose, but it's not well supported by checkers. Mypy can have issues if the parameter is a generic type, and pyright doesn't support it at all since it's not part of @dataclass_transform.
ye pretty much
The problem IIRC is that to determine that mylist: list[str] = attrs.field(converter=list) should have a type of Iterable[str] you'd need to do generic type inference, but creating the methods has to be done at an earlier stage before that sort of analysis is possible. Worst case scenario, you could potentially pass another attrs class itself in as a converter, meaning the defined type is recursively dependent on the plugin logic...
Is this a pyright bug? type can't be an instance of a protocol
from typing import TypeVar, Protocol, runtime_checkable
@runtime_checkable
class Foo(Protocol):
foo: int
T = TypeVar("T")
def bar(cls: type[T]): # or type[Any]
if isinstance(cls, Foo):
reveal_type(cls) # Never
The equivalent code in mypy simply doesn't do anything, btw, as it statically determines the reveal_type to be "impossible" ๐
I think it is a bug
I believe since you are checking a type not an instance, it should be issubclass not isinstance
A class can still satisfy a protocol
class Hmm:
foo: ClassVar[int] = 42
But that class wouldnt' be an instance of Foo
why not?
from typing import TypeVar, Protocol, runtime_checkable
@runtime_checkable
class Foo(Protocol):
foo: int
T = TypeVar("T")
def bar(cls: type[T]):
if issubclass(cls, Foo):
reveal_type(cls) # Type of cls is Type[Foo]```
Why isn't Hmm an instance of Foo? It satisfies the requirement (has a foo attribute)
actually I think issubclass doesn't really work with protocols
Not by default, to have isinstance and issubclass work with a protocol you need runtime checkable decorator.
Actually, mypy does this correctly
but only if cls is annotated as a plain type
Revealed type is "__main__.<subclass of "type" and "Foo">"
well, "correctly"
it treats type and type[Any] differently, although they should be equivalent
o ๐ฆ TypeError: Protocols with non-method members don't support issubclass()
Yeah it's a pyright bug, it says Never, but the code can clearly be reached if you run it.
can we type a Protocol to tell object need either one attribute or an other (or both)
but at leat one
class HasFoo(Protocol):
foo: int
class HasBar(Protocol):
bar: str
HasFooOrBar = HasFoo | HasBar
ok, that's what I decided to do, I was wondering if there was any other way to do it but it's fine
thx
why do you need this btw?
for a discord bot, I have an argument that can either have "guild" or "guild_id" as attribute
I've been spoilt by Rust's rich generics system, is there a way to make mypy/pylance bound the type of a typevar automatically?
I've got a function defined as
def map_reduce(
data: Iterable[T],
emitfunc: Callable[[T], Iterable[tuple[K, U]]] = lambda rec: [(rec, None)],
reducefunc: Callable[[list[U]], V] = lambda v: v,
) -> dict[K, V]:
What I want is for the types of K and U to be bounded automatically by the type of emitfunc I.E. if none is passed, K == T and U == None, but mypy just tells me that they're not the same type and I can't do that
(same for list[U] and V)
Hmm, could you elaborate a bit? Maybe an example on https://mypy-play.net
ah hmmm
I think you'll need the new "typevar defaults" thing
or you'll have to use overloads
I ended up using overloading, yeah. Also turns out that pyright just copes with that, so I've just moved to pyright lol
pyright is amazing if you can use it
it doesn't have a plugin system but if you're ok with that then I'd choose it any day over mypy
I'll be honest, I don't think I've ever used any mypy plugins
some plugins ship with mypy itself so there's a good chance you have used some
like the one for dataclasses
ye
Love it (errors caused by type narrowing and Self not working properly)
is there something that tells the typechecker, if this paramater is True, returns this type, else return this other type?
a) use typing.overload
b) create two different functions
usually in such situation you're better off redesigning the code to have two separate functions, or perform a similar maneuver
mind showing the function?
We have this function, I'd like to add typing for the return type but I'm not quite sure how to type this... ```py
def session():
"""Returns a boto3 SQS client."""
return aiobotocore.session.get_session().create_client(
"sqs",
region_name=REGION,
aws_access_key_id=credentials.aws_access_key,
aws_secret_access_key=credentials.aws_secret_key,
)
When printing the type of `session()`, I get `ClientCreatorContext`, and `async with session() as sqs: print(type(sqs))` I get `client.SQS`, which is a class that doesn't even exist in the source. I guess it must be created at runtime which really sucks :/
Anyone got any ideas?
def pieces_lists(self, coords: bool = False) -> list[list[Optional[Piece] | tuple[Optional[Piece], tuple[int, int]]]]:
"""Returns a list of lists of pieces representing the board.
If `coords` is True, it will return a list of lists of tuples of pieces and their coordinates."""
return [
[
self[x, y] if not coords else (self[x, y], (x, y))
for x in range(8)
] for y in range(8)
]```
Maybe this helps?
https://pypi.org/project/types-aiobotocore/
Unfortunately it didn't
I assume overload is for this, never really looked into it
Ive seen it before though, its something like this right:
@overload
def pieces_lists(self) -> list[list[Optional[Piece]]:
...
def pieces_lists(self, coords: bool = False) -> list[list[tuple[Optional[Piece], tuple[int, int]]]:
function```
def pieces(self) -> list[list[Piece | None]]:
return [[self[x, y] for x in range(8)] for y in range(8)]
def pieces_with_coords(self) -> list[list[tuple[Piece | None, int, int]]]:
return [[(self[x, y], x, y) for x in range(8)] for y in range(8)]
eh I feel like its too simular
hm?
Those functions are somewhat similar, but it doesn't mean they have to be literally the same method
eh alright, how would I do it with overload though, just curious
@overload
def pieces_lists(self, coords: Literal[True] = ...) -> list[list[Piece | None]]:
...
@overload
def pieces_lists(self, coords: Literal[False]) -> list[list[tuple[Piece | None, tuple[int, int]]]:
...
def pieces_lists(self, coords: bool = False) -> list[list[Piece | None | tuple[Piece | None, tuple[int, int]]]:
function
Perhaps see this:
https://martinfowler.com/bliki/FlagArgument.html
is that last return correct? or should it be list[list[Optional[Piece] | tuple[Optional[Piece], tuple[int, int]]]
since its not always gonna return the coords aswell
fixed
that isnt correct either though right?
its never gonna return just list[list[tuple[int, int]]
errr yes
also is Piece | None better than Optional[Piece]?
I have brain issue
the first example there feels to me like it's fine to use one function, especially if you can use keyword args
I guess it's more of an issue with Java, but it often leads to other problems, like returning different types
I would use | None if you don't plan to support <=3.9
it's shorter and provides little space for confusion
does <3.9 not have Optional?
It has optional, but it doesn't have | for types
also I just wanna stay consistent, is def func(arg: Optional[someting] = None) also worse than | None
I would just always use | None
They mean the same thing, but I'd use the pipe version
If I would keep that as one method I might make an enum for the flag
so that the boolean is not as cryptic, and it's easier to extend if I get more booking types
I guess the conceptual issue here is that the user will have to pass a flag with a literal every time, like foo(bar, baz=True). Which is more complex and probably longer than two methods
Also with two methods, it's easier to correlate tests with the function
why not?
can you show the whole error? and the code
probably variance issue
or two different types with the same name
Could you recommend some resources that explain variance?
It was painful to get a gist of these concepts. I want to be able to teach them now so resources with great examples are a must.
I am talking about a bit more advanced resources than beginner guides though.
Not sure how 'advanced' this is... but I have it as part of my mini-tutorial
https://decorator-factory.github.io/typing-tips/tutorials/generics/variance/
it is a bit dense though, and probably lacks examples
Another resource which might be relevant is PEP 695 (adding syntax for typevars) - there it describes how checkers can automatically determine the variance.
https://peps.python.org/pep-0695/#variance-inference
That I have read many times and sadly I find examples there to be lacking
do you have some good examples in mind?
If I did, I wouldn't need any help ;)
I can come up with working examples that will be understood by people who already understand variance. However, coming up with the right analogies and metaphors is much harder and is key to teaching it to a less prepared audience.
class SyncWithInteraction(Protocol):
def __call__(self, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
class AsyncWithInteraction(Protocol):
async def __call__(self, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
CallableWithInteraction = SyncWithInteraction | AsyncWithInteraction
def rig(check: CallableWithInteraction) -> CallableWithInteraction:
def is_owner(*args, **kwargs) -> bool:
interaction: discord.Interaction = next(value for value in (*args, *kwargs.values()) if isinstance(value, discord.Interaction))
if TYPE_CHECKING:
assert isinstance(interaction.client, 'Butters')
if interaction.user == interaction.client.owner:
return True
return False
if inspect.iscoroutinefunction(check):
async def wrapper(*args, **kwargs) -> bool: # type: ignore
if is_owner(*args, **kwargs):
return True
return await check(*args, **kwargs)
else:
assert inspect.isfunction(check)
def wrapper(*args, **kwargs) -> bool:
if is_owner(*args, **kwargs):
return True
return check(*args, **kwargs)
return wrapper``` ```py
@rig
async def interaction_check(self, interaction: discord.Interaction) -> bool:```
Type "(self: Self@ChessView, interaction: Interaction) -> Coroutine[Any, Any, bool]" cannot be assigned to type "CallableWithInteraction"
Type "(self: Self@ChessView, interaction: Interaction) -> Coroutine[Any, Any, bool]" cannot be assigned to type "(interaction: Interaction, *args: Unknown, **kwargs: Unknown) -> bool"
Parameter name mismatch: "interaction" versus "self"
Parameter 1: type "Interaction" cannot be assigned to type "Self@ChessView"
"Interaction" is incompatible with "ChessView"
Parameter "**kwargs" has no corresponding parameter
Function return type "Coroutine[Any, Any, bool]" is incompatible with type "bool"
"Coroutine[Any, Any, bool]" is incompatible with "bool"PylancereportGeneralTypeIssues```
I figured this should work, is it thinking that rig will return a non coroutine?
I think that's a bad idea
reason being -- you can't really check if a function is "sync" or "async"
async def my_coroutine_function(interaction: discord.Interaction) -> bool:
...
@rig
def interaction_check(interaction: discord.Interaction) -> Coroutine[bool, Any, Any]:
return my_coroutine_function(interaction)
interaction_check returns a coroutine, but your library thinks otherwise
interaction_check is gonna return a boolean
nope
in my snippet it is
interaction_check is a function that will return a Coroutine[bool]
So functionally it's the same as my interaction_check
oh is that something else
!e
async def foo():
return 42
def bar():
return foo()
print(foo())
print(bar())
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | <coroutine object foo at 0x7f2fc5a54880>
002 | <string>:7: RuntimeWarning: coroutine 'foo' was never awaited
003 | RuntimeWarning: Enable tracemalloc to get the object allocation traceback
004 | <coroutine object foo at 0x7f2fc5a54880>
005 | <string>:8: RuntimeWarning: coroutine 'foo' was never awaited
006 | RuntimeWarning: Enable tracemalloc to get the object allocation traceback
no its awaiting it in mine
that's not the issue at all
the functionality works great, its just not properly type hinted
You cannot check at runtime which one an object belongs to
class SyncWithInteraction(Protocol):
def __call__(self, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
class AsyncWithInteraction(Protocol):
async def __call__(self, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
im not, im doing that with inspect
those are just to typehint
class AsyncWithInteraction(Protocol):
async def __call__(self, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
def rig(check: AsyncWithInteraction) -> AsyncWithInteraction:
def is_owner(*args, **kwargs) -> bool:
interaction: discord.Interaction = next(value for value in (*args, *kwargs.values()) if isinstance(value, discord.Interaction))
if TYPE_CHECKING:
assert isinstance(interaction.client, 'Butters')
if interaction.user == interaction.client.owner:
return True
return False
async def wrapper(*args, **kwargs) -> bool:
if is_owner(*args, **kwargs):
return True
return await check(*args, **kwargs)
return wrapper``` will this work better?
I would definitely prefer this
alright well it does the same though
Because you can have:
- a function like
barthat returns a coroutine, but is not anasync deffunction --inspect.iscoroutinefunction(bar)will be False - a custom
__call__able object for which both will fail
now I can define an async function like py @rig async def interaction_check(interaction: discord.Interaction, *anything, **else) -> bool: return False which will return True if is_owner will return True else False
to use for something like thispy @rig async def interaction_check(self, interaction: discord.Interaction, *anything, **else) -> bool: return self.turn == interaction.user
so that if I run it (the owner) it returns True ALWAYS, but when seomeone else runs it, it will return whatever is inside of interaction_check
i would just be using it for the only case where I have something like my example, an async or sync function that has an interaction param and any other parameter I dont care about here
and Id decorate it with @ rig to have it reutrn True whenever I use it
I guess Python just doesn't have a good solution to writing function which are "both sync and async"
well type checker still doenst like this
in what way?
the functionality worked completely fine, just the type checker was complaining and im not sure how to type hint this
Type "(self: Self@ChessView, interaction: Interaction) -> Coroutine[Any, Any, bool]" cannot be assigned to type "(interaction: Interaction, *args: Unknown, **kwargs: Unknown) -> Coroutine[Any, Any, bool]"
Parameter name mismatch: "interaction" versus "self"
Parameter 1: type "Interaction" cannot be assigned to type "Self@ChessView"
"Interaction" is incompatible with "ChessView"
Parameter "**kwargs" has no corresponding parameterPylancereportGeneralTypeIssues```
Ah
well, you specified that an AsyncWithInteraction object will be comfortable if you pass some arbitrary arguments into it
I think you'll need to use ParamSpec or a TypeVarTuple
oh, could you implement it for me, I have no idea where I would use htose
I don't exactly remember what limitations ParamSpec has and I'm lazy
But there's
!pep 612
those are some long ass peps tho
would I use a protocol with that aswell then?
Yeah I think you'll need a generic protocol
ok Im not sure where to use the param spec, I got this though ```py
class AsyncWithInteraction(Protocol):
async def call(self: View, interaction: discord.Interaction, *args, **kwargs) -> bool:
...
def rig(check: AsyncWithInteraction) -> AsyncWithInteraction:
def is_owner(*args, **kwargs) -> bool:
interaction: discord.Interaction = next(value for value in (*args, *kwargs.values()) if isinstance(value, discord.Interaction))
if TYPE_CHECKING:
assert isinstance(interaction.client, 'Butters')
if interaction.user == interaction.client.owner:
return True
return False
async def wrapper(*args, **kwargs) -> bool:
if is_owner(*args, **kwargs):
return True
return await check(*args, **kwargs)
return wrapper``` but
if I dont say the self will be of View, rig will be accepted, but whenever I use it inside of a View, itll tell me Argument of type "(self: Self@ChessView, interaction: Interaction) -> Coroutine[Any, Any, bool]" cannot be assigned to parameter "check" of type "AsyncWithInteraction" in function "rig" Type "(self: Self@ChessView, interaction: Interaction) -> Coroutine[Any, Any, bool]" cannot be assigned to type "(interaction: Interaction, *args: Unknown, **kwargs: Unknown) -> Coroutine[Any, Any, bool]" Parameter name mismatch: "interaction" versus "self" Parameter 1: type "Interaction" cannot be assigned to type "Self@ChessView" "Interaction" is incompatible with "ChessView" Parameter "**kwargs" has no corresponding parameterPylancereportGeneralTypeIssues when decorating
I dont really understand much of what im doing, I just need a way for the type checker to accept this, because it will save a lot of work
@regal summit are you using a static type checker? (not familiar with this)
don't really see the reason why you should use paramspec here.
wait actually @regal summit why do you need the function to have an interaction parameter at all?
Just accept a Callable
is_owner relies on interaction
So it needs an interaction param, can just tell the type checker Any but thatโs not always valid
This looks like a discord bot ๐ค
Whatever library you're using, it should have an owner-only check
Yeah but according to your implementation, it doesn't have to be the first argument, and it doesn't have to be called interaction
I would just be easy on the typing here and explain stuff in the docs
Not the way I want it implemented
Yeah it just needs an argument that is of type Interaction
im kinda confused, this looks like the default implementation of is_owner in dpy
Maybe I don't understand something, but is this really needed if you can just put py if interaction.user == interaction.client.owner: return True or ```py
if somekinda.lib.is_owner(interaction):
return True
that sounds a lot easier consider the complexity needed to understand the decorator
type[Exception] or typing.Type[Exception]?
the former
is_owner raises if the user is not owner, mine will return True if the user is owner, else run the function
well thats possible but the actual functionality works perfectly, just the typehinting wasnt correct
Ill be the only one using it anyway so it doenst need to be super clear what it does at first glance
anyone know how to type dtype here
def seq_to_array(seq: Sequence[_T] | Array[_T], dtype: type[_SimpleCData]) -> Array:
"""Cast a Sequence to a ctypes.Array of a given type."""
if isinstance(seq, Array):
return seq
arr_type = typing.cast(Type[Array[_T]], dtype * len(seq))
return arr_type(*seq)
I get a
can you explicitly tell that an object follows some protocols?
say we have this protocol```py
class DictKey(Protocol):
def hash(self):
...
def eq(self, other):
...
and we have a class that satisfies that protocol
you have to skim through the methods of the class to see if they follow DictKey
is there a way to tell right away that it follows DictKey?
without telling it in doc strings
you can inherit from the protocol
Morning
hello
I have this part of a Generic class and I want to tell that in the get method id it's an attribute of the class ModelType is there a way I could do it?
class DAO(DAOProtocol[ModelType, CreateSchema, UpdateSchema]):
"""DAO Base that performs all the basic CRUD operations."""
def __init__(self, model: Type[ModelType]):
self.model = model
async def get(
self, db: AsyncSession, id: Union[int, UUID]
) -> Union[ModelType, EmptyType]:
"""Get single item by id."""
statement = select(self.model).where(self.model.id == id)
results = await self.execute(db, statement)
db_object = results.first()
if not db_object:
return Empty
return cast("ModelType", db_object[0])
Nevermind, I don't really need to do that, but it's an interesting question I believe, maybe the same type as ModelType.id?
Is ModelType a typevar?
Yes
yeah then that's not really possible
you would need "higher kinded types" which is currently not a thing in Python
Even tho it's bound?
Yeah, you can only resolve a type variable to a concrete type, not a "generic"
Maybe there's a way around this though. Can you share more context?
!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.
hmm, why do you need DAOProtocol?
At first I was using composition so functions expected a lookalike of DAOProtocol but now I'm using DAO(Generic[Model, Create, Update])
DAOProtocol it's close to be removed honestly
I think I did something similar some time ago, let me dig it up
The Base of the ModelType it's this:
@as_declarative()
class Base:
"""Base for all declarative models."""
table_name_pattern = re.compile(r"(?<!^)(?=[A-Z])")
convention = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
date_added = Column(DateTime, nullable=False, default=datetime.utcnow)
date_updated = Column(DateTime, nullable=True, onupdate=datetime.utcnow)
registry = _registry(metadata=MetaData(naming_convention=convention))
@declared_attr
def __tablename__(cls) -> str:
return re.sub(
cls.table_name_pattern, "_", pluralize(cls.__class__.__name__)
).lower()
actually nevermind, my idea was stupid
Hello @lyric tangle, please see #โ๏ฝhow-to-get-help and open a help thread in #1035199133436354600.
These channels are for discussing specific topics
I have a function that returns an object I need to use in a async with obj(...) as client: manner, how can I type hint what client will be?
def get_client():
return client_creator.create_client(...)
async def main():
async with get_client() as client:
client: MyClient # I want to avoid this line of code everywhere
just define the type-hint for the return type of that get_client function
well, whatever client_creator.create_client returns
or is the issue that it's returning several different client classes without any shared class?
I can do that, but then it doesn't type hint the final client object
what's the return type of that function? Does that returned class define type-hints properly?
no
and the class I get as client doesn't even exist where it says it would
Which is why I had to install an alternative package for the typings
And that's the class I want client typed as
this is how you generally specify a type for async context managers
it's just getting the return type of __aenter__
So should I subclass the return type of create_client and type it as that?
if the create_client function returns some class that doesn't properly specify what's returned from __aenter__, then you could do multiple things
So create_client is defined like this ```py
def create_client(self, *args, **kwargs):
return ClientCreatorContext(self._create_client(*args, **kwargs))
Where this is `ClientCreatorContext` ```py
class ClientCreatorContext:
def __init__(self, coro):
self._coro = coro
self._client = None
async def __aenter__(self) -> AioBaseClient:
self._client = await self._coro
return await self._client.__aenter__()
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._client.__aexit__(exc_type, exc_val, exc_tb)
- Subclass it
- Create a protocol class to cast it into, leaving the actual runtime class the same
- cast the
clientwithin eachasync withblock
so, in this example, the client your async context manager should be of AioBaseClient type then
perhaps it could be worth it to make the ClientCreatorContext generic
and use the return type from the passed coro's return type
this is all defined in a installed package
well then these are your options
I'm just looking to type my get_client function
along with reporting this issue to the lib
the typing as AioBaseClient works just fine
The problem is that the class we actually get has a bunch of methods that aren't defined in the base client
That's why I'm doing this workaround :p
yeah, that's why I mentioned making it generic
but that's something the lib has to handle
I'm going to implement #1, but I'm also curious about #2 could you provide an example or resource
class Foo:
def foo(self, x: int) -> int:
return x
class Bar:
def foo(self, x: int) -> int:
return x + 1
in here, the Foo and Bar classes both have the same function foo, but they don't have any shared class, so if you wanted a fucntion to accept any such class with foo function defined like this, you could just define a protocol: ```py
from typing import Protocol
class MyProto(Protocol):
def foo(self, x: int) -> int:
...
def func(f: MyProto):
...
func(Foo()) # Works type-wise
func(Bar()) # Works type-wise
that's the simple example on how protocol classes work
similarly, you could make a create_instance function, and have it return a protocol
so even though it actually returns a class that implements that protocol (Foo or Bar), the type-checker will just see the protocol
so, making a protocol with proper __aenter__ (and __aexit__) and stating that your function returns that could solve your issue
So it's the same as #1 just subclassed from Protocol instead?..
it's not a subclass
the protocol class is never even instantiated
you just return the original class
and just tell the type-checker it's actually some protocol class you defined
Okay the Protocol worked perfectly
def create_instance() -> MyProto:
if random.randint(0, 1) == 1:
return Bar()
return Foo()
you should still report this to the library though
There's already a couple issues
as what they should actually be doing is to make the ClientCreatorContext generic, over the return type of the passed coro
from typing import TypeVar, Awaitable, Generic, Optional
ClientT = TypeVar("ClientT", bound=AioBaseClient)
class ClientCreatorContext(Generic[ClientT]):
def __init__(self, coro: Awaitable[ClientT]):
self._coro = coro
self._client: Optional[ClientT] = None
async def __aenter__(self) -> ClientT:
self._client = await self._coro
return await self._client.__aenter__()
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._client.__aexit__(exc_type, exc_val, exc_tb)
this is probably all that library has to do
So I find type hinting to be very helpful for programmers. Are there any situations when type hinting wouldn't be recommended?
when the type system cannot express the info required
Type hinting is painful in py2
I really wait for 3.7 EOL.
3.7 is not officially dead currently, so typeshed must support it. There are a lot of functions in stdlib that uses pos-only parameters, but pos-only syntax was introduced only in 3.8. So, typeshed is using __arg to flag a pos-only args.
Once 3.7 is dead, typeshed can be a lot cleaner.
yeah I don't find it too bad either. It does confuse new contributors to typeshed pretty regularly though
How is it i print a value to a string?
Like it takes a value and sendes it in a discord server
try #discord-bots for discord api related questions :P
Ok sry
is this the only channel related to type checking/type hinting? (im looking more for the overall type checking type stuff
type theory?
self.exclude: set[str] = set(exclude) if exclude else set()
how should i typehint something that could be a set of str or an empty set?
will it always be empty?