#type-hinting

1 messages ยท Page 53 of 1

soft matrix
#
SomeBusinessLogicType = float
"""Does this not work"""
dapper yew
#

... should it? let me try that out

#

oh my, it does

#

very nice

#

why does that work?

pastel egret
#

That'd be entirely up to your IDE whether it supports that.

dapper yew
#

Yeah, to be clear it works in pycharm (which is what our team uses) but I'm quite surprised

pastel egret
#

Also, instead of a type alias, you might want to look into typing.NewType - it lets you instead specify the business type is a subclass of that at type check time, so the checker will refuse passing in the original type without conversion.

oblique urchin
#

"String literals occurring immediately after a simple assignment at the top level of a module, class, or init method are called "attribute docstrings"."

dapper yew
dapper yew
fierce ridge
#

great find

#

now i can convince my manager to let me use these, after he told me to stop

oblique urchin
#

glad to be of service, anything else your manager needs convincing about?

pastel egret
#

It is a bit inconsistent runtime-wise, since the interpreter doesn't handle these at all.

fierce ridge
#

yes, please convince him to go back in time 18 months and write tests before putting the application into production, and to draw up a high-level architecture plan too

acoustic thicket
#

i love this

undone carbon
#

how to type hint

#

a func

#

which has 3 args: a, b, c

#

if a is 1

#

then

#

c should be None n only b should be specified

soft matrix
#

overloads

undone carbon
fierce ridge
#

with Literal? you might have a hard time getting a type checker to figure that out

undone carbon
#

yaa

#

lmao

fierce ridge
#

consider not using "magic numbers" for this

#

you can use overload and Literal, but you often cannot statically verify when something is a literal 1 as opposed to any other int

#

why not use an enum instead? you can use IntEnum if you really need it to be an integer

undone carbon
#

enum?

fierce ridge
#

!d enum

rough sluiceBOT
#

New in version 3.4.

Source code: Lib/enum.py

An enumeration is a set of symbolic names (members) bound to unique, constant values. Within an enumeration, the members can be compared by identity, and the enumeration itself can be iterated over.

Note

Case of Enum Members

Because Enums are used to represent constants we recommend using UPPER_CASE names for enum members, and will be using that style in our examples.

soft matrix
#

surely verifying the enum has the same issue

fierce ridge
undone carbon
#

tiz is the code btw

from typing import Literal, overload, Optional, Union

@overload
def foo(a: Literal[1], b: int, c: None = ...) -> int: ...

@overload
def foo(a: Literal[2], c: int, b: None = ...) -> str: ...

@overload
def foo(a: Literal[1, 2], b: Optional[int] = ..., c: Optional[str] = ...) -> Union[int, str]: ...

def foo(a: Literal[1, 2], b: Optional[int] = None, c: Optional[str] = None):
    if a == 1: return b
    elif a == b: return c

reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))

n mypy gives me these errors

untitled.py:12: error: Overloaded function implementation does not accept all possible arguments of signature 2
untitled.py:16: note: Revealed type is "builtins.int"
untitled.py:17: note: Revealed type is "Union[builtins.int, builtins.str]"
undone carbon
fierce ridge
#

actually you might be right @soft matrix , let me try

soft matrix
#

if you are only using this at a top level (ie mypy knows they are literal[1/2]) i think the approach you already have is fine

undone carbon
#

okay

fierce ridge
#

i don't know what i was thinking, of course enum still needs literal ๐Ÿคฆโ€โ™‚๏ธ

undone carbon
#

can u guys explain to me wat's happening lmao ๐Ÿ™ƒ

#

@fierce ridge

fierce ridge
#

is c : int in the 2nd overload correct?

#

did you mean c : str?

undone carbon
#

oh

#

i see

undone carbon
#

ya

#

mypy's working fine now

#

but the error

#

untitled.py:12: error: Overloaded function implementation does not accept all possible arguments of signature 2

#

still occurs

soft matrix
#

you need to make the arguments key word only i think

#

after a

fierce ridge
#

weird error message

fierce ridge
#
from __future__ import annotations
from typing import Literal, overload

@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...

@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...

def foo(a: Literal[1, 2], b: int | None = None, c: str | None = None) -> int | str | None:
    if a == 1:
        return b
    elif a == b:
        return c
    else:
        return None

reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))

this works fine

undone carbon
#

ok

#

thx

soft matrix
#

oh or that ig

undone carbon
#

i m a bit confused with overloads lmao

fierce ridge
#

my understanding of the situation is that mypy was confused by you switching between keyword args and positional args

undone carbon
#

wat's the diff between keyword args n positional args

fierce ridge
#

def f(x, y=1), x is positional and y is keyword

undone carbon
#

oh

#

okay

fierce ridge
#

often shortened to "arg" and "kwarg"

acoustic thicket
#

y would just be an optional argument in this case

#

did you mean just f(x, y=1)

#

as in while calling it

fierce ridge
#

i'm fudging a bit

#

people refer to them by those terms

#

but yeah, more precisely y is a parameter with a default argument

solid light
acoustic thicket
#

if you have def f(x, y=1) you could still pass y by position, f(1, 2) would be valid

soft matrix
acoustic thicket
soft matrix
#

but it doesnt matter

undone carbon
#

all the helpers n mods r here lmao

fierce ridge
#

we like types and type hinting

undone carbon
#

lol

acoustic thicket
#

the s in my nickname is for static typing

undone carbon
#

nah

#

it's for stinky

#

:>

solid light
undone carbon
#

lol

#

wat does the error missing return statement mean

#

def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str]:

solid light
undone carbon
#

nope

#

not the case

solid light
#

What tool are you using? mypy? @undone carbon

undone carbon
#

yes

#

mypy

solid light
#

That might help?

undone carbon
#

i solved the prob

#

i think it's cos

#

nah

#

i dun know how to explain

#

just show ya the code

#
from typing import Literal, overload, Optional, Union

@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...

@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...

def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str]:
    if a == 1:
        if b: return b
    elif a == 2:
        if c: return c

reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
#

tiz is the initial code

#

which caused the error

#
from typing import Literal, overload, Optional, Union

@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...

@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...

@overload
def foo(a: Literal[1, 2], b: Optional[int] = ..., c: Optional[str] = ...) -> Union[int, str]: ...

def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None):
    if a == 1:
        if b: return b
    elif a == 2:
        if c: return c
    # else:
    #     return None

reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
solid light
#

Yeah

undone carbon
#

n tiz solved it

solid light
#

It's the same issue as what I linked

undone carbon
#

oh

#

ya

#

thx

solid light
#

It doesn't always reach a return

#

You need the else

undone carbon
#

oh

#

okay

#

ah

#

yes

#

i understood now

#
from typing import Literal, overload, Optional, Union

@overload
def foo(a: Literal[1], b: int = ..., c: None = ...) -> int: ...

@overload
def foo(a: Literal[2], b: None = ..., c: str = ...) -> str: ...

def foo(a: Literal[1, 2], b: Union[int, None] = None, c: Union[str, None] = None) -> Union[int, str, None]:
    if a == 1:
        if b: return b
    elif a == 2:
        if c: return c
    return None

reveal_type(foo(1, 1))
reveal_type(foo(2, c='2'))
#

u mean tiz?

#

@solid light

solid light
#

Well technicallypy else: return None

#

Or actually yeah

#

You need it how you put it

#

So that works aspy return b if a == 1 and b else c if a == 2 and c else None

undone carbon
#

else: return None still gives me tat error

#

no idea why

solid light
undone carbon
#

got it

#

anyways thx a lot

solid light
#

๐Ÿ‘

undone carbon
#

so u normally use overload to list down all the possible combinations of type hints? @solid light

#

not really sure how to phrase ma question lol

solid light
#

No clue

#

I know basically nothing about typehinting lol

undone carbon
#

lol

#

so u normally use overload to list down all the possible combinations of type hints?

soft matrix
#

generally you shouldnt have tonnes of overloads to a function with different params and returns generally its a sign of bad design

#

and you should probably make separate functions

upbeat wadi
#

