#type-hinting

1 messages ยท Page 72 of 1

floral ibex
#

for endpoint argument

fierce ridge
#

https://mypy-play.net/?mypy=latest&python=3.10&flags=ignore-missing-imports%2Cshow-error-codes%2Cstrict&gist=fa77dcfd6d30d2c11093d89402b5a621

from aiohttp import ClientSession

class HTTPClient:

    _BASEURL: str = "https://random-d.uk/api/v2/"

    async def _request(self, endpoint: int) -> int:
        async with ClientSession().get(f'{self._BASEURL}{endpoint}') as resp:
            return await resp.json()


client = HTTPClient()
client._request('hi')

with --strict --no-missing-imports --show-error-codes

main.py:9: error: Returning Any from function declared to return "int"  [no-any-return]
main.py:13: error: Argument 1 to "_request" of "HTTPClient" has incompatible type "str"; expected "int"  [arg-type]
Found 2 errors in 1 file (checked 1 source file)
floral ibex
#

why is mine fucked

#

wtf

fierce ridge
#
  1. are you actually using mypy, or pycharm? pycharm has its own built-in (and somewhat buggy) type checker

  2. try using --strict, just in case (you should anyway)

  3. there must be something else wrong with your code

oblique urchin
#

Show us complete code samples that show the problem you're seeing. Otherwise we can only guess

fierce ridge
#
  1. it's possible that the thing you thought was str actually has type Any. unless it's a string literal, this is a possibility. try using reveal_type on whatever you pass to _request
floral ibex
#

my ide is vscode

#
class HTTPClient:

    _BASEURL: ClassVar[str] = "https://random-d.uk/api/v2/"

    async def _request(self, endpoint: int) -> int:
        async with ClientSession().get(f'{self._BASEURL}{endpoint}') as resp:
            return await resp.json()

a: HTTPClient = HTTPClient()

async def main():
    print(await a._request("random"))

asyncio.run(main())
oblique urchin
floral ibex
#

fuck

fierce ridge
#

and this is why you should use --strict!

#

there's basically no reason not to use it unless you're working on a legacy codebase

floral ibex
#

i'm starting to hate typehinting

#

๐Ÿ˜”

fierce ridge
#

no, you're starting to hate loosey-goosey type hinting

floral ibex
#

might aswell since i'm here

#

what are generics

#

i think i know what generics are but still not sure

fierce ridge
# floral ibex what are generics

think about a list. a list can hold any type. the list type itself has no knowledge of the stuff that it holds. a list knows that it can hold stuff, and that you can iterate over it, slice it, append to it, etc. but the actual stuff inside is unknown to the list and irrelevant to the behavior that makes the list act like a list.

#

so list is a generic type. it has one type parameter, indicating the type of the list elements.

fierce ridge
#

in python, you can define your own generics by inheriting from Generic

#

for which you also need TypeVar. a "type variable" is just a placeholder: it says "there will be a type here, but i don't know what it is yet"

floral ibex
#

okay

fierce ridge
#

consider a custom maplist function:

from collections.abc import Callable, Iterable

A = TypeVar('A')
B = TypeVar('B')

def maplist(func: Callable[[A], B], xs: Iterable[A]) -> list[B]:
    return [func(x) for x in xs]
#

it doesn't matter what A or B actually are, as long as every A placeholder gets filled with the same type, and every B placeholder gets filled with the same type

#

so you can pass arguments Callable[[int], float], list[int] but not Callable[[int], float], list[str]

#

do you see how that works?

floral ibex
#

yes

#

oh i did know what generics are

#

that was my understanding of what generics are

fierce ridge
#

yeah, you'd say that maplist is a "generic function", and list is a "generic type"

floral ibex
#

oh wait nevermind

oblique urchin
floral ibex
#

why does .json() return any?

oblique urchin
floral ibex
#

also can you not use Generic[T] if the class doesn't take in anything?

#

cause it's just been screaming at me

fierce ridge
#

i think you can, but it won't have any effect

#

or maybe you can't

#
from typing import Generic, TypeVar

T = TypeVar('T')

class Thing(Generic[T]):
    pass

this is valid according to mypy

floral ibex
floral ibex
# fierce ridge as always, show your code
T = TypeVar("T")
V = TypeVar("V")

class HTTPClient(Generic[T, V]):

    _BASEURL: ClassVar[str] = "https://random-d.uk/api/v2/"

    async def _request(self, endpoint: str) -> dict[T, V]:
        async with ClientSession().get(f'{self._BASEURL}{endpoint}') as resp:
            return await resp.json()

a: HTTPClient = HTTPClient()

async def main() -> None:
    print(await a._request("random"))

asyncio.run(main())
#

i'll change dict to any later

fierce ridge
#

the problem is a: HTTPClient

#

you need to say what the T and V are now

#

(json keys are always str)

#

normally i annotate json objects as dict[str, object] and then use TypeGuard or typing.cast as needed to take the dicts apart

#

that, or i use attrs or pydantic to deserialize to something more structured

floral ibex
#

like HTTPClient(str, str)?

#

nevermind figured it out

frail rover
#

I don't comprehend this error, I'm trying to make a Protocol that can engulf both TypedDict and dict for reading

Here's what InfoDict is:

K = TypeVar('K', contravariant=True)
V = TypeVar('V', covariant=True)
DV = TypeVar('DV')

class InfoDict(Protocol, Generic[K, V]):
  def __getitem__(self, key: K, /) -> V: ...

  @overload
  def get(self, key: K, /) -> Optional[V]: ...

  @overload
  def get(self, key: K, default: DV, /) -> V | DV: ...

  def get(self, key: K, default: DV = None, /) -> V | DV: ...
trim tangle
frail rover
#

there's practically none, just using this to bring up the error

trim tangle
#

What is SpotifyAPITrack?

#

Also, why not just use Mapping?

frail rover
#

oh god nevermind this was XYZ

#

Mapping worked, I just needed to make it a property on another protocol attribute

#

thanks

chilly ruin
#

hi, im getting slightly conflicting errors and i have no idea how co/contravariance works

class GlobalWaitForCheck(Protocol[EventNameT]):
    def __call__(self, event: EventNameT, *args: Any) -> Awaitable[bool] | bool:
        ...

class WaitForTuple(Protocol[EventNameT]):
    @overload
    def __getitem__(self, item: Literal[0]) -> EventNameT:
        ...

    @overload
    def __getitem__(self, item: int) -> Any:
        ...

    def __getitem__(self, item: int) -> EventNameT | Any:
        ...
``` ```py
Type variable "EventNameT" used in generic protocol "GlobalEventCallback" should be contravariantPylancereportInvalidTypeVarUse
``` ```py
Type variable "EventNameT" used in generic protocol "WaitForTuple" should be covariantPylancereportInvalidTypeVarUse
``` ```py
EventNameT = TypeVar("EventNameT", bound=Hashable)
``` so like, `GlobalWaitForCheck` is a protocol to accept `str` if the generic is `str`, `WaitForTuple` returns a `str` for the first element if the generic too is `str`
fresh iris
#

Can we create mapping (input model to output model) using pydantic ? If not, any good tools for this ?

hearty shell
#

What do you mean?

#

Also, just use different TypeVars, not sure what you are doing but seems like you could just have EventNameT_contra = TypeVar("EventNameT_contra", bound=Hashable, contravariant=True) and EventNameT_co = TypeVar("EventNameT_co", bound=Hashable, covariant=True)

chilly ruin
#

yeah ive read that, every explaination is just so wordy to me

#

like i get the slightest idea but they get so complex

hearty shell
#

Open any typing playground and start messing with them with very simple functions and classes, I found that to be the best way to understand them

hasty hull
#

I needed to write a function to take an instance of a generic object but that function has to be agnostic about the type that was given to the generic object

fierce ridge
#

i'm surprised that pyright is requiring covariance in that situation

#

it makes sense that they'd warn you

#

or is it semantically invalid to have an invariant function parameter typevar?

#

i never bother to set up co/contra-variance ๐Ÿ˜† too lazy

#

and what happens if you have a typevar that is uses as both a return type and a parameter type in the same definition?

deft merlin
#

how to type hint a generator? shoud i just say int if its yields int?

pastel egret
#

Either collections.abc.Iterator[int], or typing.Generator[int, None, None].

#

Generator lets you specify the parameters for send() and throw().

deft merlin
#

oh ok, thanks

#

i understand Generator[YieldType, SendType, ReturnType] YieldType and ReturnType but what is SendType??

pastel egret
#

yield is an expression, not a statement - you can do blah = (yield x), and then the code doing the iterating can send a value back into the generator with the send() method.

deft merlin
#

ah ok

floral ibex
#

can you typehint a already typehinted variable when assigning a new value to the variable?

brisk heart
#

You should just make a new name for the variable

floral ibex
#

Okay then

brazen jolt
#

is it possible to type-hint a return type of a function as an overloaded callable?

@overload
def my_fun(a: int, convert: Literal[True]) -> float:
    ...

@overload
def my_fun(a: int, convert: Literal[False]) -> int:
    ...

def my_fun(a: int, convert: bool) -> Union[int, float]:
    ...

def foo() -> my_fun:
    ...

I was thinking of doing something like Union[Callable[[int, Literal[True]], float], Callable[[int, Literal[False]], int]], but that didn't work as I'd expect, instead, it tried to match both of the union types, so when I passed True, it complained that it wasn't compatible with Literal[False], and vice-versa

hearty shell
#

Use Protocols

#
class my_fun(Protocol):
    @overload
    def __call__(self, a: int, convert: Literal[True]) -> float:
        ...
    
    @overload
    def __call__(self, a: int, convert: Literal[False]) -> int:
        ...
    
    def __call__(self, a: int, convert: bool) -> Union[int, float]:
        ...
brazen jolt
#

oh, of course, that makes sense

#

yeah, that should do it

#

though I still think it's pretty weird that it acted like this

#

doesn't Union mean either of

#

why did it try to match both

hearty shell
#

Because the return type of foo is a Union, so the callable that gets returned is either the one that takes a literal true or a literal false

brazen jolt
hearty shell
#

You can't be sure which one it is

#

So it is not safe to call it with either literal true or literal false

brazen jolt
#

It'd be neat if there was something like typing.Overald taking variadic of callables

hearty shell
#

The annotations could get very overwhelming though, usually when you get a "x does not match Overload[]" the error messages are huge

brazen jolt
brazen jolt
#

using protocol with overloaded __call__ isn't really the same as Callable

#

and doesn't match type-wise

#

I mean, this works and basically does what I'd need ```py
def foo() -> MyFunProto:
return cast(MyFunProto, my_fun)

#

but it's not ideal

#

it's also very annoying to have to type out all of the overloads again, when my_fun already defines them

hearty shell
#

which is annoying I agree

hearty shell
#

Can you show the code?

