#type-hinting

1 messages ยท Page 10 of 1

carmine phoenix
#

Iterator[T] is better

#

no need to specify stuff that wont change the bahviour from the user perspective

heady flicker
#

Yes, exactly as @oblique urchin said.

carmine phoenix
#

and this way it is also more flexible for the devs

heady flicker
#

None in this case. However, in general case, I would go for more specific hints

#

That's a bit extreme, yes

soft matrix
#

this can however be useful for some things like version tuples

#

cause then you can statically determine if some things will be Never

brisk hedge
#

@mellow talon here

mellow talon
#

@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

trim tangle
#

it does "O(1) type checking" by only checking one random element of a nested collection, which sounds just... wrong

#

and unstable

soft matrix
#

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

trim tangle
#

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

soft matrix
#

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

trim tangle
#

!pypi typeguard

rough sluiceBOT
trim tangle
#

this one also seems working, although last commit was in 2021

trim tangle
#

and provide some half-sensible error messages

#

for example, IIRC the creator of beartype is against static type checkers?..

soft matrix
#

oh they are?

#

lol

#

itd be weird to have this section if they were

oblique urchin
#

the guy's style is a bit... unbearable

brisk hedge
#

not a fan of the paw puns? ๐Ÿ˜„

trim tangle
#

maybe I'm misinterpreting this ๐Ÿคทโ€โ™‚๏ธ
not tot say that it's an invalid opinion

soft matrix
#

it is certainly an opinion

tranquil turtle
#

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

soft matrix
#

well until it calls it

oblique urchin
#

unless it introspects the bytecode

trim tangle
#

omw to solve the entscheidungsproblem

tranquil turtle
#

I think it is possible to look into consts of code of wrapped func, find there another code object, and then wrap it

tranquil turtle
#

Hmm. There is no difference between "Any" and "object" from beartype's point of view

rare scarab
#

Does Any include None?

#

Yes.

brisk fjord
# trim tangle

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?

rare scarab
#

but how does beartype handle duck types?

#

aka protocol

brisk fjord
#

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?

soft matrix
#

they do have to implement everything ๐Ÿค”

rare scarab
#

Make several protocols implementing each combination of methods, all in a union

brisk fjord
#

I may be wrong though

rare scarab
#

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

green gale
#

i heard that it only checks one item of a collection, and that's a dealbreaker for me

brisk fjord
#

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?

green gale
#

if i have type checking, i don't want it half-assed. give me all the guarantees.

soft matrix
#

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

brisk fjord
#

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?

soft matrix
#

i must have explained something badly

brisk fjord
#

No worries, I appreciate the help

#

I have a sense there's subtlies here that I'm not getting

soft matrix
#

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

brisk fjord
#

Ok, so it sounds like this is for the type checkers/to not have to explicitly declare/subclass everything. Aka a nicer dev experience

soft matrix
#

yes ๐Ÿ‘

brisk fjord
#

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

mint inlet
#

(namely the example about typing.Iterable / typing.Iterator needing to be subclassed from every time w/o this)

carmine phoenix
#

does anybody know if yield from iterable will be deprecated in the future?

carmine phoenix
#

perhaps I misunderstood

oblique urchin
#

it's very unlikely for any major language feature to get deprecated. One Python 3 was enough

carmine phoenix
#

also Im asking because apparently what most ppl use it for wasnt how it was intended to be used

oblique urchin
#

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

trim tangle
carmine phoenix
#

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"

trim tangle
#

if if you're using generator-based coroutines for something else, like a state machine

trim tangle
#

definitely not removing yield from from the language

carmine phoenix
#

I was confused when I first saw, but that makes sense ig

runic sleet
#

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)

carmine phoenix
#

do you say: "all the methods and functions are fully typed" or:
"all the methods and functions are fully type hinted"?

runic sleet
# acoustic thicket 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)
acoustic thicket
#

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
violet sluice
#

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
lethal solar
#

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

brisk hedge
#

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

lethal solar
#

ok I see, thanks

#

If MyClass has a Generic type, I should bound with MyClass[Any] ? (The typevar used in generic has no constraints)

soft matrix
#

yes

carmine phoenix
soft matrix
#

id say the former

solemn sapphire
rare scarab
#

use a literal?

solemn sapphire
#

you mean?

trim tangle
#

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)
solemn sapphire
#

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.

trim tangle
#

If it's something you know but the type checker can't prove, use assert False or raise AssertionError

solemn sapphire
#

ah

trim tangle
#

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?..

solemn sapphire
#