https://mystb.in/MeetupThongPaid.python
is it possible for me to keep the parameter info (ie so the type checker knows that self doesn't have to be passed if the method is called from an instance)? or would I have to remove the _P.args/kwargs in async def __call__(self, *args: _P.args, **kwds: _P.kwargs) -> _T: ...?

blazing nest
#

But the type checker is correct

upbeat wadi
#

the code would run without error at runtime, i wasn't sure how to type it correctly though

blazing nest
#

!e ```python
def deco(func):
func('One arg')

class Test:
@deco
def some_method(self, arg):
print(arg)

t = Test()

rough sluiceBOT
#

@blazing nest :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 4, in <module>
003 |   File "<string>", line 6, in Test
004 |   File "<string>", line 2, in deco
005 | TypeError: Test.some_method() missing 1 required positional argument: 'arg'
blazing nest
#

That decorator is getting the function before it becomes bound

#

Exactly like the static type checker is complaining about

upbeat wadi
#

ah, so I would have to drop the paramspec for the __call__ to get it to not complain?

blazing nest
#

I am saying that the code doesn't work

upbeat wadi
#
In [1]: from async_lru import alru_cache

In [2]: class A:
   ...:     @alru_cache
   ...:     async def foo(self, a: int):
   ...:         return a + 2
   ...:

In [3]: a = A()

In [4]: await a.foo(3)
Out[4]: 5```
blazing nest
#

I don't follow when it becomes bound though ๐Ÿค”

soft matrix
#

it takes the args from the call

#

so self is injected by python

upbeat wadi
#

yeah

#

it would be easier to return the callable itself but then I lose the information about the methods/attributes added to it

soft matrix
#

you probably need descriptor behaviour here

#

this is definitely doable but i think you need like 2 classes

upbeat wadi
#

alright thanks

#

tried py def __get__(self) -> Callable[_P, _Coro[_T]]: ... instead of the __call__ but it doesn't recognize the decorated function as a method of the instance then

#

oh right that's not how get works

blazing nest
upbeat wadi
soft matrix
#

this is mostly correct

#

im pretty sure the last bit not working is a pyright bug

upbeat wadi
#

Ah, ill try seeing if I can repro it again

soft matrix
upbeat wadi
#

Ah okay, i was using Self earlier but then removed it

soft matrix
#

or something along those lines

upbeat wadi
#

I'll try that out thanks

soft matrix
#

well i just changed _wrapped_alru_cache to subclass _callable_alru_cache cause you dont have to use it in a class right?

upbeat wadi
#

yeah

#

just wondering, why use that # type: ignore for the functools import?

soft matrix
#

pyright gave me grief about it being private

upbeat wadi
#

oh, it didn't for me

#

are you on strict?

upbeat wadi
soft matrix
upbeat wadi
#

makes sense then

#

thanks for the help

soft matrix
#

oh wait i got it working

#

you were passing args and kwargs to get

#

oh that actually doesnt work still

#

it says it doesnt take any positional args

upbeat wadi
#

ah i missed that

#

it works when the function isn't in a class so that's weird

#

this signature doesn't seem correct (for args/kwargs)

#

the return type should be int and not _T@... as well

soft matrix
#

maybe just ask eric

upbeat wadi
#

sure ill make a discussion

upbeat wadi
undone carbon
#

how to type hint not None?

oblique urchin
little hare
#

!d typing.Any ?

rough sluiceBOT
#

typing.Any```
Special type indicating an unconstrained type.

โ€ข Every type is compatible with [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any").

โ€ข [`Any`](https://docs.python.org/3/library/typing.html#typing.Any "typing.Any") is compatible with every type.
little hare
#

oh, that can be None, still

soft matrix
#

I would hope that would be included with Intersection if that ever happens

undone carbon
#

Sad...

oblique urchin
#

No, that's different from intersection

#

Intersection is being discussed on the python/typing tracker

jagged sphinx
#

is this the correct channel for this?

#

What keybinding do you guys use for arrow keys?

trim tangle
undone carbon
#

is there any way to remove a mypy error?

#

it feels annoying

#

oh

#

i found it

#

just use # type: ignore

spare mauve
trim tangle
visual loom
#

How can I add type hints to attributes that are added dynamically when overriding __getattr__
Imagine the following classes. I would like to have ide auto-complete/type hints for the topping_cost method.

class Topping:
    def __init__(self, name: str) -> None:
        self.name = name

    def topping_cost(self):
        return 5

class Pizza:
    def __init__(self, topping: Topping) -> None:
        self.topping = topping

    def __getattr__(self, attr):
        if hasattr(self.topping, attr):
            return getattr(self.topping, attr)
        raise AttributeError(f'Missing attr {attr!r}')


mushroom_topping: Topping = Topping('mushrooms')
pizza: Pizza = Pizza(mushroom_topping)
print(pizza.topping_cost())      <================ I need IDE's to recognize that topping_cost is available to Pizza. How can I type hint this?
soft matrix
#

there isnt a good way to do this

#

well with autocompletion, type checkers shouldnt error there

#

but without something like a mypy plugin you cant do this

visual loom
#

thanks @soft matrix
ok, that's disappointing. How can I create a mypy plugin, can you please point me to a good resource?

visual loom
#

thanks.

trim tangle
#

I tried making a mypy plugin

#

I didn't go very far...

#

maybe I'm just stupid

undone carbon
#

lolol

undone carbon
#

can i do smth like tiz?

foo = Union[str, int]
def func(arg: foo) -> None: ...
#

is it right?

#

@pastel egret

pastel egret
#

You can yes, it's a "type alias".

undone carbon
#

okay

#

vscode wouldnt give me the hints tho

#

it would just show me arg: foo which kinda sucks cos i need it to show tat arg: Union[str, int]

oblique urchin
#

I think that's intentional, usually people want to see the type alias name

undone carbon
#

okayy

acoustic thicket
#

how do you typehint frozensets
is it like tuples, where frozenset[int, int] would mean a frozenset of exactly two ints

#

hmm apparently it expects only one type argument

#

so how does one typehint an unordered pair of integers

pastel egret
#

Frozensets just have 1 arg, which is whatever's contained in them. Tuples are unique in that you can specify a specific length, for frozensets you'll have to do frozenset[int] for any number.

#

You could also use a tuple[int, int], sorting the contents first, perhaps.

acoustic thicket
#

hmm

solid sleet
#

why can't you specify a specific length for a frozenset

#

it's not like you can add to them

pastel egret
#

Since there's no order, a frozenset[int, str] would be pretty useless, you wouldn't have a way to extract just the string to use it? And the type system didn't have a way to specify specific integers or the like, so it'd be kinda unique?

solid sleet
#

the order doesn't matter though

#

i don't understand

#

and you can still extract the string

#

a, b = my_frozenset; my_string = a if isinstance(a, str) else b

#

it is kind of weird to have two different types in a set though

#

but i'm not a typing guy

#

i prefer no types to complicated types

pastel egret
#

I mean with tuples we do often care about the order, but you canโ€™t do that with sets.

tranquil turtle
#

what am i doing wrong? (mypy 0.910)

pastel egret
#

It looks like MyPy hasn't quite implemented support yet, though make sure you're updated to the latest version.

crimson raft
#
    @overload
    def execute(self, query: str, *args, fetch_all: Literal[True]) -> SQLResults:
        ...

    @overload
    def execute(self, query: str, *args, fetch_all: Literal[False]) -> SQLResult:
        ...

    def execute(
        self, query: str, *args, fetch_all: bool = True
    ) -> SQLResults | SQLResult:
        with self.connection as cursor:
            result: Cursor = cursor.execute(query, args)
            if fetch_all:
                return result.fetchall()
            return result.fetchone()```
is this how this should be written?
i feel like it's wrong since fetch_all is optional that the overloads shouldn't be like that, but if i set it to `fetch_all: Literal[True] = True` it'll error that the overloads' signatures don't match the return type
#

i don't exactly have an error per-se, just advice

#
SQLResult = tuple
SQLResults = list[SQLResult]```
for reference of those explicit types
#

fetchall returns a list of tuples, fetchone returns a tuple

pastel egret
#

Flip the order of those two around, then omit fetch_all entirely in the true case?

#

Would it be better to just make these two different methods anyway?

eternal rivet
#

Hi everyone, I am having trouble with my abstract class inheritance. Basically, I want to inherit from an abstract class that takes another abstract class as argument, but how can I make the attributes of its implementation recognized correctly its argument types? Is it confusing? Here is what I mean:

from abc import ABC
from abc import abstractmethod
from dataclasses import dataclass


# Define abstract classes
class Params(ABC):
    """An abstract class for storing parameters."""

    pass


class Solver(ABC):
    """An abstract class to solve a problem with a specific parameter set."""

    def __init__(self, params: Params) -> None:
        """Initialize with a Params object."""
        self.params = params

    @abstractmethod
    def solve(self) -> None:
        """An abstract method that must be implemented in the subclasses."""
        pass


# Implementation of abstract classes
@dataclass
class ParamsX(Params):
    """A subclass of Params which has attributes first_param and second_param."""

    first_param: int
    second_param: list[float]


class SolverX(Solver):
    """A subclass of Solver which solve the problem X."""

    def __init__(self, params: ParamsX) -> None:
        """Initialize with the ParamsX object."""
        super().__init__(params)

    def solve(self) -> None:
        """Solve the problem X."""
        print(self.params)  # The params is recognized as Params
        print(self.params.first_param)  # The first_param is recognized as Any
        print(self.params.second_param)  # The second_param is recognized as Any


# There're also ParamsY and SolverY, ParamsZ and SolverZ, etc

My question: How can I make the first_param and second_param show respectively as the int and list[float] types? I haven't check with mypy but the Visual Studio Code indicates that.

#

An attempt: If I do with the following code, the types are correct but I don't think it's a pythonic way, it will be like the abstract class Params doesn't have any purpose at all.

class SolverX(Solver):
    """A subclass of Solver which takes a ParamsX object as argument."""

    def __init__(self, params: ParamsX) -> None:
        super().__init__(params)
        self.params: ParamsX  # Forced annotation
pastel egret
#

What you need is a Generic, not necessarily an ABC.

#

Change Solver to be like this:

ParamT = TypeVar('ParamT', bound=Params)

class Solver(ABC, Generic[ParamT]):
    def __init__(self, param: ParamT) -> None: 
        self.params = param

class SolverX(Solver[ParamsX]):
    ....
#

TypeVar defines a type variable, which you later substitute with a specific type. bound restricts it to only permit subclasses of the specified type. Then having the class "subclass" Generic means that the variable is scoped to the whole class. So then when you use the subscripted version, it's as if the type variable was replaced by the real class.

#

Note the other way you can use it is specific to a function - you put it in the signature, then it takes on whatever type you use to call the function.

eternal rivet
#

Wow man, I took a whole day to find this answer, I even posted a Stack Overflow question ๐Ÿคฃ . Thank you so much for the answer, and the detailed explanation too. Really appreciate that!

#

I guess I have to read the docs one more time, maybe two ๐Ÿ˜†

pastel egret
eternal rivet
#

Cool! Thanks a lot. Can I give you a credit on Stack Overflow post?

pastel egret
#

Sure!

blazing nest
#

Though I understand that this doesn't correctly tell the type checker that there is 1 X and 1 Y

pastel egret
#

Well, even if you did what could it do with that information?

#

Even if you say unpack it has to be a union since it can't know which is which.

buoyant swift
subtle creek
buoyant swift
#

hm, both of those also could mean a tuple with two of the same types

brisk hedge
#

You can't enforce typevars representing different types

craggy lantern
buoyant swift
#

yeah it could

#

A and B could be the same

craggy lantern
#

yeah

#

Union[Tuple[A, B], Tuple[B, A]] could perhaps work (depending on use case) if there was a way to express the constraint that A and B are are not equal

#

but there isn't in Python

blazing nest
#

Are A and B type variables or just "something to replace"?

#

Union[Tuple[int, str], Tuple[str, int]] will error if you try to pass Tuple[int, int] or Tuple[str, str]

craggy lantern
subtle creek
craggy lantern
subtle creek
craggy lantern
#

I only jumped in because I was interested in the abstract problem ๐Ÿ™

blazing nest
#

Hmm, still if you just give the generic types then it should work fine

craggy lantern
blazing nest
#

Yeah you can't enforce that someone doesn't do TheGeneric[int, int]

craggy lantern
#

yeah. defo not right to use a frozenset for that

#

well I mean unless you want to have business logic outside your type signature which is fine too

craggy lantern
buoyant swift
#

๐Ÿ˜”

#

the woes of a bolted on type system

acoustic thicket
#

ie an unordered pair of vertices

#

frozenset made sense to me because its the only unordered and fixed size structure i could think of

upbeat wadi
trim tangle
#

besides Agda

#

I think it's a really fringe use case

trim tangle
acoustic thicket
#

because each edge is between two vertices

trim tangle
#

ah

#

then you'd need the type checker to understand the semantics of equality with your types @acoustic thicket

#

because you don't know if {a, b} has size 1 or 2

acoustic thicket
#

wdym

trim tangle
acoustic thicket
#

oh true

trim tangle
#

So if you write {Vertex(...), Vertex(...)}, the type checker would need to have some knowledge of how == works on vertices

#

and if you don't construct them in-place, well, then it just can't know if they're equal

acoustic thicket
#

hm makes sense

#

so that brings me back to
how do you represent an unordered pair

trim tangle
#

I'd just use a tuple

acoustic thicket
#

hmm

#

yeah i dont think thatll lead to any problems for me

buoyant swift
#

you could also have both edges (a,b) and (b,a)

trim tangle
buoyant swift
#

ยฏ_(ใƒ„)_/ยฏ

leaden oak
#

Does mypy recognize

import sys

if sys.version_info >= (3, 7, 0):
    fixed_code ...
else:
    deprecated_code ...

because I just tried this and it was still warning. Removing the last item in the tuples fixed it ๐Ÿค”

trim tangle
#

oops

#

!e

print((3, 7) >= (3, 7, 0))
print((3, 7) >= (3, 7))
rough sluiceBOT
#

@trim tangle :white_check_mark: Your eval job has completed with return code 0.

001 | False
002 | True
leaden oak
#

Yeah, but doesn't sys.version_info contain the micro value too?

oblique urchin
#

Type checking only supports two elements in the version tuple

#

Micro versions don't exist

leaden oak
#

sorry, bugfix version

#

!eval

import sys
print(sys.version_info)

although it's called micro here

rough sluiceBOT
#

@leaden oak :white_check_mark: Your eval job has completed with return code 0.

sys.version_info(major=3, minor=10, micro=0, releaselevel='final', serial=0)
oblique urchin
#

Oh I always forget which one is which

trim tangle
#

tbh

oblique urchin
#

I'm actually not sure what the motivation for this was, maybe implementation simplicity

leaden oak
#

it'd be usually fine except for maybe typing which was unstable in the 3.5.x and 3.6.x series

#

thankfully my usecase isn't impacted :)

oblique urchin
#

Like you can pass --python-version 3.6 to mypy but mypy wouldn't want to have to remember every single micro version

oblique urchin
leaden oak
#

Black dropped support for 3.6.0 and 3.6.1 due to typing's changes during its provisional period so yeah

river hollow
#

Can you change a functions annotations not in runtime

#

and have them effect the type checker?

#
async def foo(arg_one: int) -> str:
    return arg_one.capitalize() # I dont want pyright to get mad at me

foo.__annotations__['arg_one'] = str

>> await foo('wee')
'Wee'
#

(more specifically with pyright).. pls ping on reply im going to bed

trim tangle
#

@river hollow That won't work. Why not just do this? ```py
async def foo(arg_one: str) -> str:
return arg_one.capitalize()

river hollow
#

In actual use, that wonโ€™t work

#

When I wake up tmrw I can provide more context if you need

tranquil turtle
#

Use overload

soft matrix
#

Update pycharm but this isn't the right place for questions like this maybe try #editors-ides?

craggy lantern
#

and the size can't be determined statically

#

because no duplicates

acoustic thicket
#

yeah, i realised

stone hemlock
#

Opinions on mypy vs pyright ? Personally I am using mypy in every day programming but 5inking to switch to pyright

trim tangle
tranquil turtle
#

what am i doing wrong?

buoyant swift
#

what version are you using? that only works in 3.10

tranquil turtle
#

python 3.10

#

mypy 0.910

#

code runs without errors

buoyant swift
#

not sure then, seems like a mypy issue

tranquil turtle
#

okay...

buoyant swift
#

i'd check if mypy supports that expression

tranquil turtle
#
dict[Union[str, int], Union[int, str, bytes]]
```this fixes the error
buoyant ridge
#

An example function for context:
def make_inst(cls):
return cls()
How would one type hint this to say that it takes any class and returns an instance of that class?
(I believe I'd use typing.Type for the class vs. class instance issue, but i don't know how to represent 'any class')

trim tangle
#

If you want to support any class that has a no-param init, you could accept any callable that takes no arguments: ```py
T = TypeVar("T")

def make_inst(cls: Callable[[], T]) -> T:
return cls()

buoyant ridge
#

oh i see, i actually need to represent any sort of initialisation then

#

actually, the real function im trying to hint isn't much more complex

#

it just uses inspect.getmembers to get all members of a certain type from a module object

#
C = TypeVar('C', object) # this is syntactically incorrect
def get_objects(module: ModuleType, cls: Type[C]) -> List[C]:
    members = inspect.getmembers(module, lambda obj: isinstance(obj, cls))
    return [i for _, i in members]
#

would i just need to use TypeVar('C')?

#

well thinking about it that would work... but im not sure it's tight enough? like would Type[C] ensure only classes can be used in the cls parameter?

trim tangle
#

remove object

#

Type[C] should ensure that, yes

buoyant ridge
#

awesome, ok

#

thank you!

hallow flint
hallow flint
oblique urchin
undone carbon
#

would beartype give me an error when using type alliases ?

#

@pastel egret

pastel egret
#

No, since it's at runtime, those would just get evaluated.

#

If you're using forward references though, and called the function before that reference was actually defined that'd break...

undone carbon
#

okay thx

twilit badge
#

How should I type-hint a Union of the exact object of discord.embeds.Embed.Empty or a str. Embed.Empty is already initialized from discord.embeds._EmptyEmbed. It is expected to be used directly so I wouldn't really like to just type-hint with discord.embeds._EmptyEmbed

#

I considered something like Literal[Embed.Empty] but that's probably missusing Literal since it was only for str, int or enum values

trim tangle
#

@twilit badge You can't really do that. You'll have to typehint it as _EmptyEmbed. Or just accept None and access Embed.Empty in case of None

twilit badge
#

oh, why is that impossible though?

#

I'd expect there to be a way to type-hint singletons like this

trim tangle
#

Literal works for int, str, bytes, tuple and Enum literals.

#

there isn't any way to type-hint your own singletons

#

you can make a PEP ๐Ÿ™‚

twilit badge
#

hmm, yeah it would certainly be a nice feature imo

little hare
trim tangle
#

hm?

twilit badge
trim tangle
#

I think type checkers don't allow calls in annotations

little hare
#

ah

blazing nest
little hare
#

thatttttt

#

not the (), the []

brisk hedge
#

That won't work, because Embed.Empty is not a type

#

That's the whole point isn't it

#

Annotate it with _EmptyEmbed or use your own proxy singleton

oblique urchin
#

Or wait for PEP 661

leaden oak
#

!pep 661

rough sluiceBOT
#
**PEP 661 - Sentinel Values**
Status

Draft

Created

06-Jun-2021

Type

Standards Track

trim tangle
indigo locust
#

Question about typing.overload

#

Its a decorator that exists simply as an indicator, right? What's the point of having it if there is no concrete implementation?

#

Oh, is it simply to map return types to input types, with the concrete implementation doing some sort of if-else check and returning as needed?

void panther
#

Yes, it's there to provide overloads for signatures that wouldn't otherwise be possible without it. The function does all of the logic itself through

#

e.g. when you accept an int argument the function does something and returns ClassA and when it accepts a str it returns ClassB; without the overload you'd just have unions which doesn't quite capture how the function behaves

indigo locust
#

For sure!

pastel egret
#

Another use is with functions that only allow specific combinations of optional arguments or other weird things - range() for instance.

oblique urchin
#

By default, yes. You can use the --allow-redefinition flag to allow this though

rustic gull
#

interesting, thanks i didnt know that flag

upbeat wadi
#

class A:
    @alru_cache
    async def in_class(self, a: int) -> int:
        return a + 1

async def main():
    reveal_type(A.in_class)
    reveal_type(A().in_class)
    await A().in_class(1) # Should not have an error
    await A.in_class()  # Correct error
    await A().in_class._origin(1)  # Should only say a is missing```
#

this is the most confusing typing i've done blobpain

#

eric also mentioned __p0 (the first argument it expects us to pass when the method is accessed from an instance) though I don't really understand why that's there

#

replacing Concatenate[_O, _P] with just _P gives this
the first error is the incorrect one

main galleon
#

Hey I would be eternally grateful if someone could help me out with this..
I have a class where I'm inheriting from requests.Session
I've overridden the request method, but I'm not changing the method signature at all.. I'm really just setting a new flag that I'm adding to the class essentially.
Problem is mypy wants me to add typing to my method arguments, of which the original request method has many.
What is the best way to handle this?
I feel like there has to be a better way, since when I look at the methods signature, I see this:

#
_Data = Union[None, Text, bytes, Mapping[str, Any], Mapping[Text, Any], Iterable[Tuple[Text, Optional[Text]]], IO[Any]]

_Hook = Callable[[Response], Any]
_Hooks = MutableMapping[Text, List[_Hook]]
_HooksInput = MutableMapping[Text, Union[Iterable[_Hook], _Hook]]

_ParamsMappingKeyType = Union[Text, bytes, int, float]
_ParamsMappingValueType = Union[Text, bytes, int, float, Iterable[Union[Text, bytes, int, float]], None]
_Params = Union[
    SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType],
    Tuple[_ParamsMappingKeyType, _ParamsMappingValueType],
    Iterable[Tuple[_ParamsMappingKeyType, _ParamsMappingValueType]],
    Union[Text, bytes],
]