brazen jolt
#
class MyFunc(Protocol):
    @overload
    def __call__(a: int, convert: Literal[True]) -> float:
        ...

    @overload
    def __call__(a: int, convert: Literal[False]) -> int:
        ...

    @staticmethod
    def __call__(a: int, convert: bool) -> Union[int, float]:
        ...


def foo() -> MyFunc:
    return my_fun
#

my_fun is defined as I've posted initially

#

it may be the staticmethod

hearty shell
#

You are not supposed to omit self

#
class MyFun(Protocol):
    @overload
    def __call__(self, a: int, convert: Literal[True]) -> float:
        ...
    
    @overload
    def __call__(self, a: int, convert: Literal[False]) -> int:
        ...
brazen jolt
#

but my_fun doesn't take self

hearty shell
#

This is good enough for the protocol

soft matrix
#

It's treated as a bound method

hearty shell
brazen jolt
hearty shell
#

a function is just an instance of FunctionType

soft matrix
#

self is not passed to the callable but it's needed in the protocol cause functions technically take self

brazen jolt
#

apparently, this works too: ```py
class MyFunc(Protocol):
@overload
@staticmethod
def call(a: int, convert: Literal[True]) -> float:
...

@overload
@staticmethod
def __call__(a: int, convert: Literal[False]) -> int:
    ...
soft matrix
#

Yeah you can also do that

brazen jolt
#

alright, cool, thanks!

#

I still do think that an Overload type would be neat though

#

or just allowing to do def foo() -> some_function

soft matrix
#

I don't disagree

#

Pyright I think already has an overload special form

hearty shell
#

Yup, you can see it in their error messages

soft matrix
#

Although you can't instantiate it from python

hearty shell
#

Overload[(a: int, convert: Literal[True]) -> float, (a: int, convert: Literal[False]) -> int, (a: int, convert: bool) -> (int | float)]

#

They also use the sadly caput pep for concise function annotation

#

rip

#

Oh yeah, that is another issue

#

overloads are kind of limited when you can only refence positional arguments

#

without names and such

#

which without that pep, callables are not capable of doing, and neither would a hypothetical Overald annotation

brazen jolt
#

https://github.com/python/typing/issues/566 implementing this would also kind of address the issue, since AnyOf would act like Any and just adapt to what it needs to be, but would only work for specific types. If this was implemented, it could basically be used as the Union I was thinking of initially

GitHub

Sometimes a function or method can return one of several types, depending on the passed in arguments or external factors. Best example is probably open(), but there are other examples in the standa...

#

though I didn't see any issue that even brings up implementing typing.Overload (I may just be blind though)

hearty shell
#

Something like FunT = TypeVar('FunT', Callable[[int, Literal[True]], float], Callable[[int, Literal[False]], int])

brazen jolt
#

that's probably not going to happen, but yeah

hearty shell
brazen jolt
#

I don't think so, it's more that the functions in the protocol are wrapped and the self is automatically passed, so it's removed from the signature and therfore meets the same signature of my_fun

#

i.e. the methods defined in that class are essentially partials, with self already passed, which is done by making them "bound method" objects

#

on the class itself, they're essentially descriptors, and on instances, they're just auto-passed self

#

but my_fun is defined globally as a simple function, it doesn't take self and there's no hidden mechanism there making it take it

hearty shell
#
class MyFoo:
    def __call__(self, a):
        return a + 42

f = MyFoo()

f(0)

This is just as hidden as what you are talking about, you can still access the actual function via f.__class__.__call__ and the same goes with functions

def foo(a): return a + 42

foo.__class__.__call__()
#

doing that will complain it is missing two arguments

brazen jolt
#

__call__ doesn't have anything to do with self

#

what you shown is just there to be compatible with objects etc.

#

but I'm pretty sure internally, this is just handled differently and there's really no self there

#

that's also why you can't just access it like you can with regular instances

hearty shell
#

Not sure what you mean, a method taking in self just means that it uses an instance of that class in the function

#

Trying to do foo.__class__.__call__() will raise an error, saying it is missing an instance of FunctionType (self) and the actual argument a

#

just like it would with a normal calss

brazen jolt
#

huh

#

that's very interesting, I thought this worked a bit differently

#

can you actually instantiate FunctionType yourself?

hearty shell
#

You can even pass a completely different function

def foo(): return 3

def bar(): return 9

foo.__class__.__call__(bar)
#

this will return 9

hearty shell
brazen jolt
#

ah, yeah, that makes sense then

indigo locust
#

So

#

How exactly am I supposed to work mypy into my workflow?

#

And I supposed to run it over my code as an analysis tool periodically (several times a day???) to make sure everything is up to snuff?

#

Honestly, anything less than live analysis just seems... wrong to me. But I know PyCharm in the least has a rather pitiful type checker

#

Not sure about the other popular IDES, but it should go without saying that some IDEs will have more, or less, complete checking

clever bridge
#

If I have a function with two parameters one and two and one is a Literal, is there a simpler way to change the type of two based on the value of one than a massive series of overloads?

#
class AAAData(TypedDict):
  param1: str
  param2: int

class BBBData(TypedDict):
  param1: bool
  param2: dict[str, str]

@overload
def func(one: Literal["aaa"], two: AAAData) -> None:
  ...

@overload
def func(one: Literal["bbb"], two: BBBData) -> None:
  ...

def func(one: Literal["aaa", "bbb"], two: dict[str, Any]) -> None:
  ...

(The case I have has like a dozen separate options for the literal)

void panther
#

same thing with CI but a bit less obstructive while being more annoying to deal with

trim tangle
#

like ```py
class AAAData(TypedDict):
tag: Literal["aaa"]
param1: str
param2: int

class BBBData(TypedDict):
tag: Literal["bbb"]
param1: bool
param2: dict[str, str]

def func(some: AAAData | BBBData) -> None:
...

clever bridge
#

Hmm, didn't think of that

#

Maybe

boreal ingot
clever bridge
#

The pyright integration is awesome

boreal ingot
#

It is, works well with error lens (I guess most linters would)

clever bridge
#

Yeah

#

I love it, it catches so many issues

boreal ingot
#

I did have an issue with it scanning folders it should not be when using pdm, but just had to mess around with the config a bit

boreal ingot
#

Best case is of course live linting in your editor, but I would also explicitly run it (be it by an automated script, or just by you directly) in some part of your workflow

indigo locust
#

Hmmmm

#

What with all the work Python as a language and as a community has put into getting type hinting up and running, and drilling the concept home

#

It feels pretty weak that the various IDEs don't consider their live linters to be of greater importance

#

My issue is this: what do I do if I need to write middleware that can't be understood very well by most editors?

#

As much as I'd love to be able to expect user's of my code to use this IDE or else incorporate that type checker into their workflow, that seems unrealistic

boreal ingot
indigo locust
#

And its up to my user to consume the project in a sensical way

boreal ingot
# indigo locust And its up to my user to consume the project in a sensical way

it does depend on the type of project tho:
if you are making a library that other will be using, you would want to support mypy.
but if the project is something else were the only time people will be working with the code is when developing for that project, requiring the use of a specific type checker is less of an issue in my opinion, similar to how you might mandate the use of flake8/black

indigo locust
boreal ingot
#

exactly

indigo locust
#

Here's my problem

#

I'm writing a library which would massively benefit from a custom generic descriptor class

#

Easy enough to type in mypy, but not supported by live linting wrt to most IDEs

boreal ingot
indigo locust
#

And since the project is designed for use by beginners, I can't expect them to have the wherewithall to be able to use mypy

#

VSCode is supposed to be better ๐Ÿ˜

boreal ingot
#

it is much better in my experience

#

at least when using Pylance (the vscode extension that wraps pyright)

indigo locust
#

So how should I proceed? I meant, I guess so long as I put the work in to write the descriptors properly, I could make the case to myself that its the IDEs fault for being limited and no my fault for not being smart enough to write the tool properly

boreal ingot
#

or maybe include a warning for PyCharm user specifically that the inbuild type checker is not supported by your project

indigo locust
#

Seems reasonable. If that's the best that can be done, then that's the best that can be done

#

Thank you for the frank assessment. I've been having a hard time getting straight answers

boreal ingot
#

np ๐Ÿ˜„

chilly ruin
#
@overload
def listen(self, event_name: EventNameT) -> Callable[[EventCallback], EventCallback]:
    ...

@overload
def listen(self, event_name: None) -> Callable[[GlobalEventCallback[EventNameT]], GlobalEventCallback[EventNameT]]:
    ...

def listen(
    self, event_name: EventNameT | None = None
) -> Callable[[EventCallback | GlobalEventCallback[EventNameT]], EventCallback | GlobalEventCallback[EventNameT]]:
    ...
Overloaded implementation is not consistent with signature of overload 1
  Function return type "(EventCallback) -> EventCallback" is incompatible with type "(EventCallback | GlobalEventCallback[EventNameT@Dispatcher]) -> (EventCallback | GlobalEventCallback[EventNameT@Dispatcher])"
    Type "(EventCallback) -> EventCallback" cannot be assigned to type "(EventCallback | GlobalEventCallback[EventNameT@Dispatcher]) -> (EventCallback | GlobalEventCallback[EventNameT@Dispatcher])"
      Parameter 1: type "EventCallback | GlobalEventCallback[EventNameT@Dispatcher]" cannot be assigned to type "EventCallback"
        Type "EventCallback | GlobalEventCallback[EventNameT@Dispatcher]" cannot be assigned to type "EventCallback"
          Type "GlobalEventCallback[EventNameT@Dispatcher]" cannot be assigned to type "EventCallback"PylancereportGeneralTypeIssues
median ledge
#

quick question, how should I type hint a tuple with the first item of type A and one or more items of type B?

#
Tuple[A, B]

or

Tuple[A, B, ...]
#

or smth else

upbeat wadi
#
from typing_extensions import Unpack

x: tuple[int, Unpack[tuple[str, ...]]] = (1, '', '')
#

note that typing_extensions isn't in the stdlib

median ledge
#

hmm, I see

#

Although, in this case, both A and B are two IntEnum subclasses, so it seems Tuple[int, ...] works

median ledge
soft matrix
#

generally its good to be as specific with types you export

#

but at the end of the day its up to you

chilly ruin
#

I've been using the unpack version personally

#

I only just found out you can unpack the tuple into the tuple to do this

chilly ruin
blazing nest
chilly ruin
#

ended up typing the non-overload with ... as the overloads show possibilities and the inner function knows what it gets too

#

as if i do that, the inner decorator has complaints as its types arent typevar's

floral ibex
#

Hello me again