True, if all the variants of an enum have been mentioned the type checker should be able to say that ok this is unreachable

solemn sapphire
heady flicker
trim tangle
heady flicker
#

Exactly.

trim tangle
#

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?

oblique urchin
#

e.g. literals, unions, True/False

leaden oak
#

What would you call an indexed type? Like List[int] or Dict[str, str]?

brazen jolt
#

generic?

leaden oak
#

ah yeah

brazen jolt
#

!d typing.Generic

rough sluiceBOT
#

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...
leaden oak
#

what would you call the type "inside" of the generic?

brazen jolt
#

you use type-vars there

leaden oak
#

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

brazen jolt
#

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

oblique urchin
leaden oak
#

Awesome, thanks!

harsh lantern
#

are tuples of types a valid type object in python

solemn sapphire
buoyant swift
#

no?

soft matrix
#

Oh I misread

#

I thought they meant can you do tuple[type, ...]

harsh lantern
# buoyant swift no?

so if i want to typecheck a tuple with an int and a string, i can't do (int, str)?

buoyant swift
#

no

harsh lantern
#

i have to use NamedTuples?

buoyant swift
#

tuple or Tuple

harsh lantern
#

tuple[int, str]?

soft matrix
#

Yes

solemn sapphire
brisk hedge
#

but compatible with all stable versions

trim tangle
#

ye

#

if you're making a library, there are few good reasons to be incompatible with 3.9

hallow flint
#

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

trim tangle
#

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:
    ...
hallow flint
#

@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

mild zodiac
#
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.
runic sleet
#

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])
    ...
deep saddle
#

from __future__ import annotations moment

oblique urchin
#

the future import doesn't help here because this is not an annotation

deep saddle
#

that is true i am blind

runic sleet
#

wait but how? The ctypes.Array[_T] needs to be defined at runtime right?

oblique urchin
#

if TYPE_CHECKING: _Base = Array[_T] else: _Base = Array

runic sleet
# oblique urchin `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"
oblique urchin
#

hm I guess it implicitly turns into List[Any]. Not sure how to solve that

deep saddle
#

TrollDespair py if TYPE_CHECKING: class Foo(Array[T]): ... else: class Foo(Array): ...

harsh lantern
#

how do i create a type representing a tuple of int and str

deep saddle
runic sleet
harsh lantern
runic sleet
#

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)): ...
deep saddle
#

this won't accept subclasses of object, only exact object instances

oblique urchin
#

no

deep saddle
#

if I understand your question correctly

deep saddle
oblique urchin
#

a TypeVar can't have only one constraint. even if it did, it would still accept subclasses

deep saddle
#

rats

#

im 0 for 2 today on type hinting

#

I feel like the docs are a little misleading though

oblique urchin
#

it's confusing. the TypeVar has to be solved to exactly str or bytes, but after that you can still pass subclasses

harsh lantern
#

python should have Exactly[bytes]

deep saddle
#

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

oblique urchin
#

it's also difficult to make this type-safe; anything typed as bytes could not be safely passed to Exactly[bytes]

tranquil turtle
#
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

soft matrix
#

This just looks like

tranquil turtle
#

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.

soft matrix
#

!pep 688

rough sluiceBOT
#
**PEP 688 - Making the buffer protocol accessible in Python**
Status

Draft

Python-Version

3.12

Created

23-Apr-2022

Type

Standards Track

tranquil turtle
tranquil turtle
lethal solar
tranquil turtle
#

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

trim tangle
#

a protocol describes how an object works, not its class

#

Why do you need this Protocol?

lethal solar
#

To assert a class we use respect a pattern
but I guess I should use abstract for this kind of things ?

lethal solar
#

I don't really know when I should use protocols / abstract classes

trim tangle
#

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

tranquil turtle
#

why mypy-master is three times slower?

lethal solar
tranquil turtle
#

i dont think you need classes for that. You can store language name in instance attribute

lethal solar
#

here there is juste the protocol + the actual implementation (btw, I should name LanguageProtocol, not LanhuaheImplementation)

lethal solar
trim tangle
lethal solar
#

well mypy don't complain

#

but it seems counterintuitive

soft matrix
lethal solar
tranquil turtle
#

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: ...

tranquil turtle
tranquil turtle
soft matrix
#

oh mypyc might be it

trim tangle
# lethal solar wdym ?

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

lethal solar
#

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

trim tangle
#

Ah, I see

tranquil turtle
#
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): ...
lethal solar
#

Here I don't think Protocol is needed ๐Ÿค” does it ?