def request(
    self,
    method: str,
    url: str | bytes | Text,
    params: _Params | None = ...,
    data: _Data = ...,
    headers: MutableMapping[Text, Text] | None = ...,
    cookies: None | RequestsCookieJar | MutableMapping[Text, Text] = ...,
    files: MutableMapping[Text, IO[Any]] | None = ...,
    auth: None | Tuple[Text, Text] | _auth.AuthBase | Callable[[PreparedRequest], PreparedRequest] = ...,
    timeout: None | float | Tuple[float, float] | Tuple[float, None] = ...,
    allow_redirects: bool | None = ...,
    proxies: MutableMapping[Text, Text] | None = ...,
    hooks: _HooksInput | None = ...,
    stream: bool | None = ...,
    verify: None | bool | Text = ...,
    cert: Text | Tuple[Text, Text] | None = ...,
    json: Any | None = ...,
) -> Response: ...
acoustic thicket
#

like, do you just have a single statement in the function and a super().request(*args, **kwargs)

main galleon
#

No there's a bit more im doing to proxy the request. But my main question is with typing. How should I handle the typing in q situation where I'm overriding a method?

#

The signature is the same though. Same method arguments and return value

soft matrix
solid light
#

How do I typehint an exception? I tried : Exception but that doesn't work.