#
async def get_list(self, *, GIF: bool=False, JPG: bool=False, HTTP: bool=False) -> dict[str, list[str] | int] | list[str]:
        data: dict[str, list[str] | int] = await self._request("list")
        
        if GIF:
            gif_data: list[str] = data['gifs']
            gif_data.sort(key=lambda v: int(v.split(".")[0]))
            return gif_data
        elif JPG:
            jpg_data: list[str] = data['images']
            jpg_data.sort(key=lambda v: int(v.split(".")[0]))
            return jpg_data
        elif HTTP:
            http_data: list[str] = data['http']
            http_data.sort(key=lambda v: int(v.split(".")[0]))
            return http_data

        return data
#

so this is my code

#

i get alot of errors with mypy

#

nevermind i'm dumb

floral ibex
#

actually i do need help

#

how can i make gif_data, jpg_data, and http_data typehint into a list and not make mypy scream at me

#

because data is dict[str, list[str] | int]

#

the values are either a list of strings or an int

floral ibex
#

but since i typehinted data earlier as dict[str, list[str] | int]

#

it's making me do those variables as list[str] | int

#

they will never be ints though

#

i've been told to use TypedDict

#

but i feel like returning TypedDict and having my variables typehinted as TypedDict would be too vague

#

or is it just me

static saddle
#

whatsup bastards java is a divine language , pythonners smoks grass, when i code on java, God himself blesses me to ascend to heaven, cthulhu favors me! the codes on java make the world reen, and the codes on your disgusting python only make the light bulbs fade, since all the gods of the gods of the sphere of worlds are disgusted by you. when people see python coders, they takes a pythfork in theirb hands and start a righteous war

floral ibex
#

hello sebastian

somber dune
#

Thanks for that copypasta/monologue, @static saddle. It's not on topic for this channel, though.

pastel egret
eager canyon
#

Could someone help me? I want to try type-hint (I think, idk the terms) an argument so it will only accept certain values, I don't really know how to explain it, so I'll try show and example of what I'm trying to do:```py
def some_function(dictionary: in source_dictionaries):
source_dictionaries = ['all', 'ahd-5', 'century', 'wiktionary', 'webster', 'wordnet']

pastel egret
#

You want Literal["all", "ahd-5", "century", ...].

eager canyon
#

ok, thank you :D

pastel egret
#

Note that Literal only accepts literal values, so regular str is not allowed.

floral ibex
#

I just feel like it's too vague

#

Because i have it in an another file called utils.py because i don't think having it in the client.py file is good

#

Or am i just wrong

eager canyon
pastel egret
#

That is a common thing to do, define your types elsewhere.

#

Ideally you'd swap it with a proper class, perhaps a dataclass, but if you have to use dicts it's a good option.

#

You could define a type alias for each key in the dict, so you don't need to re-declare the typesyou expect...

floral ibex
buoyant swift
rare vapor
#

oh, you already got that answer... sorry for the duplicate

rare scarab
#

I apparently can't use TypeVar(bound=TypedDict) with Unpack in python 3.11?

#

It works with a normal TypedDict class

#

Technically though, I'm using python 3.10 with typing_extensions

oblique urchin
rare scarab
#

No.

#

Expected TypedDict type argument for Unpack (Pylance reportGeneralTypeIssues)

#

FYI I'm trying to make a basic interface for partial to see what it can do.

from functools import partial as _partial
from typing import Callable, TypeVar

from typing_extensions import Concatenate, ParamSpec, TypedDict, TypeVarTuple, Unpack

TArgs = TypeVarTuple("TArgs")
TKwargs = TypeVar("TKwargs", bound=TypedDict)

R = TypeVar("R")
P = ParamSpec("P")


def partial(
    func: Callable[Concatenate[Unpack[TArgs], Unpack[TKwargs], P], R],
    *args: Unpack[TArgs],
    **kwargs: Unpack[TKwargs],
) -> Callable[P, R]:
    return _partial(func, *args, **kwargs)  # type: ignore


def foobar(a: str, /, b: list[str], *, c: int) -> str:
    ...


three = partial(foobar, c=3)
oblique urchin
# rare scarab No.

ok I can't change that ๐Ÿ™‚ Feel free to report a bug to Pyright.

#

I don't think bound=TypedDict is acceptable in general though, TypedDict isn't itself a type

#

also, that looks like you're reinventing ParamSpec

rare scarab
#

maybe.

#

I'm trying to extract things from ParamSpec and exclude it from the returned callable

#

does Paramspec support slices?

oblique urchin
#

no it doesn't

soft matrix
#

you are aware most type checkers already have builtin functools.partial support

rare scarab
#

I can't concat 2 paramspecs either?

#

So I have to actually call the partial to get its param spec

#

otherwise it's just partial[T]

oblique urchin
soft matrix
#

oh pyright does

#

idk about mypy i just assumed it did

rare scarab
oblique urchin
#

interesting, Eric doesn't like special casing things ๐Ÿ™‚

rare scarab
#

At the same time:

soft matrix
#

actually i cant find any reference to it

#

maybe im just imagining things

rare scarab
#

maybe typeshed adds types to it?

oblique urchin
#

no it's there ```packages/pyright-internal/src/analyzer/constructorTransform.ts:// Applies a transform for the functools.partial class constructor.
packages/pyright-internal/src/analyzer/constructorTransform.ts: // We assume that the normal return result is a functools.partial class instance.
packages/pyright-internal/src/analyzer/constructorTransform.ts: if (!isClassInstance(result.returnType) || result.returnType.details.fullName !== 'functools.partial') {
packages/pyright-internal/src/analyzer/constructorTransform.ts: // the "partial" mechanism, mark it has having a default value.
packages/pyright-internal/src/analyzer/constructorTransform.ts: // Create a new copy of the functools.partial class that overrides the call method.

#

(from git grep partial\\b in pyright repo)

soft matrix
#

oh yeah

#

i was only searching the first page of releases

#

sure would be nice though if pep 612 made partial mostly typeable

rare scarab
#

Special casing is no fun

#

I just realized why *args: *TArgs doesn't work with from __future__ import annotations. It really doesn't like the syntax.

oblique urchin
rare scarab
#

I'm aware ๐Ÿ™‚

hearty shell
oblique urchin
hearty shell
#

True

oblique urchin
#

so he doesn't have to write special special cases for each library

#

instead it's one general special case

rare scarab
#

It's a special case to end all special cases

#

pyright is being uncooperative with this new typing

#

I figured it out.

hearty shell
#

It is not really meant for that yeah x)

rare scarab
#

This is what it wanted.```py
Args: TypeAlias = tuple[str, int]

hearty shell
#

Also, you cant unpack kwargs

#

I think...

rare scarab
#

well pyright isn't complaining

hearty shell
#

Oh I think it might just be TypeVarTuple that you cant use with kwargs then

rare scarab
#

When I put a non Unpack[TypedDict] in kwargs, it says

Expected TypedDict type argument for Unpack

#

They should add NamedTuple as a possible unpack value for *args

oblique urchin
#

pyanalyze accepts it too

hearty shell
#

I see, interesting

maiden mesa
#

I have a package which uses python 3.10's union type hinting using the | symbol, however I want users who are using python3.9 to be able to use the package as well, i've added typing extensions but it doesn't quite seem to work, what i might be doing wrong? this is what i have in my pyproject.toml:

[tool.poetry.dependencies]
typing-extensions = {version = "^4.1.1", python = "3.10"}
// other dependencies here
oblique urchin
maiden mesa
#

Gotcha, thanks!

brisk heart
#

Any way I could implement the 3.10 union syntax during annotation resolution? I feel it'd be cool to still support it when doing reflection stuff but I don't wanna affect anything outside of the resolution

#

Like yeah I could overwrite the bitwise or for types but I'm not sure how reliable that is and it might maybe break something

stark shuttle
#

You could implement a custom codec which rewrites the token stream before parsing, that's the "usual" black magic people use to implement non-standard syntax.

#

That's sufficiently cursed that I've never seen it in a production library though.

little hare
#

sadru you better not do that >:(

stark shuttle
#

I don't think anything less would work though, overriding bitwise or for types is not going to work reliably.

#

I would strongly recommend just giving up on this feature, as I have for Hypothesis (which is not shy about black magic)

brisk heart
#

fair

#

Most people should've upgraded to 3.10 anyways at this point

#

I've wanted to do this for months but all my attempts failed and the previous convo here just reminded me of it

stark shuttle
# brisk heart Most people should've upgraded to 3.10 anyways at this point

If you can, sure, but eg pytorch doesn't support 3.10 yet, and the latest PyPy is 3.8 - there are good as well as bad reasons not to be there yet.
"If you want support for older versions we can discuss my consulting rate" is a very valid position though, as an OSS dev the ease-of-dev/ease-of-deploy tradeoff is up to you ๐Ÿ˜ƒ

trim tangle
#

Yeah I doubt most people are ok with 3.10+

trim tangle
#

but yeah pypy 3.10 is not coming out soon

hearty hemlock
#

Do you know how I'd typehint the return type of a class decorator which returns another class. E.g:

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

def decorator(cls: T) -> ?:
    class NewClass(Mixin, cls):
        pass
    return NewClass```
hearty shell
#

Not possible to typehint the return of a function with a class that is exclusively defined inside it's scope. But you can make a protocol for NewClass in the outer scope and return that instead

#

Also given you have a type variable type[T], if you are tying to add properties of type T, you cant do that, you would need an intersection type

#

which is sadly not supported yet

boreal ingot
#

(why do you want a decorator instead of just having cls inherit from Mixin when it is created)

hearty hemlock
#

well because it's cooler. Also dataclasses do the same, but they probably have extra status

#

From a typechecker perspective

hearty hemlock
boreal ingot
#

you can have generic protocols, but that is not powerful enough to cover multiple inheritance like you have

hearty shell
brazen jolt
#

yeah, py class Proto(Protocol, T): ... isn't a thing, rather ```py
class Proto(Protocol[T]):
...

hearty hemlock
#

sadly

brazen jolt
#

but if your decorator is really purely for adding a mixin to the class, I'd suggest not using a decorator and just doing it manually

#

interesection type would be really complicated to implement and you shouldn't expect it any time soon

#

unless it would be in some limited form

hearty hemlock
#

Also I'm not even sure if my example was right. Is it