#

cause all are a Language subclass

tranquil turtle
#

yes
i still dont think you need protocol in your case because you can subclass same base class in every place

lethal solar
#

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

tranquil turtle
#

just define Language class in its own file and import it everywhere

lethal solar
#

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

tranquil turtle
trim tangle
tranquil turtle
#

In mypy it is enables by default AFAIK

trim tangle
#

that makes more sense to me

#

why is it disabled by default in pyright?

tranquil turtle
#

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

runic sleet
rough sluiceBOT
#

stdlib/ctypes/__init__.pyi line 138

def cast(obj: _CData | _CArgObject | int, typ: type[_CastT]) -> _CastT: ...```
runic sleet
#

_CastT is a TypeVar with bound _CanCastTo but it's a typeshed only class

old oar
#

What is typing.ClassVar?

rustic gull
#

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
soft matrix
#

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
harsh lantern
#

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

soft matrix
#

do you need the parameters to be named a and b?

azure mural
#

Can you type that?

harsh lantern
#

probably no

soft matrix
#

yes

#

you just need a callable protocol

oblique urchin
#

class Foo(Protocol): def __call__(self, a: Any, b: Any) -> bool: ...

harsh lantern
oblique urchin
#

for expressing the type? yes, pretty much

harsh lantern
oblique urchin
#

so yes, in general you need protocols when you want a more complex signature

#

in practice callbacks usually only take positional args though

soft matrix
#

there was a pep that would actually make the syntax you used valid but it got rejected :)

oblique urchin
#

!pep 677

rough sluiceBOT
#
**PEP 677 - Callable Type Syntax**
Status

Rejected

Python-Version

3.11

Created

13-Dec-2021

Type

Standards Track

acoustic thicket
#

still salty about that

frigid jolt
#

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)

frigid jolt
soft matrix
#

sounds like you need StrEnum

#

wait no you dont Any should be assignable to str

soft matrix
#

what error are you getting?

frigid jolt
#

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

soft matrix
#

can you show the full code?

summer berry
#

Can you give a minimal reproducible example?

frigid jolt
soft matrix
#

im assuming you have events = {} but im currently not entirely sure

frigid jolt
#
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]]
soft matrix
#

do you explicitly type self.extra_events as dict[str, list[CoroFunc]]?

frigid jolt
#

yes

soft matrix
#

well i dont see how youre getting that error then

#

name cannot be anything other than str there

summer berry
#

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?

soft matrix
frigid jolt
#

i can't paste all my code on pyright playground, it's too long

summer berry
#

A minimal example is preferable

#

I.e. the minimum amount of code needed to produce the error you see

summer berry
#

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

soft matrix
#

i think you need to rename name = ( to name_

frigid jolt
#

so this not a problem with my typehints

summer berry
#

I see you already have a None check, not sure why it didnt pick that up

summer berry
soft matrix
#

because its reassigning name which is str | None | AnyEvent and it then doesnt get renarrowed

frigid jolt
#

but before adding AnyEvent it was named name = (... and pyright wasn't raising any error

summer berry
#

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)]))```

rough sluiceBOT
#

@summer berry :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | ([1, 2, 3],)
002 | (1, 2, 3)
hallow flint
#

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]")))
rough sluiceBOT
#

@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=[])
pastel egret
#

It'd be the same reason why x = 1, 2 produces a tuple, the parenthesis are redundant?

green gale
#

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

pastel egret
summer berry
#

Thanks

pastel egret
summer berry
#

I was thinking that they're separate arguments rather than a tuple at first

oblique urchin
#

That would be nice but unfortunately it's too late for that.

#

!pep 637 for a related proposal

rough sluiceBOT
#
**PEP 637 - Support for indexing with keyword arguments**
Status

Rejected

Python-Version

3.10

Created

24-Aug-2020

Type

Standards Track

pliant vapor
#

is there a difference between

A = str | int

and

A = TypeVar('A', str, int)

?

oblique urchin
pliant vapor
#

what's the difference?

oblique urchin
#

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

oblique urchin
#

yes, it works in nearly the same way

soft matrix
#

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

oblique urchin
#

wouldn't you see the same behavior there if you had overloads (str) -> str and (bytes) -> bytes?

soft matrix
#

oh wait yeah you would

oblique urchin
#

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

median edge
#

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
rare scarab
#

Make AttributeProperty generic as well.

trim tangle
#

I don't think that's necessary

rare scarab
#

Hm...

trim tangle
#

pyright doesn't complain

rare scarab
#