Example: https://mypy-play.net/?mypy=latest&python=3.9&gist=e3040015a7e036b633832002c2cb4c3b

class Foo(ValueError):
    pass

def perform_test(expected_error: Exception) -> str:
    return "WIP"

print(perform_test(Foo))
```Gives the error `Argument 1 to "perform_test" has incompatible type "Type[Foo]"; expected "Exception"`
blazing nest
#

Somewhere in types at least

solid light
#

main.py:1: error: Module "types" has no attribute "BaseException"

void panther
#

type[Exception] (or BaseException) should work

blazing nest
void panther
#

It's a builtin

blazing nest
#

Ooh, nvm. Just remove the import

solid light
#

type[Exception] does indeed work, thanks ๐Ÿ‘

#

Why does it need to be type[Exception] and not just Exception?

blazing nest
#

I was confused because when type annotating an __aexit__ you had to import TracebackType from types

oblique urchin
#

Yes, the problem is the missing type[]. Exception means an exception instance, not an exception class.

#

so perform_test(Foo()) would be legal but perform_test(Foo) is not

oblique urchin
solid light
#

Ah right

solid light
#

And how would I annotate a function?

#

Thought maybe FunctionType but apparently not

oblique urchin
#

Callable

solid light
#

From typing?

oblique urchin
#

yes, or collections.abc on recent versions

solid light
#

On 3.9 so should I use collections?

void panther
#

FunctionType would work if you only wanted a function, which usually is not what you want and is more limited

#

yes

oblique urchin
rustic gull
#

type hinting, isnt that what we all need?

oblique urchin
#

The type system basically doesn't know that functions are instances of types.FunctionType

solid light
#

Anyway to fix this?

rustic gull
#

its like in typescript?

oblique urchin
#

easy workaround is to use typing.Type instead

solid light
#

Ah right

#

Thanks

#

It is indeed PyCharm

oblique urchin
solid light
#
from collections.abc import Callable
from typing import Optional, Type

from errors import BodyError, InvalidLoginCredentials
from interactions import login


class TestFailure(Exception):
    def __init__(self, test_name: str, message: str):
        self.test_name = test_name
        self.message = message
        super().__init__(self, f"Test {test_name!r} failed - {message}")


class UnexpectedErrorMessage(TestFailure):
    def __init__(self, test_name: str, expected_message: str, actual_message: str):
        self.expected_message = expected_message
        self.actual_message = actual_message
        super().__init__(test_name, f"Expected message {expected_message!r} but got {actual_message!r}.")


class ExceptionPlaceholder(Exception):
    pass


def perform_test(
    test_name: str,
    function: Callable,
    *args,
    expected_error: Type[Exception] = ExceptionPlaceholder,
    expected_error_message: Optional[str] = None,
    **kwargs,
):
    print(kwargs)
    try:
        function(*args, **kwargs)
    except expected_error as e:
        if (actual_error_message := e.args[0]) == expected_error_message:
            print(
                f"Test {test_name} passed! Got expected error type and message "
                f"({expected_error.__name__}, {expected_error_message!r})"
            )
        else:
            raise UnexpectedErrorMessage(test_name, expected_error_message, actual_error_message)


def test_login():
    perform_test(
        "Login (No Username)",
        login.login,
        "", "pass",  # username, password
        expected_error=BodyError,
        expected_error_message="`username` not provided"
    )
```Now have the first steps to a super basic testing suite
#

Still need to add a bit more though, like an UnexpectedErrorType(TestFailure)

void panther
oblique urchin
void panther
#

Well, at least the auto completion with it works

blazing nest
rustic gull
#

anyone knows how i can
type annotate a list with an object in it that has "mode" and "value" as params ?

soft matrix
#

use a protocol

class ModeValueParams(Protocol):
    def __init__(self, mode, value) -> None:
        ...

# then do
list[ModeValueParams]
rustic gull
#

Thank you, i will try it even though it looks confusing compared to typescript

rustic gull
oblique urchin
soft matrix
#

from typing import Protocol

rustic gull
#

oh typing, i tried types/type

#

also what do the 3 dots mean? or are they supposed to be code?

soft matrix
#

you might be better changing __init__ with __call__ but i think thats what asked for

#

the 3 dots are

#

hmm

vast olive
#

!d Ellipsis

rough sluiceBOT
#