T = TypeVar("T")
def decorator(cls: type[T]): ...```
or
```py
T = TypeVar("T", bound=type)
def decorator(cls: T): ...```
hearty hemlock
brazen jolt
#

it's type[T]

#

pretty sure a typevar bound to type would just cover everything

#

actually, both would probably work

#

interestingly enough, even though object is a subclass of type, type-wise passing instances of objects wouldn't meet being bound to type

hearty shell
#

it is the other way around iirc

boreal ingot
#

!e py print(type.__bases__) print(object.__class__)

rough sluiceBOT
#

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

001 | (<class 'object'>,)
002 | <class 'type'>
brazen jolt
#

oh, that's interesting, I though type would be in the MRO of every object

hearty shell
#

Yeah, type is a subclass of object (like everything else) and object class is an instance of type

trim tangle
#

object is an instance of type, but object is not a subclass of type

brazen jolt
#

ah

boreal ingot
#

it makes sense when you think about it, but it is a bit confusing

trim tangle
#

For example, an integer (like 42) is not a type.

#

But it's an object.

#

object cannot be a subclass of type because type is a subclass of object

brazen jolt
#

yeah, that does make sense, but it really is pretty confusing

hearty shell
#

Probably less then python 2 x)

brazen jolt
#

glad I got to skip that one

#

I only came to python from like 3.6, and while people were still using 2 and making guides for it etc. I never actually used it since 3 was a thing

#

but I've seen some quirks of it

hearty hemlock
#

I didn't quite keep up

hearty shell
#

Either, but since the best you can do is just return the same class, you dont even need the bound

T = TypeVar('T')
def decorator(cls: T) -> T: ...
#

The difference is that with type[T] you have access to T as an instance as well

hearty hemlock
#

Ok

hearty shell
#

For example, if your decorator returned an instance, that could only be done with type[T]

#
T = TypeVar('T')
def factory(cls: type[T]) -> T: ...
hearty hemlock
#

But I'd still do the bound, because otherwise this would be valid:

decorator("Hi")```(for the first version)
hearty shell
#

ah right

hearty hemlock
#

then in my case I could annotate the return type as Intersection[Mixin, T]

soft matrix
#

mypy and pyright already have intersection types, you just cant make them from python yet

hearty hemlock
brazen jolt
hearty shell
hearty hemlock
#

Lol 2014

soft matrix
#

It's also in pep 483

brazen jolt
#

though still not that much more recent

hearty shell
#

someone mentions in it that they are working on a pep for it, but that was December last year so I don't know what happened to that

brazen jolt
# hearty hemlock would it really be so complicated? ```py class C(A, B): pass x: Intersectio...

well, say you defined an intersection of str and dict, except they have completely different structures and aren't really compatible like that, for example __iter__ in one refers to individual characters, while in the other it refers to stored keys. What happens in an intersection? Do both get added as overloads? if so, would that really make sense? Not to mention things like handling variables named the same but with different types, how would you combine this? ```py
class Foo:
def init(self, x: int):
self.x = x

class Bar:
def init(self, x: str):
self.x = x


A limited intersection may be possible, where there aren't any conflicting cases like these, but it's too complex to implement this fully since resolving cases like these in a way that suits everyone just won't happen
hearty hemlock
hearty shell
soft matrix
#

Yeah I'd agree with this it should just be typing.Never

brazen jolt
#

that would be fine, yeah

trim tangle
hearty hemlock
hearty shell
#

Yes that would be a form of intersection

trim tangle
#

I suppose intersections would be more useful with Protocols, Callables and TypedDicts

hearty shell
#

More broadly an intersection of two types is a type that simultaneously satisfies the semantics of both "intersectioned" types, whether Nominal or Structural (Protocols)

#

So Intersection[Nominal1, Nominal2] is a type that inherits from both Nominal1 and Nominal2, Intersection[Nominal1, Structural1] is a type that inherits from Nominal1 and also has the properties of Structural1, and Intersection[Structural1, Structural2] is a type that has the properties of both Structural1 and Structural2 (with no inheritance constraints)

#

Intersection[Nominal1, Nominal2] is a type that inherits from both Nominal1 and Nominal2
Actually I am not so sure about that one

#

Because class Foo(A, B): ... is not the same as class Foo(B, A): .... So Idk what the right definition would be

#

It should just be an error if the order of the inheritance matters right? Because that would suggest that the inherited classes overwrite eachothers methods

brazen jolt
#

it could also be implemented like set intersection

trim tangle
brazen jolt
#

so for example if A had (x: int) -> str and B had (x: int) -> int, intersection would be an empty set

#

if they were the same though, it would be present

trim tangle
#

maybe it's (x: int) -> Never

hearty shell
trim tangle
#

why?

hearty shell
#

wait, brain fart

#

nvm

#

x)

hearty shell
#

It already is, so yeah that stands

trim tangle
#

because that would break LSP

hearty shell
hearty hemlock
#

Wait it probably shouldn't be called intersection

hearty shell
#

Why not?

hearty hemlock
#

Because multiple inheritance is more like union

#
class C(A, B): pass```has methods of A and B
#

Not only methods which both have

#

so more like A | B not A & B

#

or am I completely wrong

hearty shell
#

When we say Union and Intersection, we are talking about the Union of terms and intersection of terms, not the set themselves. So the elements of the Union of type A and type B are all the elements that are either a term of type A or a term of type B. The Intersection of A and B, are all the terms/elements that are both an element of type A and type B

hearty hemlock
#

Oh ok

hearty shell
#

I dont think you are wrong, you are just taking a different interpretation that is not really useful in the typing context

hearty hemlock
#

Im really looking forward to that addition to python. What also would be nice is typechecker support for typing.Annotated.

hearty shell
#

What do you mean by "support"?

hearty hemlock
#

Well first some types like Range, Length which can be used in annotations like this: x: Annotated[int, Range[0, 10]] or y: Annotated[list, Length[10]] and then also validation for those types

hearty shell
#

That is orders of magnitude more difficult to implement then an intersection type though

hearty hemlock
#

yeah that's true

brazen jolt
#

it requries running the code though

#

unless it's a literal directly

#

you can't expect a type checker to know what an int value in a variable will be, after manipulations

hearty hemlock
#

yes

#

But even for literals it would be pretty cool

hearty shell
#

LiquidHaskell is an example of that and it uses SMT

brazen jolt
#

yeah, sometimes, but that could take ages depending on complexity of that algorithm, you could face infinite loops, and so many other things

#

some things can be abstracted, sometimes, but that'd be pretty limited

#

not to mention things like reading values from files/sockets/stdin

#

for literals, this could get implemented, but it's mostly a waste of time since you usually won't use literals, and it would be a very incomplete implementation

acoustic thicket
#

is this what dependent types are?

hearty shell
#

No, this is called refinement types

#

You take a type and refine it via a predicate, eg: "Larger then X", "In between X and Y", "With the form ..."

#

Actually it might also be dependent types, but I think that is in the "not interesting" side of dependent types, since a lot of things that are in theory dependent types are not actually called that

rare scarab
#

re: intersection type
Java and typescript do this using the & operator.

#

java

public class Foo<T extends IFoo & IBar> {

}

typescript

type IFoo = {foo: string;}
type IBar = {bar: string;}
type Foo = IFoo & IBar;
hearty shell
#

The same would probably be done for python, with Intersection[] for backwards compatibility

#

Just like Union

rare scarab
#

Both inspired by the set methods

#

One thing I wish I could do in python is something like typescript's typeof operator.

hearty hemlock
#

what does typeof do?

#

Question: is there a way to correctly annotate a return type for this func?

T1 = TypeVar("T1")
T2 = TypeVar("T2")

def add(a: T1, b: T2):
    return a + b```
#

It feels like there is still some work to do to make pythons type annotation system truly generic

hearty shell
#

"correctly" in what sense?

hearty hemlock
hearty shell
#

would it not be this?

class SupportsAdd(Protocol):
    def __add__(self, __x): ...

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

def add(a: T, b: T) -> T:
    return a + b
hearty hemlock
#

But there's no guarantee that __add__ returns the same type

#

Also a and b should be able to be of different types

soft matrix
#

i dont think theres a way to do that currently, youd need to feed the type of b into a and a into b

hearty hemlock
#