should it be covariant or something?

oblique urchin
#

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

broken osprey
#

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?
soft matrix
#

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

broken osprey
#

I don't really mind, it is for pydantic

#

according to intellij, it isn't supported in 3.10

oblique urchin
#

PEP 646 added that, right?

#

until then you van use Literal[tuple(COUNTRY_CODE)] at runtime or for pyanalyze

median edge
rustic gull
#

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
lethal solar
#

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"]
spiral fjord
#

Seems like a missed opportunity that this doesn't exist for TypedDicts

runic sleet
#

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
soft matrix
soft matrix
lilac flame
#

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 ๐Ÿ™‚

soft matrix
#

Although you can always just use a bound type var for this use case

soft matrix
#

that doesnt sound not type safe per-say

#

just like itd be very hard to use

tranquil turtle
#

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.

soft matrix
#

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

brisk hedge
#

it wouldn't be a problem per se

#

But it would be infectious

oblique urchin
rare scarab
#

But is False is 0?

oblique urchin
#

no

#

but 0 is 0 isn't necessarily true either (not guaranteed by the language)

solemn sapphire
oblique urchin
solemn sapphire
#

ah dammit, sorry

oblique urchin
#

yeah giving the module and the type the same name isn't a great idea

solemn sapphire
#

the context is confusing as well

#

array.append(x) so your mind goes "obviously array is the type"

oblique urchin
#

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

solemn sapphire
#

I think moving the typecodes above the class is going to help

#

what do you think?

oblique urchin
#

yeah maybe, one issue is that array.array is clearly the most important thing here, so you'd want it to be first

solemn sapphire
carmine phoenix
#

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
soft matrix
#

yes, just use TypeAlias and quote the values

trim tangle
#

yeah, use TYPE_CHECKING sparingly

lethal solar
#

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

soft matrix
#

no property isnt type checkable directly

#

youre kinda just screwed lol

lethal solar
#

I forget the most important part ๐Ÿ˜…

lethal solar
#

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
soft matrix
#

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?

lethal solar
#

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

soft matrix
#
    def inner(_: Callable[..., C]) -> C:
        return cog_getter  # type: ignore
```id just do this
lethal solar
#

return property(cog_getter) # type: ignore ?

soft matrix
#

could do that if you wanted

lethal solar
#

I mean, here you return cog_getter assuming it is type C; I don't get it

soft matrix
#

when you access the property on an instance its type is C right?

lethal solar
#

wow ok I get it

soft matrix
#

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

lethal solar
#

yes right
thank you

plain dock
#

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?

oblique urchin
#

or a bound TypeVar, the Self PEP explains how to do this

runic sleet
#

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

tranquil turtle
runic sleet
blazing nest
#

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

bold gazelle
#

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?

rustic gull
#

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()

graceful blaze
#

Some reason you need it to Be an actual list?

bold gazelle
#

I think the problem is that pyright thinks df["col"] could be None

trim tangle
#

ye

graceful blaze
#

Sure. I was asking why s/he wanted to convert the column to a list at all

bold gazelle
#

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

oblique urchin
#

so you can use not_none(df.col)

graceful blaze
#

b = [] if a is None else a.to_list() ?

carmine phoenix
#

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))]
tranquil turtle
#

yes

trim tangle
#

yes

rustic gull
#

yes

twin elbow
#

Is it valid in Python 3.10 and beyond to use: [str] instead of list[str] as type hint?

oblique urchin
#

no

twin elbow
#

Okay, thank you. And for what [str] stands for? The parser doesn't complains.

oblique urchin
#

nothing, the parser doesn't really check annotations

twin elbow
#

Hmm, I mean the type-hint "parser"...

If [str] does not mean "a list of string" - what does it mean?

spiral fjord
#
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

twin elbow
#

Thank you!

oblique urchin
lethal solar
#
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 ?

soft matrix
#

is there a reason you are avoiding Generic?

lethal solar
#

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

soft matrix
#

you might be interested in

#

!pep 696

rough sluiceBOT
#
**PEP 696 - Type defaults for TypeVarLikes**
Status

Draft

Python-Version

3.12

Created

14-Jul-2022

Type

Standards Track

lethal solar
#

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

soft matrix
#

you shouldnt need to add any default for it

#

Any is the implicit default

#

well in this case its ... but w/e

lethal solar
#

if I add Generic[P] in Test()

#

Maybe it's juste because pylance is in strict mode but it complains

lethal solar
soft matrix
#

yeah thats just strict

#

you can use pep 696 atm in pyright if you want to silence the error in one fell swoop

lethal solar
#

how ?

soft matrix
#

import ParamSpec from typing_extensions

lethal solar
#

oh right

soft matrix
#

and use it with the default argument

lethal solar
#

thx

soft matrix
#

lmk if you run into any issues or have any other questions about it (im the author)

lethal solar
#

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

pastel egret
lethal solar
#

But itโ€™s ok to set a default value for T also

pastel egret
#

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.

lethal solar
#

In fact I do this so thatโ€™s why ig

#

MiscCommandCallback will need 2 args also and here P need to come first

harsh lantern
#

are protocol same as abc?

acoustic thicket
# harsh lantern 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...

โ–ถ Play video
trim tangle
#

PEP 702

soft matrix
#

i just wish it did something at runtime

#

(and was in warnings)

trim tangle
#

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

knotty hawk
#

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 = true to the pyproject.toml file
  • using the --follow-imports silent and --follow-imports skip flags
  • adding # type: ignore to 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.

soft matrix
#

im pretty sure you cant do anything about this

brazen jolt
#

yeah, you could report it to the project

soft matrix
#

you could switch to pyright but i feel like you should probably just report it

knotty hawk
#

So when using MyPy I can never use libraries that use PyRight?

#

that sucks

#

guess ill have to switch Sadge

soft matrix
#

maybe you should actually report this to pyright

#

that it doesnt warn you that this is invalid

knotty hawk
#

id have to report it to mypy then

brazen jolt
#

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

soft matrix
#

i dont think disabling this is possible

#

just cause it should be using the ast module

brazen jolt
#

oh, # type: ignore comment is shown in the AST directly?

#

that's interesting

soft matrix
#

yeah

brazen jolt
#

in that case yeah, pyright should certainly have a warning shown

median tangle
#

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

heady flicker
#

Callable[..., Foo]

median tangle
#

ohh thanks!

#

and would it be possible to somehow say what the first argument is, but ignore the rest?

heady flicker
#

I suggest to use protocols in such cases

median tangle
#

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

heady flicker
#

Why can't you use a paramspec here?

median tangle
#

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 ...

heady flicker
#

Makes sense.

trim tangle
oblique urchin
#

can't guarantee that your type checker will approve

regal summit
#

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```
oblique urchin
#