Ellipsis```
The same as the ellipsis literal โ€œ`...`โ€. Special value used mostly in conjunction with extended slicing syntax for user-defined container data types. `Ellipsis` is the sole instance of the [`types.EllipsisType`](https://docs.python.org/3/library/types.html#types.EllipsisType "types.EllipsisType") type.
rustic gull
#

thanks

soft matrix
#

its the basically the same as pass

#

but its more a sign of nothing is supposed to be here

rustic gull
#

also how do i use it?

vast olive
#

i've actually seen people use it as an indication that something is supposed to be here (but isn't rn)

soft matrix
#

i think this is similar to typescript

rustic gull
#

like this? it s till gives me an error

soft matrix
#

and it tells you one of the ways to fix it

rustic gull
#

sorry i have no clue how to fix it :/

soft matrix
#

so do "list[ModeValueParams]"

rustic gull
#

Thank you. didnt knew that strings now also can be read different

trim tangle
#

@soft matrix @rustic gull if you're using Python <3.9, you should use List[ModeValueParams]

#

(where List is from typing)

#

string-based annotations will break if you inspect them

soft matrix
#

they dont they go to forward refs

#

oh not without future annotations

#

oh maybe they dont

#

ยฏ_(ใƒ„)_/ยฏ

trim tangle
#

@soft matrix if you try to evaluate list[ModeValueParams] (for example, if you put that as an annotation for pydantic), it will obviously break\

#

And there are lots of edge cases with the new annotation system. For example:

def foo(key: K):
    Item = Dict[str, K]
    def function(strings: List[str], default: K) -> "Item":
        return {s: default for s in strings}
    return function

bar = foo("quack")

here there's no way to correctly evaluate Item

oblique urchin
#

PEP 649 to the rescue

trim tangle
#

!pep 649

rough sluiceBOT
#
**PEP 649 - Deferred Evaluation Of Annotations Using Descriptors**
Status

Draft

Created

11-Jan-2021

Type

Standards Track

trim tangle
#

oh right

upbeat wadi
#

is = ... or = the_actual_default better practice for optional arguments in overloads (not in a stub file)?

oblique urchin
upbeat wadi
#

thanks

#

is Union[Type[T1], Type[T2], Type[T3]] or Literal[T1, T2, T3] better practice?

oblique urchin
upbeat wadi
#

i see, probably should have tried it before asking, thanks again

soft matrix
#

You know if you're in stubs you can use 3.10 syntax for stuff right?

little hare
#

wdym?

upbeat wadi
#

yeah, this isn't in stubs

#

it's in the actual file

#

why would GenericClass[T1] not match the type GenericClass[T1 | T2 | T3]? (I know if the type was GenericClass[T1] | GenericClass[T2] | GenericClass[T3] it would successfully type check, but this seems like weird behaviour)

oblique urchin
upbeat wadi
#
ApplicationCommandOptionChoiceType = TypeVar('ApplicationCommandOptionChoiceType', bound=Union[str, int, float])
class ApplicationCommandOptionChoice(Generic[ApplicationCommandOptionChoiceType]):
  ...

@overload
def application_command_option(
    *,
    choices: Optional[Union[
        List[ApplicationCommandOptionChoice[int]],
        List[ApplicationCommandOptionChoice[float]],
        List[ApplicationCommandOptionChoice[str]],
    ]] = ...,
    min_value: Optional[Union[int, float]] = ...,
    max_value: Optional[Union[int, float]] = ...,
) -> Any: ...```would there be a way to type this so these rules are followed?
if the type of `choices` is `List[ApplicationCommandOptionChoice[int]]`, `max/min_value` should take an `int`
if the type of `choices` is `List[ApplicationCommandOptionChoice[float]]`, `max/min_value` should take a `float`
if the type of `choices` is `List[ApplicationCommandOptionChoice[int]]`, `max/min_value` should NOT be passed
craggy lantern
#

anyway, I think what you want is generics

main galleon
#

Say I'm using a 3rd party package and I'm making a class that inherits from one of their classes. For the methods that belong to the inherited class, do I rewrite all the type declarations? Or is there a better way to do it?

oblique urchin
upbeat wadi
#

oh it falls back to the float overload because the int overload doesn't work

#

would there be any way of preventing that? or no since float accepts int

indigo locust
#

How do instance vs class attributes work in stub files?

#

Same as in normal python, with class attributes in the class scope, and instance attributes defined in the constructor?

pastel egret
#

Both go at the class scope, to indicate a class attr you use typing.ClassVar[type].

spiral garnet
#

Yo, I actually want to create a mother type of two types like this : temp_var: IntOrStr = ""

#

Do you have an idea ?

spiral fjord
#

in 3.10 you can use |, in older versions there is typing.Union @spiral garnet

spiral garnet
#

Yes i know but i want to create a type that is basically an union

#

to avoid writing union everytime

stray summit
#

whats the correct way to annotate a nested datastructure with DATA_OPTIONS = Dict[str, Union[str, "DATA_OPTIONS"]] mypy complains about a cyclic specification

void panther
#

I think that's only a mypy limitation, the hint itself is correct

void panther
spiral fjord
tranquil turtle
stray summit
spiral garnet
tranquil turtle
stray summit
#

@tranquil turtle i see, so the declaration for older python would roughly be the same just use Union?

stray summit
tranquil turtle
soft matrix
#

Idk just suck it up not working on mypy and use DATA_OPTIONS: TypeAlias = "dict[str, str | DATA_OPTIONS]"

blazing nest
#

You aren't closing the DAG(... parenthesis

rustic gull
#

huh?

#

it's a function

solid light
blazing nest
solid light
#

It could be you the trailing , when there's no more arguments @rustic gull

rustic gull
solid light
#

After the =None

blazing nest
#

Besides, the error is in the package and not your code ๐Ÿคทโ€โ™‚๏ธ

rustic gull
#

oh

#

lemme try

#

ya its in the package, that's unfortunate i guess

solid light
#

The error is in /spec.py

#

Of the connexion library

#

Not the code you sent

rustic gull
#

i can post it lemme remember how to dump text

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

solid light
#

The error is L169 which ispy spec.setdefault('consumes', ['application/json']) # type: List[str]

#

So I'd assume it doesn't like the # type: List[str]

oblique urchin
solid light
#

Yeah, that makes sense

#

Cc @rustic gull

trim tangle
#

Am I right that type checkers don't really understand cython code?

#

I wanted to add stub files to lupa

oblique urchin
brazen jolt
#

How would I type-hint an object that defines __str__?

#
def foo(x: StrConvertable):
    return str(x)
buoyant swift
#

isn't that all objects?

brazen jolt
#

oh, I didn't realize python just supports this for everything, so I suppose I can just do x: Any

tranquil turtle
buoyant swift
#

although in general you would use protocols

granite plover
#
self.m_processes = queue.Queue()
self.current_process = None
#

why does this need a typehint

#

hovering over self.m_processes correctly shows a type of Queue

#

but mypy errors

oblique urchin
granite plover
#

ah, ok

#

ok one more question
what does this mean
No overload variant matches argument type "Frame"

#

woops, left stuff out

ClassesUI\share_price_download.py:22: error: No overload variant matches argument type "Frame"
ClassesUI\share_price_download.py:22: note: Possible overload variants:
ClassesUI\share_price_download.py:22: note:     def (master: None = ...) -> Tcl_Obj
ClassesUI\share_price_download.py:22: note:     def (master: Union[Wm, Tcl_Obj]) -> None
trim tangle
#

@granite plover Can you show the line?

granite plover
#

i sorted it out
the line was

self.transient(parent)

when it should have been

self.transient()
#

woohoo!

Success: no issues found in 63 source files
buoyant furnace
#

Hey guys. Been hitting my head against a problem for awhile now and haven't had any luck, so I'm trying to get some help. I have several classes that are derived from one. These classes inherit a specific method from their parent that intends to return an instance of the same type as the class. I'm using CPython so I'm not fighting against a type-checker, but I want to hint it in such a way that a language server will provide proper on-hover, autocompletion info, etc. on the return without needing to redefine the method on every subclass. For example:

class Base(metaclass=ABCMeta):
    # The return of this will actually never be `Base` because the class is
    # abstract. It will always be a subclass.
    def get_random_instance(self) -> 'Base':
        """Return a random, existing instance of `type(self)`."""
        ...

class Derived(Base):
    ...


derived = Derived()
# Since the return hint of the method is `Base`, the language server of course thinks that
# it is as such. I would like it to be of type `Derived` instead.
random_derived = derived.get_random_instance()

Hopefully this makes sense.

oblique urchin
#

then def get_random_instance(self: BaseT) -> BaseT:

buoyant furnace
#

Oh. That was much easier than I would have thought. I tried something close to that before, but I forgot to hint the friggin' method parameter...

Thank you very much!

trim tangle
#

also there's a proposal for Self, and pyright already supports it lemon_surprised

oblique urchin
#

oops I misspelled bound above. Also, there's an upcoming proposal to make this even easiier

#

^ yes that

buoyant furnace
#

So like, -> Type[Self]?

oblique urchin
#

No, you'd just write -> Self

buoyant furnace
#

Huh. Well that's handy.

#

Thanks guys! Really appreciate the help ๐Ÿ™‚ .

soft matrix
#

its very nice ;)

#

although to use it you need to import it from typing_extensions

#

i need to talk to pradeep about making a pr to python/peps

#

and getting a sponsor for it

oblique urchin
#

I can sponsor it if necessary

#

Though if you can get Guido to do it even better ๐Ÿ™‚

soft matrix
#

thank you for the offer, ill email guido and ask him if hed be willing to

flat crown
#

Can someone help me out with an issue I'm having.

soft matrix
#

yeah sure just ask your question

little hare
#

You'll have to share the issue before anyone can help ^^

upbeat wadi
#
from typing import TypeVar, Any

MISSING: Any = object()

IntT = TypeVar('IntT', bound=int)