:(

hearty shell
#

Which I think is the norm when it comes to these operations

hearty hemlock
#
def add(a: T1, b: T2) -> operator.add[T1, T2]:
    return a + b```doesn't work unfortunately, but could be a solution (?)
hearty shell
#

so you can give it a float and a int for example

hearty shell
#

And isnt a planned feature as far as I know

hearty hemlock
#

yeah was just dreaming

brisk heart
# hearty shell would it not be this? ```py class SupportsAdd(Protocol): def __add__(self, _...

what about something like

from typing import Protocol, overload

T_contra = TypeVar('T_contra', contravariant=True)
T_co = TypeVar('T_co', covariant=True)

class SupportsAdd(Protocol[T_contra, T_co]):
    def __add__(self, x: T_contra, /) -> T_co: ...

class SupportsRAdd(Protocol[T_contra, T_co]):
    def __radd__(self, x: T_contra, /) -> T_co: ...

@overload
def add(x: SupportsAdd[T_contra, T_co], y: T_contra) -> T_co: ...
@overload
def add(x: T_contra, y: SupportsRAdd[T_contra, T_co]) -> T_co: ...

def add(x, y):
    return x + y
hearty hemlock
#

Re intersection: I think it would also be really nice to write them as A & B

hearty hemlock
brisk heart
#

something to do with subclasses I honestly don't know either ๐Ÿ’€

#

can't remember which one is which

hearty shell
#

Well, idk, just tried add(3, 4) and it is already complaining xD

brisk heart
#

whaaa, it worked for me last time I checked

hearty shell
#

Oh wait, it works in mypy, just not in an outdated version of pyright

#

Ah, the problem was this

brisk heart
#

edited, now it should work for on the playground

hearty shell
#

Yeah you edited, the position only stuff

#

Seems that mypy doesnt care about that

brisk heart
#

yeah tbh mypy is the weird one here

hearty shell
#

Interesting, yeah this is an intense specification of an add function ๐Ÿ˜‚

#

Why is add not defined like this in typeshed? ๐Ÿค”

rough sluiceBOT
#

stdlib/_operator.pyi line 52

def add(__a: Any, __b: Any) -> Any: ...```
hearty shell
brisk heart
#

please don't use it

#

or at least drop the radd stuff

#

feels pretty overcomplicated with it

hearty shell
#

xD

#

So many hacks, and some many functions with a million overloads

#

This is pretty good in comparison

brisk heart
#

most of the overloads I've seen should be fixed with Unpack

#

can't think of anything weird that's not caused by variadic arguments tbh

oblique urchin
hearty shell
#

Yeah that is the one I was thinking of when I said hacks ๐Ÿ˜‚

brisk heart
#

oh I remember seeing y'all talk about that a while ago actually

blazing nest
#

Hm, I kind of asked something similar before but what is the exact variance of a callable's return type?

oblique urchin
#

arguments are contravariant

blazing nest
#

Should I define my ParamSpec as contravariant then?

oblique urchin
#

no, variance is not meaningful for ParamSpec

#

variance is inherent basically

blazing nest
#

Ah, for ParamSpec yeah

hearty shell
#

The docs seems to suggest otherwise although I am not sure how it applies

oblique urchin
#

ParamSpec allows covariant=True at runtime but its behavior is not specified

hearty shell
#

Parameter specification variables created with covariant=True or contravariant=True can be used to declare covariant or contravariant generic types.
Not sure how that works x)

soft matrix
#

it currently does nothing

blazing nest
#

What variance would I apply for a typevar I use like Callable[[T], T]? It has a bound class.. which I think I want covariant?

oblique urchin
#

Not sure we should have made those flags exist at all. For TypeVarTuple we just don't accept them.

oblique urchin
blazing nest
#

Ah yeah because the arguments were contravariant, and the return type covariant

hearty shell
oblique urchin
soft matrix
#

why doesnt TypeVarTuple support variance?

#

cause it seems like it'd be easy enough to specify

hearty shell
#

bounds would be nice too

#

There was an example recently here where It seemed like a natural use

#

had to do with overloads

blazing nest
#

Another thing I've already asked about, but ended up with a bug report that worked against me ๐Ÿ˜…...

I have made a utils for users to use: ```python
CommandT = TypeVar('CommandT', bound=CommandMiddlewareMixin)
MiddlewareDecorator = Callable[[CommandT], CommandT]

```python
def func() -> MiddlewareDecorator:
    ...
    return check(...)

Thing is, MiddlewareDecorator is currently just Callable[[Any], Any]. If I give it a type (which I don't want the user to need to do; I would just like to be the CommandMiddlewareMixin because that's the "bottom type") then it becomes invariant right?

def test(command: SubcommandGroup) -> SubcommandGroup:
    return command


def x() -> MiddlewareDecorator[CommandMiddlewareMixin]:
    return test
Expression of type "(command: SubcommandGroup) -> SubcommandGroup" cannot be assigned to return type "MiddlewareDecorator[CommandMiddlewareMixin]"
  Type "(command: SubcommandGroup) -> SubcommandGroup" cannot be assigned to type "MiddlewareDecorator[CommandMiddlewareMixin]"
    Parameter 1: type "CommandMiddlewareMixin" cannot be assigned to type "SubcommandGroup"
      "CommandMiddlewareMixin" is incompatible with "SubcommandGroup"
#

I want a way to say:

A callable that takes in an argument that is a subclass of CommandMiddlewareMixin and returns the same type

#

๐Ÿ˜ฉ

trim tangle
#

if you want to specify the bound in square brackets, that's not possible.

hearty shell
hearty shell
#

Or you saying you wanted a solution that didnt involve that and still made the error

blazing nest
#

...which of course passes ๐Ÿ˜…

oblique urchin
hearty shell
soft matrix
hearty shell
blazing nest
#

A very different error though, so I am not sure if that's something wrong with how I added it there?

blazing nest
hearty shell
brisk heart
#

oh noes

hearty shell
#

This is also a bug in pyright but

#
class A:
    def __add__(self, __other) -> int: return 42

class B(A):
    def __radd__(self, __other) -> str: return "foo"

reveal_type(A() + B())  # Type of "A() + B()" is "int"
#

This is actually "foo" at runtime

#

TIL ๐Ÿ˜‚

brisk heart
#

that's weird ๐Ÿค”

#

I'm surprised radd takes priority

hearty shell
#

I know, I had no idea, but it is documented

oblique urchin
#

that's only if the RHS is a subclass of the LHS I think

hearty shell
#

Note If the right operandโ€™s type is a subclass of the left operandโ€™s type and that subclass provides a different implementation of the reflected method for the operation, this method will be called before the left operandโ€™s non-reflected method. This behavior allows subclasses to override their ancestorsโ€™ operations.

#

mypy gets it right, I wonder how that is implemented

hearty shell
#

Hummm, how does self variance work again?

#

Specifically when it comes to protocols

#

Mypy wants self: T with T being covariant, while pyright wants it contravariant

#

This is fine by mypy, but pyright doesnt like it

#
from typing import Protocol, overload, TypeVar, Any


T = TypeVar('T')
T_co = TypeVar('T_co', covariant=True)
T_contra = TypeVar('T_contra', contravariant=True)

Self = TypeVar('Self', covariant=True)

class SupportsAdd(Protocol[Self, T_contra, T_co]):
    def __add__(self: Self, __other: T_contra) -> T_co: ...

class SupportsRAdd(Protocol[Self, T_contra, T_co]):
    def __radd__(self: Self, __other: T_contra) -> T_co: ...

class SupportsLRAdd(Protocol[Self, T_co]):
    def __add__(self: Self, __other: T_contra) -> T_co: ...
    def __radd__(self, __other: Any) -> Any: ...

@overload
def add(x: SupportsLRAdd[T, T_co], y: T) -> T_co: ...
@overload
def add(x: SupportsAdd[T, Any, Any], y: SupportsRAdd[T, Any, T_co]) -> T_co: ...
@overload
def add(x: SupportsAdd[Any, T_contra, T_co], y: T_contra) -> T_co: ...
@overload
def add(x: T_contra, y: SupportsRAdd[Any, T_contra, T_co]) -> T_co: ...

def add(x, y):
    return x + y

class A:
    def __add__(self: Any, __other: Any) -> int: return 42

class B(A):
    def __radd__(self: Any, __other: Any) -> str: return "foo"

class C:
    def __radd__(self: Any, __other: Any) -> bool: return True

class D:
    def __add__(self, __other) -> int: return 42
    def __radd__(self, __other) -> str: return "foo"

reveal_type(add(A(), B()))  # Revealed type is "builtins.str*"
reveal_type(add(A(), C()))  # Revealed type is "builtins.bool*"
reveal_type(add(D(), D()))  # Revealed type is "builtins.int*"
soft matrix
#

i think thats a pyright bug covariance there makes sense to me

hearty shell
#

I see, I wonder how it works with typing_extensions.Self, I havent tested that yet

soft matrix
#

it wouldnt do anything for you here

hearty shell
hearty shell
#

Not even sure this covers everything

soft matrix
#

idk this seems alright to me

hearty shell
#

Nvm you still have the same issue since you could just subclass D

#
class D:
    def __add__(self, __other) -> int: return 42
    def __radd__(self, __other) -> str: return "foo"

class E(D):
    def __radd__(self, __other) -> str: return "foo"

D() + E() would say it returns int while it should return str

#

I think this can might be possible with bounded type vars, but then if one wanted to do this to all operators, you would need a million different type var, each one bounded by a specific protocol

hearty shell
#

... it also makes add(2.3, 2) reveal as int

#

Omg I didnt even think about that fact that float + int should return int according to the notion that int is a subclass of float since it redefines __radd__ but then of course at runtime it would return a float

#

So the idea really is

class Foo:
    def __add__(self, __other) -> A: ...
    def __radd__(self, __other) -> B: ...

class SubFoo(Foo):
    def __radd__(self, __other) -> SubB: ...

reveal_type(add(Foo(), Foo())) # should reveal A
reveal_type(add(Foo(), SubFoo())) # should reveal B, not SubB
trim tangle
#

I think I don't get it

indigo locust
#

I know every static checker is different

#

But what if I'm in this situation...

#
class MyDescriptor(Generic[T_Type, T_Owner]):

  def __init__(self, type: Type[T_Type]) -> None:
    ...

  def __set_name__(self, owner: Type[T_Owner], name: str) -> None:
    ...
#

Is it possible to infer two different defining typevars across the instantiation of the object?

blazing nest
# trim tangle I think I don't get it

I have a system where the user returns a decorator. This decorator can be placed on any CommandMiddlewareMixin instance.

To aid the user, I want to provide an alias they can use so that when they make their code they can annotate the return type with this. Here is what I am imagining: ```python
def some_user_defined_whatever() -> LibraryProvidedTypeAlias:
def inner():
return 'bananas'

return library_code(inner)  # returns (<instance of CommandMiddlewareMixin>) -> <same instance of CommandMiddlewareMixin>]
#

Note that I also use this annotation a lot internally, so I would really like to write an alias for it because it is repeated so much

vapid quarry
#

Hi, which one is correct?:

class Foo:
  pass

dictionary:dict(Foo) = {}
dictionary:dict[Foo] = {}
blazing nest
#

-but also.. neither

#

dict takes two arguments. The key and the value

vapid quarry
blazing nest
#

dictionary: dict[str, Foo] = {} for example

vapid quarry
blazing nest
#

Yeah

#

An instance of string, and an instance of Foo, but yes.

vapid quarry
#

Got it thank you!!

hearty shell
#

He is not happy about it x)

#

So if the rules followed by the runtime are as convoluted as it appears they are, there's no way for a static type checker to determine the correct answer statically. I would therefore advise against using a pattern where you have a class hierarchy where the parent implements __add__ and the child implements __radd__ or vice versa.

#

If this could be ignored making the operators in the operators module generic would be much easier

#

returning an Union might be the best that can be done while still maintaining parity with the runtime behaviour

oblique urchin
hearty shell
#

The problem is that all you have to do is have the function declared, so it doesnt really violate LSP I think, but it definitely violates principle of least astonishment

#
class A:
    def __add__(self, __other) -> int: return 42
    def __radd__(self, __other) -> str: return "foo"

class B(A):
    def __radd__(self, __other) -> str: return "foo"

A() + A()  # 42 at runtime
A() + B()  # "foo" at runtime
oblique urchin
#

Doesn't mypy yell at you if your __add__ and __radd__ are incompatible?

hearty shell
#

Only partially I think, but that is probably implementation stuff

#
class A:
    def __add__(self, __other: 'A') -> int: return 42
    def __radd__(self, __other: int) -> str: return "foo"
        
class B(A):
    def __radd__(self, __other: object) -> str: return "foo"

reveal_type(A() + A())
reveal_type(A() + B())
#

this on the other hand works just fine

rough sluiceBOT
#

mypy/checkexpr.py lines 2585 to 2588

# STEP 2a:
# We figure out in which order Python will call the operator methods. As it
# turns out, it's not as simple as just trying to call __op__ first and
# __rop__ second.```
near mural
#

Hi! I see a lot of these if TYPE_CHECKING imports. Why are they used? Why is TYPE_CHECKING needed here? Why don't they just simply import it without the if?

# Something like this
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from somewhere import something

def func(arg: "something"):
    ...

(ping me if you respond)

void panther
#

TYPE_CHECKING resolves to False at runtime, so the import doesn't execute (and doesn't cause import cycles, or unnecessary imports)

#

then the "something" annotation is used so python doesn't try to use the non-existent name when you run the code @near mural

near mural
#

Ok. Thanks!

near vigil
#
class ChildClass(ParentClass):
    child_attribute = 'something'

def my_function(argument: MyClass[ChildClass]):
    return argument.child_class.child_attribute  # cannot access member "child_attribute" for type "ParentClass"    Member "child_attribute" is unknown