where T1 and T2 are both TypeVars

regal summit
pliant vapor
#

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?

soft matrix
#

that looks fine

tacit vector
#

how does it know which foo to select?

brazen jolt
#

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

carmine phoenix
#

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
trim tangle
#

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
carmine phoenix
trim tangle
#

typing aside, you might want to add some delay and not just hammer the function again and again

carmine phoenix
#

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]:
trim tangle
#

and what is your python version?

carmine phoenix
carmine phoenix
#

its pycharm ofc

#

but when I do: Optional[Callable] = None then the warning goes away

trim tangle
#

well, pycharm's typing facilities are pretty questionable IMO ๐Ÿ˜„

carmine phoenix
#

yeah very qustionable

#

I've been finding bugs everwhere lately

#

I wonder if its possible to change linter with Pycharm

trim tangle
#

I just use VSCode because Pylance is amazing

carmine phoenix
#

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

spiral fjord
#

Pylance is a vscode extention, it uses pyright for type checking which should be possible to get working with any lsp compatible editor.

carmine phoenix
#

there is no pyright or pylance on pycharm unfortunately:

trim tangle
#

yeah I don't think pycharm supports LSP ๐Ÿคก

carmine phoenix
#

but I will give mypy a shot

#

lol

trim tangle
#

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

heady flicker
trim tangle
#

omg, a talking oatmeal

heady flicker
#

More talkative than you expect.

pliant vapor
#

is it possible to type hint collections.Counter?

buoyant swift
#

it's just collections.Counter. though probably use from collections import Counter so it's less annoying

pliant vapor
#

i mean like

#
def foo(x: str) -> Counter[str]:
    ...

something like that

#

does Counter implement __class_getitem__?

soft matrix
#

yes

#

Counter[T] ~= dict[T, int]

pliant vapor
#

thx

regal summit
#

how do you typehint the return of a coroutine function?

#

is it like Callable[[args], Awaitable[return]]?

soft matrix
#

yes

upper flame
#

๐Ÿ‘‹ 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...

oblique urchin
upper flame
#

I think both .so files and .py files are shipped in the compiled version.... so it seems like there should be a toggle ๐Ÿ˜†

oblique urchin
#

that would require some dark magic in the import system I think