def foo(n: IntT = MISSING) -> IntT:
    if n is MISSING:
        n = 3  # Expression of type "Literal[3]" cannot be assigned to declared type "IntT@foo"

    return n```why doesn't this type check? `3` is an `int` which doesn't conflict with the bound type of `IntT`
trim tangle
#

@upbeat wadi If you call foo(b) where b has type bool, foo is typed to return a bool. But it will not.

upbeat wadi
#

ah okay, thanks

trim tangle
#

Just as you can't do this:

T = TypeVar("T")
def f(x: T) -> T:
    if x is None:
        return 42069
    return x
#

in other words, 3 is not assignable to IntT because there's some extra constraint on IntT you don't know.

#

i see you're a fellow pyright user ๐Ÿ‘€

upbeat wadi
#

yeah i use pyright

trim tangle
#

I was planning to dig into pyright and figure out how it works on the weekend

#

any pointers?

#

as in, read the code

brazen trellis
#

hey all, is it possible to do the following with pydantic?

class User(BaseModel):
    id: int
    name: str
    if name == 'John Doe':
        signup_ts: Optional[datetime] = None
        friends: List[int] = []
    elif name == 'Jane Doe':
        batch_size: int
        positive_weights: bool

I have two different classes, each with some shared parameters (like "id") as well as some unique parameters "batch_size, positive_weights". It would be nice to just have a single pydantic object to check the types for either class

upbeat wadi
#

to read (and understand) the code, it would help to know ts as that's what it's written in

trim tangle
#

yeah I know TS

#

I mainly wanted to fork it and hack on it

upbeat wadi
#

i see

#

to add new features?

trim tangle
#

perhaps ๐Ÿ‘€

#

or just mess around if that doesn't work

upbeat wadi
#

ah nice

upbeat wadi
#

(the error is when updating OPTION_TYPE_MAPPING, not using dict.fromkeys)

soft matrix
trim tangle
#

@upbeat wadi
fromkeys accepts Mapping[ValidTypes, int]. Because the key type variable of Mapping is invariant, ChannelTypes cannot be used in place of ValidTypes.

#

However, fromkeys also accepts Iterable[tuple[ValidTypes, int]]. So if you do OPTION_TYPE_MAPPING.update(<expr>.items()), it works.

#

subtyping is tricky

rough sluiceBOT
#

stdlib/typing.pyi lines 434 to 436

class Mapping(Collection[_KT], Generic[_KT, _VT_co]):
    # TODO: We wish the key type could also be covariant, but that doesn't work,
    # see discussion in https://github.com/python/typing/pull/273.```
soft matrix
#

oh this only works on mypy cause it makes things "frozen"

trim tangle
soft matrix
#

it does special stuff for lists aswell

#

its actually pretty cool

trim tangle
#

e๐Ÿ…ฑ๏ธic jank I guess

upbeat wadi
#

And yeah I was using pyright

soft matrix
#

maybe ask eric what he actually things of that mypy

trim tangle
soft matrix
#

cause i think it can actually be very useful

#

and its type safe

upbeat wadi
#

I would but I don't really get how it works since I don't use mypy much

#

Is it documented?

trim tangle
#

you can just make an issue, Eric proabably knows something about it

upbeat wadi
#

Alright

trim tangle
#

or he will ping gvanrossum ๐Ÿ˜‰

upbeat wadi
#

i'm wondering whether this is a bug or not now py a: dict[ChannelTypes, int] = dict.fromkeys(CHANNEL_TYPE_MAPPING.keys(), 1) since dict.update expects the dict to have the same type as the dict being updated

#

and the dict being updated is dict[ValidOptions, int]; since mypy doesn't report an error I'll report it anyways and see what Eric says though

upbeat wadi
# soft matrix oh this only works on mypy cause it makes things "frozen"
from typing import Union

options: dict[Union[int, str], int] = {}
channel_types: dict[str, int] = {}

options.update(channel_types) # error on both mypy and pyright
options.update(dict.fromkeys(channel_types.keys(), 1)) # error on pyright only
```this confuses me a bit, why does mypy give an error on the first update but not the second?
oblique urchin
upbeat wadi
#
reveal_type(channel_types) # note: Revealed type is "builtins.dict[builtins.str, builtins.int]"
reveal_type(dict.fromkeys(channel_types.keys(), 1)) # note: Revealed type is "builtins.dict[builtins.str*, builtins.int*]"
```mypy has the key and value type of `fromkeys` as conditional while pyright doesn't, would that be why?
soft matrix
#

The asterisk doesn't mean conditional on mypy

oblique urchin
#

Yeah it means "inferred"

upbeat wadi
#

ah i see

indigo locust
#

Do magic methods work with the typing.overload decorator?

#
    @overload
    def __get__(self, instance: Element, prototype: Type[Element]) -> Primitive:
        pass

    @overload
    def __get__(self, instance: None, prototype: Type[Element]) -> Attribute:
        pass

    def __get__(self,
            instance: None | Element, prototype: Type[Element]) -> Attribute | Primitive:
        pass
upbeat wadi
#

yes

#

i would advise using ... instead of pass though

#

(for the overloads only, assuming that there is an implementation for it)

indigo locust
#

Oh? Why's that?

#

This is all in a stub file anyway, so nothing is every executed

upbeat wadi
#

you dont need to provide the non overloaded function there then

#

and ... is used when overloading/for methods in stub files usually

#