``` How do I make my type checker know that `MyClass.child_class` will be the one inside the `[]` on the annotation and not just any class inheriting `ParentClass`?
hearty shell
#

What does myclass look like?

near vigil
#

jank with __class_getitem__ that returns an instance of itself, but uh this has to do with discord.py converters and how they work

hearty shell
#

Oh no

near vigil
#

yes its bad

soft matrix
#

You should probably be using commands.param if you can

near vigil
#

oh right that's a thing

#
class WithFlags:
    def __init__(self, result: str, flags: FlagConverter):
        self.result = result
        self.flags = flags

    def __class_getitem__(cls, item: Type[FlagConverter]) -> ActualConverter:
        return ActualConverter(item)
#

i'm aware it's shit, but the type checker was mad at me calling something inside the type hint

hearty shell
#

if you wanted runtime introspection without interfering with type checkers, you can always make MyClass an alias for Annotated

#

at if TYPE_CHECKING

soft matrix
#

Annotated support was also recently added to v2 so that would also be a good solution

hearty shell
#

Actually iirc it is a bit ugly, last time I tried that I had to do from typing import Annotated as ...

near vigil
#

Yeah blobpain hmmm

#

I find annotated ugly, or at least like Annotated[FlagResult, WithFlags[SQLFlags]]

#

i guess it's my best option though, lol

hearty shell
#

No, I mean

near vigil
#

does annotated allow you to not pass the 2nd arg?

hearty shell
#

MyClass[ChildClass]

#

Like

#
if TYPE_CHECKING:
    from typing import Annotated as MyClass
else:
    class MyClass:
        ...
        def __class_getitem__(cls, item):
            return cls
near vigil
#

lol, that also is ugly, but i think that would do it

#

ill just drop it in a helper file and pretend it doesn't exist

#

wait no that doesn't fix anything lmao

brisk heart
near vigil
#

yes

#

well

brisk heart
#

I added it to slash commands in disnake after dpy got shut down

near vigil
#

two things, dpy isnt shutdown and you're talking to the man who PRed commands.parameter into official d.py lmao

brisk heart
#

When I saw the abbreviation I assumed differently mb

near vigil
#

you're good lol dw

brisk heart
#

And I am aware it isn't abandoned anymore

#

Anyways fuck dpy and fuck the forks, hikari for life

near vigil
# near vigil well

I see your ๐Ÿค”, @hearty shell. so...
x: WithFlags[Thing] means that x.thing would be a Thing instance. x would still be an instance of WithFlags

#

and uh I just can't find a way to do that. I don't even know if it's possible lmao. But I feel i've seen this behaviour before. but i have no clue where i saw it

upbeat wadi
brisk heart
#

Yeah ik now

#

Looked at the source code

upbeat wadi
#

Ah i didn't see Leo's message

brisk heart
#

I haven't been keeping with dpy at all, it's absolutely mb

#

And I feel hikari-tanchi does this signature parsing stuff much better than dpy & forks anyways

near vigil
#

ooo how is it?

brisk heart
#

Converters with Converter[func] which is Converter(func) on runtime and the return type of func during type-checking

soft matrix
brisk heart
#

Yeah mb

#

I shouldn't be correcting peeps about dpy when I haven't used it in ages, I apologize for that

near vigil
#

like if it was Annotated? hmm

brisk heart
#

Through __getitem__ of a metaclass, doesn't work with mypy but I doubt anyone in their right mind uses mypy over pyright

#

If anyone is then explicit Annotated is recommended

near vigil
#

ah, that sounds like what I tried to do for a thing and failed

#

TargetVerifier[Member/Role/Channel/uhh others]

hearty shell
brisk heart
hearty shell
#

Something like this?

near vigil
#

let me try that!

brisk heart
#

__class_getitem__ is unfavorable with pyright in my experience because it always assumes it's only ever used for custom impls of Generic stuff and not for returning a custom type

soft matrix
#

i thought that was mypy

brisk heart
#

idk what the use case here is though so probs not a problem

#

mypy is generally unable to handle special behavior of both metaclass getitem and class getitem

#

Pyright just doesn't deal well with class getitem

hearty shell
#

But I though the point was that they dont deal with that no?

brisk heart
#

The only way I could ever get anything like it to work was with metaclass getitem to add some special behavior

hearty shell
#

It is perfect, as we can use that to lie and do runtime introspection while keeping the ugly internal

brisk heart
#

Purely because using __class_getitem__ never worked properly

oblique urchin
near vigil
brisk heart
#

I remember seeing some suggestion that there should be a way to tell what type-checker is being used to be able to define different annotations for them and I think that'd help a lot of my projects

#

for example in the example I sent I could just tell mypy that it's just an alias typing.Annotated since it can be used with an explicit type as Converted[runtime_type, converter_function] which is the exact same as typing.Annotated

hearty shell
#

Is that not already possible?

#

wasnt there a MYPY_CHECKING or something like that

brisk heart
#

there is already something like that? ๐Ÿ˜ณ

hearty shell
#

Well, it came before TYPE_CHECKING

oblique urchin
#

if MYPY: works like if TYPE_CHECKING: but only for mypy

hearty shell
#

So Idk what happend to it afterwords

oblique urchin
#

you just have to put MYPY = False in your code so it doesn't execute at runtime

brisk heart
#

oh that's cool actually

grave fjord
#

pyright doesn't respect if MYPY?

brisk hedge
#

it's treated like any other boolean outside of mypy

#

so declare it as False

#

pyright can then infer it to be False under most circumstances

hearty shell
#
class SupportsAdd(Protocol[_T_co, _T_contra, _R_co]):
    def __add__(self: _T_co, __other: _T_contra) -> _R_co: ...

class SupportsLRAdd(Protocol[_T_co, _T_contra, _T2_contra, _R_co, _R2_co]):
    def __add__(self: _T_co, __other: _T_contra) -> _R_co: ...
    def __radd__(self: _T_co, __other: _T2_contra) -> _R2_co: ...


def add(x: SupportsAdd[_T, _T1, _T2], y: SupportsLRAdd[_T, _T1, _T, _T2, _T3]) -> _T2 | _T3: ...


class A:
    def __add__(self, __other: Any) -> A: ...

class SubA(A):
    def __radd__(self, __other: SubA) -> bool: ...


t: SupportsLRAdd[A, Any, A, A, bool] = SubA()  # Correctly erros

reveal_type(add(A(), SubA()))  # Fine
#

Does anyone know why add(A(), SubA()) is fine here?

#

SupportsLRAdd[A, Any, A, A, bool] is just my best estimate as to what those variables resolve to when doing the above

urban root
#

Hey there. Why does pyright say
Function declaration "callback" is obscured by a declaration of the same name
with this code

def _generate_callback(cls: Union[Type[_Command], CB], fake: bool = False) -> CB:
    if inspect.isclass(cls) and issubclass(cls, _Command):
        # Context menu callback relies on the annotation
        if fake:
            async def callback(interaction: Interaction):
                pass
        elif cls.__discord_app_commands_type__ is AppCommandType.user:
            async def callback(interaction: Interaction, target: Union[Member, User]) -> None:
                cls.__discord_app_commands_id__ = int(interaction.data['id'])  # type: ignore # This will always be present
                inst = cls()
                inst.interaction = interaction
                inst.target = target  # type: ignore # Runtime attribute assignment
                await inst.callback()
        elif cls.__discord_app_commands_type__ is AppCommandType.message:
            async def callback(interaction: Interaction, target: Message) -> None:
                cls.__discord_app_commands_id__ = int(interaction.data['id'])  # type: ignore # This will always be present
                inst = cls()
                inst.interaction = interaction
                inst.target = target  # type: ignore # Runtime attribute assignment
                await inst.callback()
        else:
            async def callback(interaction: Interaction, **params) -> None:
                cls.__discord_app_commands_id__ = int(interaction.data['id'])  # type: ignore # This will always be present
                inst = cls()
                inst.interaction = interaction
                inst.__dict__.update(params)
                await inst.callback()
        return callback  # type: ignore
    return cls  # type: ignore

I just don't get it. How is callback getting defined more than once?

hearty shell
#

Because the functions have different signatures

#

Doesnt matter if only one of then gets defined at runtime, when static typing there is no way for the type checker to know which one it is going to be

loud bloom
#

ah

urban root
#

that makes sense

#

so what should be done in this situation

blazing nest
#

Pyright has like a work-around where you give them different names and then do callback = ...

hearty shell
#

From the sniped you posted though, there seems to be a way to tell what callback it is going to be statically

#

I think

hearty shell
#

Can __discord_app_commands_type__ be known statically?

loud bloom
#

kinda...

hearty shell
#

Alright, well, I mean the solution I was talking about was just making overloads of _generate_callback, but this looks like a private method so might not be worth it x)

hearty shell
#

It was a bug all along ๐Ÿฅฒ

#
class ProtoFoo(Protocol[_T_co]):
    def foo(self: _T_co) -> None: ...

class Foo:
    def foo(self) -> None: ...

class SubFoo(Foo): ...
    
a: ProtoFoo[SubFoo] = Foo()  # Fine
oblique urchin
#

oh wait yes

#

it's generic over the self type of the method

hearty shell
#

Yeah, I think it is because it makes the comparison after the method bounding, but given that mypy explicitly complains if I type self as contravariant, there must be something under the hood that was meant to support this

#

regardless it is not of much use until pyright is convinced that self should be typed covariant, which it doesnt atm

hearty shell
#

x)

summer berry
#

How do I annotate a hashable sequence? Do I have to create a class which inherits both of these?

trim tangle
#

I'm afraid you'll have to just make a protocol with all the methods

onyx kayak
#

Hello, how can mypy still treat parsedSum as Optional[str]?

            if isinstance(parsedSum, str):
                dir = parseRecord(root, ".//nms:TtlNtries/nms:TtlNetNtry/nms:CdtDbtInd")
                if isinstance(dir, str) and dir == "DBIT":
                    parsedSum = -float(parsedSum) #Incompatible types in assignment (expression has type "float", variable has type "Optional[str]")mypy(error)
hearty shell
#

What do you mean? The problem here isnt the Optional part, it is trying to assign float to a str

brisk hedge
onyx kayak
#

Therefore if I parse a string from some file and want to cast it to float, I should create a new variable?

brisk hedge
#

Yes

onyx kayak
#

Makes sense, thanks

brisk hedge
#

It's a bit confusing to have a str value in something named parsedSum anyways haha

brazen jolt
#

any ideas why would map(asyncio.create_task, coros) not work type-wise while (asyncio.create_task(coro) for coro in coros) would?

soft matrix
#

If you remove the first one does it work?

brazen jolt
#

no

soft matrix
#

Well map is a class different from generator so idk what's up if it's not that

brazen jolt
#

I've tried that, the error did seem like it had problems assigning to tasks but commenting it out didn't work

#

huh

#

but renaming tasks to something else solved it

#

so it's probably a pyright bug

soft matrix
hearty shell
#

Mypy seems to have the same problem

brazen jolt
#

hm, actually it doesn't work with other names either, it was just pyright-playground being slow lol

hearty shell
#

Yeah I think the problem is how map is resolved

brazen jolt
#

so it's a typeshed issue?

#

if mypy has the same issue, it's probably just badly defined types in python itself

hearty shell
#
_T = TypeVar("_T")
_S = TypeVar("_S")

class map(Iterator[_S], Generic[_S]):
    def __init__(self, __func: Callable[[_T1], _S], __iter1: Iterable[_T1]) -> None: ...
    def __iter__(self: _T) -> _T: ...
    def __next__(self) -> _S: ...

class Task(Generic[_T]): ...

create_task: Callable[[Coroutine[Any, Any, _T]], Task[_T]]

coros: Iterable[Coroutine[Any, Any, int]]

tasks = map(create_task, coros)

reveal_type(tasks)  # Revealed type is "__main__.map[__main__.Task*[_T`-1]]"
#