heady flicker
upper flame
leaden oak
upper flame
#

ok thank y'all for the ideas!

regal summit
#

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
upper flame
regal summit
#

well I mean thats basically all thats important

#

the typehint is in the parent class and some subclasses have a color_select method

upper flame
regal summit
#

card can be either parent class or subclass

leaden oak
#

is whatever you're typing annotating with the color_select type alias a method? did you include self?

regal summit
#

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

soft matrix
#

You need to use a callable protocol

regal summit
#

can you give me an example

soft matrix
#
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

regal summit
#

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

soft matrix
#

this doesnt meet the needs of your original question

#

the only way you can do this is with the code i showed

regal summit
#

oh well this is what I need, mustve asked incorrectly

soft matrix
#

or some form of it at least

regal summit
#

oh wait so I could do callable_wiht: AtLeastOne

soft matrix
#

but a function of (interaction, something_else) -> Any wont be able to pass as callable_with_interaction

regal summit
#

how do I make it pass lol

soft matrix
#

do you know how protocols work?

#

or what they represent?

regal summit
#

no

soft matrix
#

have you read the documentation for them?

regal summit
#

;-;

#

could you give me an example of the answer so I can try and figure out why that is the answer

soft matrix
#

id like to explain it

#

but having a grasp of what a Protocol does in the type system is key to understanding it

regal summit
#

alright, well I dont have a lot of time right now so I guess ill do that soon then

runic sleet
#

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]

upper flame
pastel egret
#

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.

trim tangle
#

Yeah, calls in general are not valid annotations

paper salmon
#

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

oblique urchin
paper salmon
#

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

trim tangle
#

ye

#

contravariance be weird

trim tangle
#

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:

  1. 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:
        ...
  1. 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
oblique urchin
#

do you already have something telling people not to use tuple[int] unless they really want a one-element tuple?

trim tangle
#

I think that's not a pattern, but yeah I've seen that. I'll remember to add this

oblique urchin
#

and avoid using TypeVars with constraints

#

which I guess is an antipattern instead of a pattern ๐Ÿ™‚

trim tangle
#

yeah those are kinda strange

oblique urchin
#

yes, that's a TypeVar with constraints

trim tangle
#

When you try to return an arbitrarily sized tuple

#

or pass it in

oblique urchin
#

in many cases yes, but what if you're writing a stub file or annotations for a library?

trim tangle
#

ah

#

true

#

I've seen a lot of different wrong annotations, like callable, (int, str), etc.

acoustic thicket
#

dict[K: V] is another one

oblique urchin
#

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)

trim tangle
#

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"

spiral fjord
#

Make a 20 common mistakes while type hinting python blog and get to the top of hn and lobsters.

trim tangle
#
  1. using type hints
trim tangle
trim tangle
vagrant yarrow
#

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 ๐Ÿ˜„

vagrant yarrow
#

Thank you so much! ^

oblique urchin
#
  • 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
trim tangle
trim tangle
oblique urchin
#

because even if the param names are meaningful, you probably still call your callback with positional args

trim tangle
#

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

oblique urchin
#

mypy lets you do that

#

which is technically unsound but perhaps more useful practically

trim tangle
#

huuh

#

i don approve of em unsound things

oblique urchin
#

or maybe it's just for protocols?

trim tangle
#

hmm, maybe it's a remnant of the time when we didn't have /?

trim tangle
#

I updated the article

brittle plover
#

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.

oblique urchin
#

at the time NoneType didn't even exist under an accessible name

leaden oak
#

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

acoustic thicket
#

is the first one meant to be about not having named parameters..?

stray summit
#

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?

trim tangle
#

I think it's an open problem in dataclasses/attrs

trim tangle
#

and the third one... I'll have to add some more stuff

pastel egret
#

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.

trim tangle
#

ye pretty much

pastel egret
#

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...

brisk hedge
#

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" ๐Ÿ˜…

spiral fjord
#

I believe since you are checking a type not an instance, it should be issubclass not isinstance

trim tangle
#
class Hmm:
    foo: ClassVar[int] = 42
spiral fjord
#

But that class wouldnt' be an instance of Foo

trim tangle
#

why not?