if you're using pyright/pylance, you'll also get an error for pass (assuming this was not in a stub file, it doesn't seem to give that error in stub files)

indigo locust
#

you dont need to provide the non overloaded function there then

#

So you're saying a stub file will automatically overload?

#

@upbeat wadi

#

Do I still include the decorator?

upbeat wadi
#

I mean that you only need the overloads

#

If there are overloads for a function

indigo locust
#

Awesome

#

That keeps things pretty then

#

๐Ÿ™‚

lone widget
#

def foo(bar: type) -> bar

#

Is there a way to do something like this? Where the parameter is a type, and the return type is whatever the parameter was?

buoyant swift
#

def foo(bar: Type[T]) -> T? idk seems kinda weird tho

#

@lone widget

lone widget
#

Oooh ok iโ€™ll try that

#

Yeah itโ€™s for a factory function with options

upbeat wadi
#

is there a way to type hint likepy if self.x is None, self.y is an int if self.x is a str, self.y is a bool

pastel egret
#

One way you can sorta do it is define a property, which checks the other attribute (raising an exception if not the right type), then casts and returns.

#

Not particularly efficient, but safe. Perhaps making the class generic and using typeguard would also work?

upbeat wadi
#

i dont think you can use TypeGuard based on attributes of the class

pastel egret
#

Well you could define a is_y_int() method, which typeguard-casts self to MyClass[int].

upbeat wadi
#

also, i meant something like: py instance = ... reveal_type(instance.x) # str | None reveal_type(instance.y) # int | bool if instance.x is None: reveal_type(instance.y) # int else: reveal_type(instance.y) # bool
iirc i tried using a generic, but i'll try again

#

ideally for my use case i wouldn't use generics (at least without being able to default the type arguments to generic)

upbeat wadi
pastel egret
#
T = TypeVar('T', int, bool)

class SomeClass(Generic[T]):
    x: str | None
    y: int | bool

def cls_is_none(obj: SomeClass) -> TypeGuard[SomeClass[int]]:
    return obj.x is None

if cls_is_none(instance):
    reveal_type(instance.y)
upbeat wadi
#

ah i should have mentioned this is within the class

pastel egret
#

Just need to make it a function, or pass the instance again as a parameter.

upbeat wadi
#

unfortunate, thanks

pastel egret
#

Maybe you could alternatively use an overloaded property, where self has a generic type to constrain which is used.

upbeat wadi
#

good idea, though pyright doesn't seem to like property overloads

#

mypy gives an error of Decorated property not supported

#

oh i just realized this wouldn't work in my case either blobdead

rough sluiceBOT
#

discord/reaction.py lines 86 to 89

# TODO: typeguard
def is_custom_emoji(self) -> bool:
    """:class:`โ€‹bool`โ€‹: If this is a custom emoji."""
    return not isinstance(self.emoji, str)```
pastel egret
#

This sort of thing sometimes indicates perhaps you need to change your type hierarchy.

upbeat wadi
#

in what way?

pastel egret
#

Well, change self.emoji to also include the other attribute. But actually since it's just a type check on that attribute already, you could just do it in the callers.

upbeat wadi
#

i see

pastel egret
#

What's the attribute this is guarding?

rough sluiceBOT
#

discord/reaction.py lines 69 to 70

emoji: Union[:class:`โ€‹Emoji`โ€‹, :class:`โ€‹PartialEmoji`โ€‹, :class:`โ€‹str`โ€‹]
    The reaction emoji. May be a custom emoji, or a unicode emoji.```
pastel egret
#

Okay, just check isinstance directly. Since your code will be using str, that's not really private anymore.

upbeat wadi
#

that could also be changed to just Emoji | PartialEmoji probably

pastel egret
#

You could also do this:

@property
def emoji_str(self) -> str:
    if isinstance(self.emoji, str):
        return self.emoji
    else:
        raise ValueError(f"Emoji {self.emoji!r} is not a string!")
#

Then inside the if-guarded code, use this which will error if the wrong value was provided.

#

Or make it not error, instead return the string form of those other emoji classes.

upbeat wadi
#

๐Ÿ‘ thanks

lone widget
#

are type mappings a thing?

#
def atomicview(buffer, atype: Type[AtomicView], **kwargs) -> AtomicViewContext:
    if atype is AtomicView:
        return AtomicViewContext(buffer=buffer, **kwargs)
    elif atype is AtomicBytesView:
        return AtomicBytesViewContext(buffer=buffer)
    elif atype is AtomicIntegralView:
        return AtomicIntegralViewContext(buffer=buffer, **kwargs)
    elif atype is AtomicIntView:
        return AtomicIntViewContext(buffer=buffer)
    elif atype is AtomicUintView:
        return AtomicUintViewContext(buffer=buffer)
    else:
        raise TypeError("'atype' must be or derive from 'AtomicView'.")

wanna somehow map Atomic*View -> Atomic*ViewContext

#

but i'm not sure if i'm reaching here

#

(As in the parameter type with the return type, not the function contents)

lone widget
#

Ah, @overload to the rescue ๐Ÿ™‚

blazing nest
trim tangle
#

in TypeScript ๐Ÿ˜”

upbeat wadi
soft matrix
#

i tried this when i did audit logs afaik it isnt possible

#

ask eric what he thinks of adding overloading properties

lone widget
#

(it's 5 type overloads for a function, and then the function is like 10 lines)

#

but when i use it, PyCharm thinks the return type is of all the overloads that match the input type

#

is there a way to make it think the return type is a single type?

#

like have Type[T] accept only T and not types that derive from T?

upbeat wadi
trim tangle
lone widget
#

gonna try TypeVar stuff

hoary pagoda
#

I would try Union[Type[C1], Type[C2], Type[C3]] instead of Type[Union]

lone widget
#

(also @trim tangle )

#

like if i use pyright or pylance it's fine, just PyCharm's type checker is confused (a little, but not in a detrimental way)

upbeat wadi
#

I've heard that it isn't the best

pastel egret
#

PyCharm's checker is a fair bit more lenient, I think the primary purpose is for autocomplete and the like.

void panther
#

I have found it to be somewhat limited even in that regard

subtle crescent
#

Hello

indigo locust
#

if I have a class that has embedded within it some kind of custom descriptor
Do I type the attribute as the descriptor's type, as its return turn, or both (somehow)

#
class SomeDescriptor(object):
  
  def __get__(self, instance: None | SomeClass, type: Type[SomeClass]) -> Some_Return_Type:
    ...

class SomeClass(object):
  
  some_value : SomeDescriptor

  or

  some_value : Some_Return_Type

  or

  some_value : Classvar(Some_Return_Type)
  some_value : Some_Return_Type
#

This is in the context of a typing stub

blazing nest
#

The static type checker should be able to figure out descriptors as far as I know

#
class SomeClass(object):  
  some_value: SomeDescriptor
indigo locust
#

Excellentttttt

#

โค๏ธ you're the best boo

blazing nest
wraith linden
#

Is it possible to create a type-variable within a generic class such that it is bound by a type parameter of that class?

#

Something like this: ```py
A = TypeVar('A')

class Foo(Generic[A]):
B = TypeVar('B', bound=A)
...

blazing nest
#

What's the use-case?

#

Are A and B unequal here?

trim tangle
wraith linden
#

I'm trying to make an event bus, which would look something like: ```py
class Event:
pass

E = TypeVar('E', bound=Event)

class Bus:

def __init__(self):
    ...

def subscribe(
    self,
    event_type: type[E],
    callback: Callable[[E], None],
) -> None:
    ...

def notify(self, event: Event) -> None:
    ...
#

Except, rather than providing my own base event, Event, I'd like to make it generic:

#
class Bus(Generic[E]):
    ...
#

Where E is the type parameter for the base event.

#

I'm not sure this really makes very much sense tbh.

blazing nest
wraith linden
blazing nest
#

Is this not the behaviour right now?

wraith linden
blazing nest
#

Do you not want event_type ans the event for the callable to be the same?

wraith linden
#

So like, you'd create your own event hierarchy of classes where the base event is called e.g. BaseEvent. Then you'd create your bus: ```py
bus: Bus[BaseEvent] = Bus()

#

The class for Bus is something like (I know this is wrong): ```py
T = TypeVar('T')

class Bus(Generic[T]):

E = TypeVar('E', bound=T)

def __init__(self):
    ...

def subscribe(
    self,
    event_type: type[E],
    callback: Callable[[E], None],
) -> None:
    ...

def notify(self, event: Event) -> None:
    ...
blazing nest
wraith linden
#

So that the code for the bus can be re-used for different busses.

blazing nest
#

Not even a subclass to yours?

wraith linden
#

Nah

rustic gull
#

print("lol")

blazing nest
#

I still don't see that problem E solves

#
T = TypeVar('T')

class Bus(Generic[T]):

    def __init__(self):
        ...

    def subscribe(
        self,
        event_type: type[T],
        callback: Callable[[T] None],
    ) -> None:
        ...

    def notify(self, event: T) -> None:
        ...
#

The only potential problem is the callback, if I remember correctly callbacks are like invariant, is that the issue you've run into?

wraith linden
blazing nest
blazing nest
#
T = TypeVar('T')
ET = TypeVar('ET', bound=T)

class Bus(Generic[T]):

    def __init__(self):
        ...

    def subscribe(
        self,
        event_type: type[ET],
        callback: Callable[[ET] None],
    ) -> None:
        ...

    def notify(self, event: T) -> None:
        ...
#

So this is kind of what you want then? Just have to move it out of the class I believe

wraith linden
blazing nest
#

Right exactly, so if you make it covariant?

wraith linden
#

Because for methods within a class, rather than being a type variable, the type parameter can be thought of as representing a constant type.

blazing nest
#
T_co = TypeVar('T', covariant=True)

class Bus(Generic[T_co]):

    def __init__(self):
        ...

    def subscribe(
        self,
        event_type: type[T_co],
        callback: Callable[[T_co], None],
    ) -> None:
        ...

    def notify(self, event: T_co) -> None:
        ...
wraith linden
#

Hmm, I'm not sure how to interpret that. Isn't making the type parameter covariant more about the relationship between different specialisations of Bus?

#

Like the relationship between Bus[C1] and Bus[C2], where C1 is a subtype of C2.

#

I might just go with the original code and remove the bound on the type-variable. So essentially any class could represent an event type on any bus.

#

As I'm essentially treating the event types as atomic anyway.

#

And maybe using classes for this is a bad idea to begin with ๐Ÿ˜„ I mean, rather than denoting each type of event with e.g. a string.

blazing nest
ivory owl
#

Hey guys, I'm writing a typed decorator for one of my classes. My decorator sets additional attributes on the class - a static method and a bool. How do I properly type the decorator so that mypy understands that the class will have those attributes?

trim tangle
trim tangle
#

i.e. you can't really typehint arbitrary mutations

ivory owl
#

should i # type: ignore those lines?

#

is that normal practice?

soft matrix
#

no

#

you should try and make it type check, which from the sounds of things it probably should be typeable

ivory owl
#

so, for example what should be type-able?


def mydeco(cls: Any):
  def open():
    print('Opening')

  cls.open = staticmethod(open)
  cls.is_fruit = True
  return cls

@mydeco
@attr.s
class Apple:
  color: str = attr.ib(default='Yellow')
trim tangle
#

Maybe it's doable with just a mixin?

oblique urchin
trim tangle
#

well ackchually

ivory owl
#

This is pretty-much my actual use case, the only real difference would be that open in my case would be a constructor of Apple and would return a new Apple based on another, let's say FruitPlate

trim tangle
#

Why can't you make it a mixin, for example?

ivory owl
soft matrix
#

it shouldnt

ivory owl
#

would mixins have any other benefits over a @dec?

#

ah actually

#

i can tell you why i can't really use a mixin

#

because def open(): actually uses cls inside of it...

trim tangle
#

so?

ivory owl
#

oh that would then be self

trim tangle
#

mixins can have classmethods

ivory owl
#

that seems to work! thank you @trim tangle

#

one more q, if mydeco has optional args, how does one define those with a mixin?

#

def mydeco(cls: Any, default: str = 'banana')

trim tangle
#

You could use a classvar, I guess?..

#

depends on what the optional args are for and how many

ivory owl
#

they'd only be referenced by the open() function

#

so if i want to redefine them, i'd do something like

#

class Apple(FruitMixin(default='apple')):

trim tangle
#

maybe customize __init_subclass__?

#

although I'm not sure how that would work with mypy

#

inheritance tricks generally are hard to introspect and type

trim tangle
ivory owl
#

@trim tangle got the mixin working well

#

thank you sir!

trim tangle
soft matrix
#

are you trying to implement intersection types?

trim tangle
soft matrix
#

yeah im just warning you eric said implementing them is a lot of work

trim tangle
#

he always says that!

#

jk I know it's a lot of work

trim tangle
soft matrix
#

gotta optimise for that speed

trim tangle
#

well... if Eric gets hit by a bus the project has a non-zero chance of dying, I guess

#

although maybe I'm exaggerating

oblique urchin
#

it's a really impressive project. it seems like he fixes every bug within a day and there's pretty much nobody else working on it

trim tangle
#

yeah, I agree

#

but I've been trying to figure out how it works multiple times, and failed

#

maybe I'm just stupid

ivory owl
#

for some reason, all the Eric's I know are incredibly smart and I don't understand why

#

maybe it's a classvar... lol

upbeat wadi
#

is using object or typing.Any more recommended? as they both refer to the same thing

buoyant swift
#

typing.Any

#

and technically they aren't the same, Any is also a subtype of every type

void panther
#

object would only expect operations that work on all objects, which aren't many

oblique urchin
#

They mean different things. It's actually better to use object when you can, because it is type safe. Any basically turns off the type system.

upbeat wadi
#

ah thanks

indigo locust
#

My typehints are a bit rusty, and I'm not used to pyi files

#

If I want to use a typevar, for example, in a sequence type which works with a T_Contained type

#
T_Contained = TypeVar('T_Contained')

class MyContainer(Sequence[T_Contained]):

  def __iter__(self) -> Iterator[T_Contained]:
    ...

#

Or is there a shorthand, like

#
class MyContainer(Sequence[TypeVar('T_Contained')]):

  def __iter__(self) -> Iterator[T_Contained]:
    ...
oblique urchin
indigo locust
indigo locust
#

Is there a difference, other than intent, between typing something as None | SOMETHING versus Optional[SOMETHING]?

pastel egret
#

Those are exactly equivalent, and also to Union[Something, None]. That'll produce Optional[Something] actually.

indigo locust
#

Oh, neat

#

In this case I think Optional[] notation is best, since it declares intent a little more, but in general

#

Praise the union operator ๐Ÿ™

grave fjord
#

| None is best

#

Because it's not really an Optional type, that would be an actual class with methods to extract the wrapped value

brazen jolt
#

how should I type-hint a class method that returns an instance of itself?```py
from typing import TypeVar, Type

T = TypeVar("T", bound="Foo")

class Foo:
@classmethod
def bar(cls: Type[T], my_var: str) -> T:
...

#

I figured I could do this, but isn't there some better way?

#

for some reason this just looks weird to me

trim tangle
grizzled smelt
#
from __future__ import annotations

Why does importing annotations fix using generic collections in type hints backwards compatability? e.g. list[int] on Python 3.7? Because the annotations delays the evaluation of the type hint till runtime? Why doesn't it fail at runtime?

grizzled smelt
blazing nest
brazen jolt
grizzled smelt
#

I don't think there's a type hint for a subclass of something, but Foo type includes all subclasses of Foo, afaik

grizzled smelt
marsh yoke
#

?????? Error

#

anyone fix it?

blazing nest
#

That's when it fails

grizzled smelt
#

ah that's kind of what I meant, but I think I need to go reread the annotations pep

grizzled smelt
grizzled smelt
brazen jolt
grizzled smelt
#

good to know

blazing nest
grizzled smelt
#

just reread the PEP, didn't realise that 'eval' was the only way to resolve them

#

assumed there would have been a safe Foo.__get_annotations__() or something

trim tangle
undone carbon
#

is beartype overload out?

supple hinge
#

Hello, any idea with mypy how to remove this error?

test_mypy_optional.py:12: error: Argument 1 to "open" has incompatible type "Optional[str]"; expected "Union[Union[str, bytes, PathLike[str], PathLike[bytes]], int]"

POC

import os

A = os.getenv("A")
assert A is not None

# Fine for mypy
with open(A, newline='') as xfile: 
    pass

# Not fine for mypy
def x() -> None:
    with open(A, newline='') as xfile:
        pass

As you can see it still considers A as possibly None, despite asserting it

boreal vessel
#

!e this is how I use type hints. a bit more of a type nudging. ```py
from ctypes import py_object as p
class annotations(metaclass=lambda*a:type(*a)()):
q={}
def setitem(self, this, that):
if this in globals():
dict.update(globals(),{this:that(globals()[this])})
else:self.q[this]=that
def getitem(self, this):
if this in self.q:return q[this]
return globals()[this].class
def del(self):
print("Unused variables:",*self.q)

class flogbals(dict):
def setitem(self, this, that):
if this in annotations.q:that=annotations.q.pop(this)(that)
dict.update(globals(),{this:that})
def start():p.from_address(id(globals())+8).value=flogbals
def end():p.from_address(id(globals())+8).value=dict

start()
a:str
a = 6
b:str = 9
c:int = a + b
d:dict
print(c)
end()

rough sluiceBOT
#

@boreal vessel :white_check_mark: Your eval job has completed with return code 0.

001 | 69
002 | Unused variables: d
supple hinge
boreal vessel
#

ok

#

but it's type hinting

#

with a twist

humble ice
#

hello - what's the proper way to type hint when a param is a list, but optional?

def th3_categories(df_inp: pd.DataFrame, categories: list[str] = None):
    """Input th3 dataframe and return filtered based on categories"""
    if categories is None:
        categories = ['SH', 'SA']

    filt = df_inp['DEPTNO'].isin(categories)
    df = df_inp.loc[filt]
    return df
supple hinge
#

doesn't Optional[list[str]] works?

humble ice
#

ok, and what is it with python 3.10?

blazing nest
blazing nest
#

Though I've seen None come first in most code with that syntax

humble ice
#

(not that i'm using 3.10 yet but)

supple hinge
blazing nest
supple hinge
#

mypy my_file.py. Could you reproduce? May be on my end

#

I'm on Python 3.9.7, with mypy==0.910and mypy-extensions==0.4.3

humble ice
# blazing nest `list[str] | None`

does this essentially mean that the param type will be a list, or that the value of the param will be None ? isn't that then along the same line of thinking as param: list[str] = None ?

buoyant swift
#

yeah

#

Optional[list[str]]

blazing nest
humble ice
#

oh rly

blazing nest
#

Mostly speculation, I just noticed that I've stopped getting complaints of None not being assignable to whatever typehint I had

buoyant swift
#

or just a bad checker ๐Ÿ˜ณ

humble ice
#

wait am i still supposed to do: categories: Optional[list[str]] = None ? i have to add the =None for pycharm not to squiggly line me when i call the function

oblique urchin
humble ice
#

that's so ugly lol

blazing nest
oblique urchin
#
GitHub

Optional static typing for Python. Contribute to python/mypy development by creating an account on GitHub.

GitHub

CC: @ddfisher @JukkaL @markshannon PEP 484 currently says: An optional type is also automatically assumed when the default value is None, for example:: def handle_employee(e: Employee = None): ... ...

#

I guess mypy still has this behavior by default ๐Ÿ™‚

ivory owl
#

Hey Guys, I'm unstructuring a pretty complex data model with cattrs, I have some fields that are within lists, etc. Sometimes, these are None, is there a way to 'omit_if_None' in cattrs? I know GenConverter has 'omit_if_default'

fierce ridge
#

honestly i find cattrs pretty confusing

#

it reminds me of some highly abstracted haskell library

#

i can try to figure out a solution when i get back to a computer, this shouldn't be a difficult task

#

the issue with cattrs is that they rely on all these factory functions and don't clearly explain the interfaces that those factory functions expect or emit

trim tangle
#

@fierce ridge so far we have...
dataclass-factory
pydantic
desert
marshmallow
cattrs
for basically serializing or deserializing structs. Why so many, and why is serde still much better?
(P.S: serde the rust library, not whatever is on pypi)

little hare
#

attrs too

#

oh that's cattrs

trim tangle
#

well, not JSON, but you get the idea

little hare
#

i mean, cattrs is on top of attrs

trim tangle
little hare
#

ye

#

you can also make a system which uses attrs, marshmallow, and desert

#

no, i am not kidding

#

yes, i do wish i was kidding

#

!pypi serde

rough sluiceBOT
#

Define, serialize, deserialize, and validate Python data structures.

little hare
#

interesting

little hare
trim tangle
#

I meant the Rust library serde

little hare
#

lmfao

#

well then

fierce ridge
indigo locust
#

What are the communities thoughts on beartyping?

#

Personally, if it does what they say it does, I think its a great addition to Python. Optional static typing FTW

oblique urchin
#

I don't find runtime explicit typechecking all that compelling. The nice thing about static typing is that it finds issues without running all your code

indigo locust
#
class Node(object):

  children : Iterable[NodeTypeA, NodeTypeB, NodeTypeC]
  
  @property
  def firstSiblingAny(self) โ€”> None | Node:
    ...

  @property
  def firstSiblingTypeA(self) โ€”> None | NodeTypeA:
    ...

  @property
  def firstSiblingTypeB(self) โ€”> None | NodeTypeB:
    ...

  @property
  def firstSiblingTypeC(self) โ€”> None | NodeTypeC:
    ...

  @property
  def firstSiblingThisType(self) โ€”> None | THIS_TYPE:
    ...

class NodeTypeA(Node):
  ...

class NodeTypeB(Node):
  ...

class NodeTypeC(Node):
  ...
#

How do I indicate THIS_TYPE?

#

Or, do I just reimplement it on all the subclasses of node?

#
T_ThisNodeType = TypeVar('T_ThisNodeType')

...

  @property
  def firstSiblingThisType(self: T_ThisNodeType) โ€”> None | T_ThisNodeType:
    ...
#

???

oblique urchin
indigo locust
#

๐Ÿ˜ฎ

#

This is new to me

indigo locust
#
T_ThisNodeType = TypeVar('T_ThisNodeType', bound='Node')

cdef class Node(Target):

  ...

    # ------------------------------------------------------------------------------------------
    # ------------------------------------- First Siblings -------------------------------------
    # ------------------------------------------------------------------------------------------
    def firstSiblingOfType(self, nodetype: T_NodeType) -> Undefined | T_NodeType:
        ...

    @property
    def firstSibling(self) -> Undefined | T_ThisNodeType:
        ...

    @property
    def firstSiblingNode(self) -> Undefined | Node:
        ...