I am just speculating but it looks like the problem is trying to give a generic function when initialising map

#

Resolving _S requires resolving Callable[[_T1], _S] which in this case is Callable[[Coroutine[Any, Any, _T]], Task[_T]], but to resolve _T you need to resolve __iter1: Iterable[_T1]

rustic gull
brazen jolt
rustic gull
#

Wait how placeholder returns None

brazen jolt
#

it doesn't

#

placeholder returns Iterable[int] and it's an async func, so it's actually returning a coroutine that returns that

rustic gull
#

Oh I didn't see the async

hearty shell
#
class Foo(Generic[_T]): ...

def foo(a: Callable[[_T], _S], b: Foo[_T]) -> _S: ...

bar: Callable[[_T], _T]
baz: Foo[int]

reveal_type(foo(bar, baz))  # Argument 1 to "foo" has incompatible type "Callable[[_T], _T]"; expected "Callable[[int], _T]"
oblique urchin
#

I think there are some mypy issues about this, it's pretty hard to get right

hearty shell
#

Literally from pyright

#
from typing import *

class Parent: ...

class ChildA(Parent):
    def __add__(self: 'ChildA', __other: int) -> int: ...

class ChildB(Parent):
    def __add__(self: 'ChildB', __other: bool) -> int: ...
    
class ProtoFoo(Protocol):
    __add__: Callable[[Parent, int], int]


a: ProtoFoo = ChildA()  # Fine
a: ProtoFoo = ChildB()  # Error
hearty shell
soft matrix
#

That makes sense to me

brazen jolt
#

well, kind of, if you didn't return NotImplemented, pyright would be correct here

hearty shell
#

Yes, but not to Eric

hearty shell
#

now try doing the same thing but instead of doing Callable use normal function syntax

brazen jolt
#

oh nvm, I missunderstood the issue here, yeah

hearty shell
#

Yeah this issue stems from me trying to define a Protocol as generic covariant on the first parameter of one of its methods, the self parameter, and then having pyright complain

#

saying that it should be contravariant

loud bloom
#

I'm doing something super cursed with metaclasses and I'm trying to figure out how to make it play well with Pyright

I have two classes, Foo & Bar
Foo is meant to be subclassed in order to be used (and not instantiated); Bar is not subclassed, it's an instance made for every Foo subclass

Foo has a metaclass with a __new__ that looks something like this:

def __new__(
    cls,
    classname: str,
    bases: tuple,
    attrs: Dict[str, Any],
):
    if not bases:  # This metaclass should only operate on subclasses
        return super().__new__(cls, classname, bases, attrs)

    sub_cls = super().__new__(cls, classname, bases, attrs)
    # Do stuff here
    return Bar(...)

However, with an example like this:

class Baz(Foo):
    pass

Baz becomes an instance of Bar, but the type checker thinks it's Type[Baz], a subclass of Foo... Is there a way to let Pyright know Baz is an instance of Bar?

torpid yarrow
#

anyone know why mypy would give this error for a library that is typed and built with a py.types marker?```error: Skipping analyzing "growstocks": module is installed, but missing library stubs or py.typed marker [import]

oblique urchin
torpid yarrow
oblique urchin
hearty shell
#

Not sure what is going on here, you are returning an instance of Bar for a class creation?

#

Actually, pretty sure there is no way, but who knows

little hare
pastel egret
#

Type[Baz] should also be recognised as a Bar too, you should see if you can define special methods etc. in general though metaclasses are tricky for type checkers, they should support basic operations.

torpid yarrow
hearty shell
#

But the fact that this doesnt work tells me that you cant actually do anything else:

class MetaMetaFoo(type):
    def __call__(self, *args, **kwargs) -> type[int]: ...

class MetaFooA(type, metaclass=MetaMetaFoo):
    def __new__(cls, *args, **kwargs) -> type[int]: ...

class MetaFooB(type):
    def __new__(cls, *args, **kwargs) -> type[int]: ...
    
class FooA(metaclass=MetaFooA): ...
class FooB(metaclass=MetaFooB): ...

a: int = FooA()  # Error
b: int = FooB()  # Error
#

maybe using some kind of descriptor would work?

torpid yarrow
#

While I'm here...

I have a type: ignore[no-untyped-def]at the top of one of my files to ignore all occurrences of that code, and it successfully works to disable that code, but at the same time it also gives Unused "type: ignore" comment... Any idea why?

loud bloom
loud bloom
#

for a subclass creation

#

so if i do

class Baz(Foo):
    pass

Baz is actually a Bar instance, not a subclass

loud bloom
loud bloom
#

a solution that partially works is doing if TYPE_CHECKING: Foo = Any

hearty shell
#
from typing import *

class Bar: ...
class Foo: ...
    
def deco(cls) -> Bar:
    return cls  # type: ignore

@deco
class Baz(Foo): ...

reveal_type(Baz)  # Type of "Baz" is "Bar"
#

@loud bloom Does this work?

loud bloom
#

im trying to avoid using decorators

#

but yeah that works

hearty shell
#

Well

#

This would also work

#
from typing import *

if TYPE_CHECKING:
    class Bar(type): ...
    class Foo(metaclass=Bar): ...
else:
    class Bar: ...
    class Foo: ...
    
class Baz(Foo): ...

a: Bar = Baz
loud bloom
#

unless I'm misunderstanding it, that doesn't work

hearty shell
#

Here Baz will be an instance of Bar

#

Isnt that what you wanted?

loud bloom
#

it's not for me

#

๐Ÿค”

hearty shell
#

is it a error?

loud bloom
#

yeah, Expression of type Type[Baz] can't be assigned to declared type Bar

hearty shell
loud bloom
#

codebase is quite huge, ill try to trim it down a little

hearty shell
#

Alright, for all methods and functions just strip the implementation

loud bloom
#
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    base = type
else:
    base = object

class Bar(base): ...

class Meta(type):
    def __new__(cls, classname, bases, attrs):
        if not bases:  # This metaclass should only operate on subclasses
            return super().__new__(cls, classname, bases, attrs)

        return Bar(...)

if TYPE_CHECKING:
    meta = Bar
else:
    meta = Meta

class Foo(metaclass=meta): ...

class Baz(Foo): ...

a: Bar = Baz  # Error
#

That's the gist of it

hearty shell
#

Humm, seems like the problem is having the metaclass be a variable and not the it directly

#
if TYPE_CHECKING:
    class Foo(metaclass=Bar): ...
else:
    class Foo(metaclass=Meta): ...
#

would have to be like this

loud bloom
#

oh hmm

#

ill try that

#

oh wow

#

that works

#

thank you! โค๏ธ

blazing nest
hallow fractal
#

hi, i got a question re. typing.

consider this snippet of code

from typing import TypeVar
from pydantic import BaseModel, Field

T = TypeVar("T")


class Thing(BaseModel):
    a: int = Field()

    @classmethod
    def query(cls: T) -> T:
        return cls(
            a=42,
            b=42,
        )


class ExtendedThing(Thing):
    b: int = Field()

here, def query(cls: T) -> T: is wrong because the actual return type is an instance of T (a type/class), not the actual type/class T, how do i specify this? (basically looking for opposite of typing.Type)

hearty shell
#

def query(cls: type[T]) -> T:

hallow fractal
#

oh yes of course, now i feel dumb heh

#

thanks!

trim tangle
#

also @hallow fractal, if you already depend on typing_extensions you can just use Self

hallow fractal
#

that's great info, thanks!

hearty hemlock
torpid yarrow
rose crypt
#

Hi, is there any way to get func param type with Optional by annotation?

rose crypt
#
import inspect
from dataclasses import dataclass
from typing import Optional

@dataclass
class T:
    a: str 

def foo(bar: T):
    print(bar.a)
    
def fooo(bar: Optional[T] = None):
    if bar:
        print(bar.a)
    
# without Optional, the annotation is the type obj
func_sign = inspect.signature(foo)
param = func_sign.parameters['bar']
p_type = param.annotation
print(p_type)
t = p_type(a=2)
print(t)

# with Optional, how to get the runtime type?
func_sign = inspect.signature(fooo)
param = func_sign.parameters['bar']    
p_type = param.annotation
print(p_type)
# how to make a t?
# t = p_type(a=2)
# print(t)
brisk heart
#

typing.get_args(Union[T1, T2]) == (T1, T2)

stark shuttle
blazing nest
#

!e ```python
from typing import Generic, ParamSpec, Union, TypeVar

P = ParamSpec('P')
RT = TypeVar('RT')

class Test(Generic[P, RT]): ...
class Subclass(Generic[P, RT]): ...

Alias = Union[Test[P, RT], Subclass[P, RT]]
Alias['...', object]

rough sluiceBOT
#

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

001 | Traceback (most recent call last):
002 |   File "<string>", line 10, in <module>
003 |   File "/usr/local/lib/python3.10/typing.py", line 311, in inner
004 |     return func(*args, **kwds)
005 |   File "/usr/local/lib/python3.10/typing.py", line 1061, in __getitem__
006 |     arg = arg[subargs]
007 |   File "/usr/local/lib/python3.10/typing.py", line 311, in inner
008 |     return func(*args, **kwds)
009 |   File "/usr/local/lib/python3.10/typing.py", line 1053, in __getitem__
010 |     raise TypeError(f"Expected a list of types, an ellipsis, "
011 | TypeError: Expected a list of types, an ellipsis, ParamSpec, or Concatenate. Got ForwardRef('...')
stray summit
#

anyone aware of a easy way to type annotate the kwargs to a function based on the fields/parameter of a different function?

i have types like

class MyModel:
   id: uuid
   name: str
   hostname: str

and i want to declare helpers like
def wait_for_update(model_or_id: Model|uuid, **kw: Magic) -> Model):
where i would like kw to be a non total TypedDict thats the fields of MyModel without id

acoustic thicket
stray summit
stray summit
acoustic thicket
hearty shell
#