spiral fjord
#
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]```
trim tangle
#

actually I think issubclass doesn't really work with protocols

spiral fjord
#

Not by default, to have isinstance and issubclass work with a protocol you need runtime checkable decorator.

brisk hedge
#

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

spiral fjord
#

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.

lethal solar
#

can we type a Protocol to tell object need either one attribute or an other (or both)
but at leat one

trim tangle
lethal solar
#

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

trim tangle
#

why do you need this btw?

lethal solar
#

for a discord bot, I have an argument that can either have "guild" or "guild_id" as attribute

bright tangle
#

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)

trim tangle
#

ah hmmm

#

I think you'll need the new "typevar defaults" thing

#

or you'll have to use overloads

bright tangle
trim tangle
#

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

bright tangle
#

I'll be honest, I don't think I've ever used any mypy plugins

trim tangle
#

me neither

#

it's used for stuff like ORMs that do a lot of magic

oblique urchin
#

like the one for dataclasses

trim tangle
#

ye

bright tangle
#

Love it (errors caused by type narrowing and Self not working properly)

regal summit
#

is there something that tells the typechecker, if this paramater is True, returns this type, else return this other type?

trim tangle
#

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?

karmic zealot
#

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?
regal summit
# trim tangle mind showing the function?
    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)
        ]```
karmic zealot
regal summit
#

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```
trim tangle
#
    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)]
regal summit
#

eh I feel like its too simular

trim tangle
#

hm?

#

Those functions are somewhat similar, but it doesn't mean they have to be literally the same method

regal summit
#

eh alright, how would I do it with overload though, just curious

trim tangle
#
@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
regal summit
#

since its not always gonna return the coords aswell

trim tangle
#

fixed

regal summit
#

that isnt correct either though right?

#

its never gonna return just list[list[tuple[int, int]]

trim tangle
#

errr yes

regal summit
#

also is Piece | None better than Optional[Piece]?

trim tangle
#

I have brain issue

oblique urchin
trim tangle
#

I guess it's more of an issue with Java, but it often leads to other problems, like returning different types

trim tangle
#

it's shorter and provides little space for confusion

regal summit
#

does <3.9 not have Optional?

trim tangle
#

It has optional, but it doesn't have | for types

regal summit
#

also I just wanna stay consistent, is def func(arg: Optional[someting] = None) also worse than | None

trim tangle
#

I would just always use | None

#

They mean the same thing, but I'd use the pipe version

trim tangle
#

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

regal summit
#

why not?

trim tangle
#

probably variance issue

oblique urchin
#

or two different types with the same name

heady flicker
#

I am talking about a bit more advanced resources than beginner guides though.

hallow flint
#

I think the mypy docs are fairly good

#

iirc opium puppy also wrote something up

trim tangle
#

it is a bit dense though, and probably lacks examples

pastel egret
heady flicker
#

That I have read many times and sadly I find examples there to be lacking

trim tangle
#

do you have some good examples in mind?

heady flicker
#

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.

trim tangle
#

same here

#

I suck at teaching tbh

regal summit
#
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?
trim tangle
#

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

regal summit
#

interaction_check is gonna return a boolean

trim tangle
#

nope

regal summit
#

in my snippet it is

trim tangle
#

interaction_check is a function that will return a Coroutine[bool]

#

So functionally it's the same as my interaction_check

regal summit
#

oh is that something else

trim tangle
#

!e

async def foo():
    return 42

def bar():
    return foo()

print(foo())
print(bar())
rough sluiceBOT
#

@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
regal summit
#

no its awaiting it in mine

trim tangle
#

that's not the issue at all

regal summit
#

the functionality works great, its just not properly type hinted

trim tangle
#

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:
        ...
regal summit
#

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?
trim tangle
#

I would definitely prefer this

regal summit
#

alright well it does the same though

trim tangle
regal summit
#

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

regal summit
#

and Id decorate it with @ rig to have it reutrn True whenever I use it

trim tangle
#

I guess Python just doesn't have a good solution to writing function which are "both sync and async"

regal summit
trim tangle
#

in what way?

regal summit
#
  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```
trim tangle
#

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

regal summit
#

oh, could you implement it for me, I have no idea where I would use htose

trim tangle
#

I don't exactly remember what limitations ParamSpec has and I'm lazy

#

But there's

#

!pep 612

rough sluiceBOT
#
**PEP 612 - Parameter Specification Variables**
Status

Accepted

Python-Version

3.10

Created

18-Dec-2019

Type

Standards Track

trim tangle
#

and

#

!pep 646

rough sluiceBOT
#
**PEP 646 - Variadic Generics**
Status

Accepted

Python-Version

3.11

Created

16-Sep-2020

Type

Standards Track

trim tangle
#

those are some long ass peps tho

regal summit
#

would I use a protocol with that aswell then?

trim tangle
#