model_or_id does that take a model instance?

stray summit
#

model or id takes either the uuid or a model instance

#

(this is a wrapper for some painfull openapi based stuff)

stray summit
#

the most annoying thing about python typing is that one cant compute or "lift" them :/

acoustic thicket
#

hmm

stray summit
#

basically i cant just "unpack" the Model" i have to go from "Model" which has a id, to a TypedDict that does NOT have the id and additionally is total=False, and even thats just a basic half measure

#

if i want to type helper methods that mirror a subset of the ctor arguments of a model, then i suddenly have to suddenly do something else and painfull

rose crypt
#

@brisk heart @stark shuttle thanks, it works

#
import typing
f_hint = typing.get_type_hints(fooo) 
p_hint = f_hint['bar']
types = typing.get_args(p_hint)
print(types)
p_real_type = next(_type for _type in types if _type is not None)
t = p_real_type(a=3)
print(t)
#

if type(_type) != type(None)

trim tangle
#

On a serious note, yeah, type-level programming is very cool

#

Have you tried TypeScript?

stray summit
# trim tangle You can, with mypy plugins || ๐Ÿฅฒ ||

so aware of any mypy plugin that will actually let you programm those lifts in your code/library? else its just very hard and frustrating

i mean i'm also looking for stuff that takes a set of classes that are an interface for some sans web api description and lifts async/sync wrappers for them around it

aka ```python

class MyApi:
def myMethod(id: UUID) -> RequestReturning[MyModel]:
magic...

apiclient = RequestsCLient(MyApi)

apiclient.myMethod(someuuid) # returns MyModel

async_api_client = HttpxAsyncCLient(MyApi)

await async_api_client.mymethod(someuuid2) # yield MyModel

oblique urchin
stray summit
hearty shell
#

Is there really no way to take a ParamSpec from one function and give it to another as optional? ๐Ÿค”

stray summit
#

also its a pain that we cat jsut use signature objects to declare callable interfaces

magic meteor
#

uh

indigo locust
#

Is there a way I can inherit typing.Annotated?

#

Basically, I'd like to write a class which, to the static checkers of the world, types exactly like some wrapped class

#

Except it carries with it some extra annotational data and methods

soft matrix
#

not currently

indigo locust
#

Well damn

#

That sucks XD

#
from types import GenericAlias

from typing import Annotated
from typing import Generic
from typing import TypeVar

T_Annotated = TypeVar('T_Annotated')

class _ReadOnly(object):

    def __getitem__(prototype, annotated: T_Annotated) -> T_Annotated:
        return Annotated[annotated, 'ReadOnly']

ReadOnly = _ReadOnly()

a : ReadOnly[int]
print(__annotations__)
#

Does the job, it seems ๐Ÿ˜

#

I'd have preferred an actual wrapper around the modified type, since then I could have implemented some operators

#

Beartyping has a validator system which uses bitwise notation to combine with other annotations or regular types

soft matrix
#

im hoping to at some point make a pep that generalises things like Annotated, TypedDict and LiteralStr so that they are less rigid to use

#

but idk when ill have the time to do all that

indigo locust
#

I mean... ๐Ÿ˜

#

At the core of this, I suppose, is the fact that __getitem__ is technically lying around what it returns

#

Which is fine, since functionally there is no different between Annotated[int, ...] and int and that's pretty much the point

#

So I guess instead of returning Annotated[annotated, 'ReadOnly'], I could just as easily return an instance of _ReadOnly... maybe...?

hearty shell
#

Doesnt look like it ๐Ÿค”, although for stuff like this I think it is usually best to lie to the typecheker using if type checking and make your own Annotated

brisk heart
#

not sure about mypy but it definitely works for pyright

fierce ridge
#

are you expected to use TypeAlias for that?

oblique urchin
fierce scroll
#

Can anyone assist me with type hinting Mixin classes that live in other modules? I have a class let's say:

# foo.py
from ._mixins import AMixin

class Foo(AMixin):
    ....
# _mixins.py
class AMixin:
    def do_something(self: Foo) -> Foo:
        # circular ref on imports now ?
        # typing.TYPE_CHECKING won't work
#

the mixin interface is fluent so it returns self

#

I can use a protocol? but the Foo class will be substancial in size

trim tangle
#

I was under the impression that the point of a mixin is that it can be used with different classes

hearty shell
#

You are type hinting the parent class with a subclass?

trim tangle
#

You might want to use typing_extensions.Self, or the equivalent solution with a TypeVar

fierce scroll
#

ideally yeah, i'm writing an assertions library and the Foo class would be colossal so I was trying to move things out into mixins

#

i.e StringMixin, RegexMixin etc

hearty shell
#

Ahh, I see, then typevars/Self should be your solution yeah

#
class AMixin:
    def do_something(self: Foo) -> Foo:

this wouldnt typecheck even if they were in the same module you eliminated circular references

#
from typing_extension import Self

class AMixin:
    def do_something(self) -> Self:
T = TypeVar('T', bound='AMixin')

class AMixin:
    def do_something(self: T) -> T:
fierce scroll
#

I need autocomplete for my users (its vital), maybe I should just move back to composition but the number of 'delegating' methods in the main class would be quite substancial, I'm just not happy with either approach, need to bang my head on the wall some more!

trim tangle
#

well, if a class has 200 methods that's some complexity for ya

#

if you split it into mixins, now the user has more indirection when reading the code.

#

"which mixin is this method defined in again? let me search all of them one by one..."

fierce scroll
#

Yeah it's a bit of a rock and a hard place, when all mixin types are typically a 'possibility' for pretty much any value

#

much easier if i care less about types/autocomplete, but that's something I really want

#

perhaps i shall move over to software-design to assistance on that front, thanks for the help here tho - been very useful

rustic gull
#

al

trim tangle
#

My new favourite way of getting an Unknown:

[][0]
#

Interestingly, mypy and pyright react differently to {}[0]

hearty shell
#

Have any of you ever benefited from the fact that type[SubA] is compatible with type[A]?

brisk heart
#

I guess orms would use it

hearty shell
#

But the constructor isnt type checked, so is that annotation really that useful when it comes to orms?

#

In fact that only way it would seem to be useful is if the constructor was checked for compatibility across subclasses, as stated in the documentation

brisk heart
#

I was thinking like select(Model) if its defined like class Model(ormlib.BaseModel)

hearty shell
brisk heart
#

start a select query

row = await select(Model).where(Model.a == 1).one()
#

I'm not sure if any orm actually works like this because I haven't used one in ages

hearty shell
#

Humm, I think usually something like select would be a method or classmethod on the models, but yeah in that case there would some value for annotating the select function. But the usefulness stops there I think, you can't really have any idea what fields would be returned if you annotated it as

def select(model: type[BaseModel]) -> SelectModel: ...

I guess where it be more useful for this case indirectly is using as a type bound for a type variable

hearty shell
#

like

T = TypeVar('T', bound=type[BaseModel])
def select(model: type[T]) -> SelectModel[T]: ...
brisk heart
#

actually yeah

#

damn

blazing nest
#

I do runtime type checks of the instructor anyway

#

If I have code like that, I usually annotate a parent class (like a mixin or simply a "marker" class) I want users to subclass

hearty shell
hearty shell
hearty shell
#

granted the constructor is the only "real" problem, which the docs do say that type checkers should check

summer berry
blazing nest
trim tangle
#

I would file a bug on mypy

summer berry
hearty shell
soft matrix
summer berry
#

The typevar/generic thing was just my attempt to make it work

#

Would I just have to cast it in this case?

hearty shell
#

Not possible I think, you are trying to "dynamically" map literals to types, so you would need a way to alter the supported keys of a typeddict throughout "compilation", or a way to add overloads to a function in a similar way

#

both of which are not currently possible as far as I know Both of which are definitely not supported, and I think for similar reason __subclasshook__/ABCs is also not supported

summer berry
#

Thanks. I started to suspect it wasn't possible the more I thought about it. That's why I mentioned casting at the end.

summer berry
#

Yeah, I saw that mentioned but couldn't really follow the discussion.

hearty shell
# summer berry Yeah, I saw that mentioned but couldn't really follow the discussion.

The gist of it was

  • class properties are not actual properties as they remain instances of the classmethod class, and not the property class, this breaks stdlib and third party assumptions about properties
  • It does not allow for setter semantics you would expect with a normal property
  • staticmethod and abstractmethod do not compose well with property
  • Reconciliating all these factors would evolve braking changes and or a lot of special casing
summer berry
#

I see. Indeed I have experienced the woes of trying to make abstract class properties

hearty shell
summer berry
#

Oh, that's weird

#

Then I guess that what I quoted above is the reason for it

digital wraith
#

Are there any use-cases where strict-typing is not "better"?

hearty shell
#

Oh right, the reason it is unknown is because that actually doesnt work with normal classes

#

because you have not implemented the __match_args__

#

I think

summer berry
#

Oooh yeah good point

#

let me try to add that

#

Actually I'll just use kwargs in the pattern

#

Yup then I get the same issue as with a dataclass

summer berry
#

Unrelated, another thing that's been bothering is no type narrowing for something like ```py
a = A(1)
match a:
case A(str()):
B(a.value)

I have to do `str() as value` and use that instead to get the type narrowed, even though (at least for a human) it's possible to infer that `a.value` must be a `str` at that point.
summer berry
hearty shell
#

But map.get(int()) would never be anything else other then None

#

How would allowing that be useful?

summer berry
#

Here's a better example to show my situation. I think it makes more sense in this case:

class Parent: ...
class Child(Parent): ...

map: dict[Child, Any] = {}

def func(thing: Parent):
    if x := map.get(thing):
        ...
hearty shell
#

Humm, touche

#

But the same can be said about all other function, it is just that get does not raise an exception

def foo() -> object: ...

def bar(x: bool): ...

bar(foo())
#

There are at least 2 cases where this function call would be fine

summer berry
#

(using pyright)

ancient snow
#

got an error message?

summer berry
#
 Expression of type "tuple[C, ...]" cannot be assigned to declared type "HashableSequence[B]"
   "tuple[C, ...]" is incompatible with protocol "HashableSequence[B]"
     Type "(__x: SupportsIndex, /) -> C" cannot be assigned to type "(s: slice, /) -> HashableSequence[T@HashableSequence]"
       Parameter 1: type "slice" cannot be assigned to type "SupportsIndex"
         "slice" is incompatible with protocol "SupportsIndex"
           "__index__" is not present
       Function return type "C" is incompatible with type "HashableSequence[T@HashableSequence]"
         "C" is incompatible with protocol "HashableSequence[T@HashableSequence]"
           "__reversed__" is not present
   ...
ancient snow
#

๐Ÿคทโ€โ™‚๏ธ

summer berry
#

I looked at the stubs for tuple and it has the slice overload so I'm not sure why it doesn't want it here