Yeah I think you'll need a generic protocol

regal summit
#

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

terse swallow
#

@regal summit are you using a static type checker? (not familiar with this)

terse swallow
trim tangle
#

wait actually @regal summit why do you need the function to have an interaction parameter at all?

#

Just accept a Callable

regal summit
#

is_owner relies on interaction

#

So it needs an interaction param, can just tell the type checker Any but thatโ€™s not always valid

fathom river
#

This looks like a discord bot ๐Ÿค”

#

Whatever library you're using, it should have an owner-only check

trim tangle
#

I would just be easy on the typing here and explain stuff in the docs

regal summit
regal summit
soft matrix
#

im kinda confused, this looks like the default implementation of is_owner in dpy

trim tangle
#

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

carmine phoenix
#

type[Exception] or typing.Type[Exception]?

soft matrix
#

the former

regal summit
regal summit
#

Ill be the only one using it anyway so it doenst need to be super clear what it does at first glance

runic sleet
#

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

harsh lantern
#

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

trim tangle
humble plume
#

Morning

trim tangle
#

hello

humble plume
#

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?

trim tangle
#

Is ModelType a typevar?

humble plume
#

Yes

trim tangle
#

yeah then that's not really possible

#

you would need "higher kinded types" which is currently not a thing in Python

humble plume
#

Even tho it's bound?

trim tangle
#

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?

humble plume
#

Ok

#

File it's quite big 200+ lines, let me split it a little.

trim tangle
#

!paste

rough sluiceBOT
#

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.

humble plume
#

Cool, thanks it works

trim tangle
#

hmm, why do you need DAOProtocol?

humble plume
#

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

trim tangle
#

I think I did something similar some time ago, let me dig it up

humble plume
#

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()
trim tangle
#

actually nevermind, my idea was stupid

humble plume
#

lol

#

It happens

trim tangle
#

These channels are for discussing specific topics

karmic zealot
#

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
brazen jolt
#

just define the type-hint for the return type of that get_client function

karmic zealot
#

Yes, but what would that be

#

not MyClient

brazen jolt
#

well, whatever client_creator.create_client returns

#

or is the issue that it's returning several different client classes without any shared class?

karmic zealot
brazen jolt
#

what's the return type of that function? Does that returned class define type-hints properly?

karmic zealot
#

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

brazen jolt
#

this is how you generally specify a type for async context managers

#

it's just getting the return type of __aenter__

karmic zealot
#

So should I subclass the return type of create_client and type it as that?

brazen jolt
#

if the create_client function returns some class that doesn't properly specify what's returned from __aenter__, then you could do multiple things

karmic zealot
#

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)
brazen jolt
#
  1. Subclass it
  2. Create a protocol class to cast it into, leaving the actual runtime class the same
  3. cast the client within each async with block
brazen jolt
#

perhaps it could be worth it to make the ClientCreatorContext generic

#

and use the return type from the passed coro's return type

karmic zealot
brazen jolt
karmic zealot
brazen jolt
#

along with reporting this issue to the lib

karmic zealot
#

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

brazen jolt
#

yeah, that's why I mentioned making it generic

#

but that's something the lib has to handle

karmic zealot
#

I'm going to implement #1, but I'm also curious about #2 could you provide an example or resource

brazen jolt
#

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

karmic zealot
#

So it's the same as #1 just subclassed from Protocol instead?..

brazen jolt
#

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

karmic zealot
#

Okay the Protocol worked perfectly

brazen jolt
#
def create_instance() -> MyProto:
    if random.randint(0, 1) == 1:
        return Bar()
    return Foo()
brazen jolt
karmic zealot
#

There's already a couple issues

brazen jolt
#

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

waxen loom
#

So I find type hinting to be very helpful for programmers. Are there any situations when type hinting wouldn't be recommended?

soft matrix
#

when the type system cannot express the info required

tranquil turtle
#

Type hinting is painful in py2

brazen jolt
#

who on earth is still using python 2

#

it's EOL since january 2020

tranquil turtle
#

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.

soft matrix
#

honestly that doesnt bother me that much

#

i just tune it out automatically

oblique urchin
#

yeah I don't find it too bad either. It does confuse new contributors to typeshed pretty regularly though

rustic gull
#

How is it i print a value to a string?

#

Like it takes a value and sendes it in a discord server

soft matrix
rustic gull
#

Ok sry

cerulean latch
#

is this the only channel related to type checking/type hinting? (im looking more for the overall type checking type stuff

soft matrix
#

type theory?

frigid jolt
#

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?