#type-hinting

1 messages · Page 58 of 1

hasty hull
#

I thought this just worked? ```py
class Foo:
foo: int

def bar(foo: Type[Foo]) -> None:
...

class Bar(Foo):
pass

bar(Bar)

#

or have I misunderstood the question

rustic gull
#

That will accept Foo as well, which I don't want

hasty hull
#

ah I see

rustic gull
#

This is what I was doing anyway so that's fine

#

Just wanted to make sure there wasn't a nicer way of doing it

soft matrix
#

soon™️ type[Foo] & ~Foo

#

im actually writing a email to typing sig about a not type

#

but that should hopefully work at some point in the future

rustic gull
#

I don't like it, looks ugly

soft matrix
#

wait ignore me

#

that doesnt work at all

#

lol

oblique urchin
soft matrix
#

i can make it work

#

but not like that

oblique urchin
#

The semantics of & should be that the type matches all of the operands

#

I guess this would require adding __invert__ to type objects at runtime which may be a hard sell

#

Oh wait this doesn't work because ~Foo would mean "not an instance of Foo"

soft matrix
#

its only the same stuff as __or__

oblique urchin
#

So we'd also need a construct for "exactly the Foo class object"

fierce ridge
oblique urchin
trim tangle
#

Why?

soft matrix
#
class InstanceOf(Protocol[T_co]):
    __class__: T_co

~Foo & InstanceOf[Foo]
```i think this works
hasty hull
#

You could do some fuckery with protocols: ```py
from typing import Literal, Protocol, Type

class FooProtocol(Protocol):
subclassed: Literal[True]

class Foo:
pass

class FooSubclass(Foo):
subclassed: Literal[True] = True

class Bar(FooSubclass):
pass

def func(foo: Type[FooProtocol]) -> None:
...

func(Foo) # errors
func(Bar) # no error

rustic gull
soft matrix
#

why are you doing this in the first place?

rustic gull
# trim tangle Why?

The method that takes the subclass as an argument wouldn't work on the superclass

trim tangle
#

But why?

rustic gull
#

Because the subclasses all define an attribute that the method accesses, but the superclass doesn't

trim tangle
#

Maybe you need an abstract class then?

rustic gull
#

So it would break if it accepted the superclass

rustic gull
#

How would that help?

pastel egret
#

Means you can't instantiate the superclass.

trim tangle
#

I don't think it would :)

trim tangle
rustic gull
#

Yeah

pastel egret
#

Ah yeah.

rustic gull
#

If it helps, the specific case I have is I'm making a wrapper for an API, and the superclass represents a resource that the API provides (because the resources have some shared functionality), and each subclass is the model for a particular resource. Each subclass defines a PATH_SEGMENT for the resource URL, but the superclass (which doesn't represent a particular resource) doesn't. Should I refactor this? It does sound like I should be using an abstract base class instead.

bright dome
#

We just need a "hybrid" type system. You want static OK "@static-typing😋

#

Let me be quiet lol

pastel egret
rustic gull
#

No, the superclass won't be instantiated. At the moment I have PATH_SEGMENT: str in the superclass - what's the difference with ClassVar[str]?

pastel egret
#

ClassVar indicates it should be a class variable, not an instance variable.

rustic gull
#

Ah cool

#

I'll change it to an ABC and str to ClassVar[str] then

soft matrix
#

how long before they realise class variables arent really class variables in python?

blazing nest
#

I want to make sure I am not speaking out of my ass - technically .pyi files aren't constrained to a specific Python version yeah?

little hare
#

i don't think so don't quote me

soft matrix
#

you can use whatever syntax you want

pastel egret
#

They're not no.

soft matrix
#

some imports might not work

blazing nest
#

Yeah, what I mean is that even if the lowest Python requirement is for example Python 3.8 I can use list[str] and str | None inside .pyi files for example

pastel egret
soft matrix
#

yeah that works in pyis

pastel egret
#

You can use those indeed, also forward referencing, import cycles and the like aren't an issue.

soft matrix
# little hare p-pardon?

!e ```py
from typing import ClassVar
class Foo:
bar: ClassVar = 1 # ah yes this is totally a class only variable
print(Foo().bar) # whoops

rough sluiceBOT
#

@soft matrix :white_check_mark: Your eval job has completed with return code 0.

1
blazing nest
pastel egret
#

It's a class variable because it's stored in the class dictionary.

#

But accessing things on an instance falls back to the class, that's just how it works?

soft matrix
#

!e ```py
from typing import ClassVar
class Foo:
...
print(Foo().name) # this doesn't work

rough sluiceBOT
#

@soft matrix :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 4, in <module>
003 | AttributeError: 'Foo' object has no attribute '__name__'. Did you mean: '__ne__'?
soft matrix
#

name is in the class dictionary

#

!e ```py
class Foo: ...
print(Foo.dict) # this doesn't work

rough sluiceBOT
#

@soft matrix :white_check_mark: Your eval job has completed with return code 0.

{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
soft matrix
#

oh maybe not

#

well ignore me

pastel egret
#

The name, dict and weakref aren't in the dict, they're pointers in the class structure.

soft matrix
#

anyway when i think of a class var i think of something like name where accessing it doesnt work on the instance

#

what are those called?

#

metaclass properties?

pastel egret
#

That's more of a special case.

#

They're metaclass properties indeed:

#

!e

print(list(type.__dict__))
rough sluiceBOT
#

@pastel egret :white_check_mark: Your eval job has completed with return code 0.

['__new__', '__repr__', '__call__', '__getattribute__', '__setattr__', '__delattr__', '__init__', '__or__', '__ror__', 'mro', '__subclasses__', '__prepare__', '__instancecheck__', '__subclasscheck__', '__dir__', '__sizeof__', '__basicsize__', '__itemsize__', '__flags__', '__weakrefoffset__', '__base__', '__dictoffset__', '__mro__', '__name__', '__qualname__', '__bases__', '__module__', '__abstractmethods__', '__dict__', '__doc__', '__text_signature__', '__annotations__']
pastel egret
#

__name__'s in there, as a <member>.

little hare
#

wait if you type a dict to a list it only does the keys?

pastel egret
#

Iterating over a dict gives keys.

little hare
#

!e print(list({'a':'b'}))

rough sluiceBOT
#

@little hare :white_check_mark: Your eval job has completed with return code 0.

['a']
pastel egret
#

I believe there was a very fierce debate over how it iterate.

little hare
#

sounds fun

pastel egret
raw valve
#

There is currently no way of annotating for an argument to be a bound method that takes zero arguments is there?

#

I'm currently annotating with. types.MethodType but I would like to indicate that the method should have zero arguments

#

This is for annotating a descriptor method, which type checkers probably can't assess anyway but I like it for documentation

soft matrix
#

just make your own version of it that implements dunder call with no args

#
@final
class MethodType(Protocol):
    __defaults__: Tuple[Any, ...] | None  # inherited from the added function
    __func__: Callable
    __self__: object
    __name__: str  # inherited from the added function
    __qualname__: str  # inherited from the added function
    def __init__(self, func: Callable[..., Any], obj: object) -> None: ...
    def __call__(self) -> Any: ...
rustic gull
#
from typing import List
from math import inf

def maxProfit(self, prices: List[int]) -> int:
    buy, profit = inf, 0
    for price in prices:
        buy = min(buy, price)
        profit = max(profit, price - buy) # Incompatible types in assignment (expression has type "float", variable has type "int")
    return profit

profit will never actually be assigned the inf float (0 > -inf), I'm not sure how to tell mypy this

soft matrix
#

just make profit a float

#

int and float are compatible bar like 2 methods or something

pastel egret
#

You could also start buy = prices[0].

rustic gull
raw valve
rough sluiceBOT
#

descriptors.py line 87

def register(method: MethodType) -> None:```
cyan patio
#

:|

soft matrix
#

wheres the code for this?

#

i can think of three options here

  1. unpack the tuple yourself again so mypy can infer its tuple[int, int]
  2. typing.cast it
  3. type: ignore
#

idk why you need to cast marker to a tuple twice but yeah that seems alright

#

typing.cast just tells the type checker that it should ignore what it knows about the type of something, you know what type it is

#

!d typing.cast

rough sluiceBOT
#

typing.cast(typ, val)```
Cast a value to a type.

This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).
soft matrix
#

you need to import typing and then do typing.cast(tuple[int, int], tuple(marker))

#

i feel like you should go with the destructuring option

#

x, y = marker

indigo locust
#

Hey, I could use a little explanation of 'bound' for a type var'

#

Still getting my bearings with it

#
from typing import TYPE_CHECKING
from typing import Sequence
from typing import TypeVar

if TYPE_CHECKING:
    from positron.ObjectModel.Bases.Node import Node
    
T_NODETYPE = TypeVar('T_NODETYPE', bound=Node)

class Children(Sequence[Node]):
    pass

class Subset(Sequence[T_NODETYPE]):
    pass
#

I have these two classes here. Children is a readonly container type, instances of which are attached to every object of type Node. It contains that nodes children

#

Specifically, it contains that node's children in sequential order

#

The Subset object is a view of the Children object. Every Node object has three such Subset objects attached to it — which display those children of the parent of a specific type (Either 'Element', 'TextNode', or 'CommentNode')

#

As such, Subset is a sequence of Nodes also, but of a specific type of node each. As such, the typevar T_NODETYPE is used. It represents a generic sub-category of Node.

#

Do I bind it to Node, or, to each possibly subtype as such bound='Element | TextNode | CommentNode'

trim tangle
#

@indigo locust If you don't have bound, any type could be used in place of the type variable. If you do use bound, you specify which supertype the assigned type has to be a subtype of. For example:

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

def create_dict(k: K, v: V) -> dict[K, V]:
    return {k: v}
#

In your case, you should bind it to Node.

indigo locust
#

Now, is there a way to bind from the other direction also?

#

As it is, bound says "this type or one of its descendants"

#

NVM

#

There would be no point. If I wanted to be that specific, I'd just specify that exact type

trim tangle
#

@indigo locust the type Node already means "Node or a subclass of Node" 🙂

#

There's no way to say "type Node, but not its descendant". That would break LSP

indigo locust
#

That said, there is another type of Node which can't be a child

trim tangle
indigo locust
#

Document and Fragment are Node types, but they are roots. Now, its no biggie if I'm not 100% precise

trim tangle
#

You could make a ChildNode class if you want to be precise 🙂

indigo locust
#

Blegghghghghghg

#

Nooooooooope

#

This object model is convoluted enough, thank you XD

trim tangle
#

or a type alias storing a union

indigo locust
#

I guess the only way to be 100% specific would be to create a dedicated subset class for each type

blazing nest
trim tangle
#

an invariant typevar just means that YourClass[A] is not in any subtype relation with YourClass[B] even if there is a relation between A and B

#

(unless A and B are equal, of course)

blazing nest
#

Ah

indigo locust
#

What happens if I bind to a literal?

trim tangle
indigo locust
#

So, here's what I want...

#
    def firstChildOfType(self, base: Type[Element] | Literal[CommentNode, TextNode]):
        ...
soft matrix
#

what does a Literal[CommentNode] mean?

indigo locust
#

The purpose of the function should be pretty obvious. I'd like to be able to feed it either:
The class Element, any subclass of Element, or any instance of Element —> return either an instance of element or Undefined
The class CommentNode or an instance of CommentNode —> return either an instance of CommentNode or Undefined
The class TextNode or an instance of TextNode —> return either an instance of TextNode or Undefined

soft matrix
#

you shouldnt be using literal here

indigo locust
#

Yeah 😐 That was a shot in the dark but its not right

#

Maybe an overload?

soft matrix
#

you dont need to

#

oh you do

#

but you dont need 3

#

you can just do it in two

indigo locust
#
    @overload
    def firstChildOfType(self, selector: Element | Literal[0]) -> Element | Undefined:
        ...
    
    @overload
    def firstChildOfType(self, selector: CommentNode | Literal[1]) -> CommentNode | Undefined:
        ...
    
    @overload
    def firstChildOfType(self, selector: TextNode | Literal[0]) -> TextNode | Undefined:
        ...
    
    def firstChildOfType(self, selector):
        ...
#

So this first overload selects either the class Element, a subclass of Element, an instance of Element, or the number 0 (its enumeration)

#

Right?

soft matrix
#

i need to check if you can use a constrained type var with Type

#
E_Types = TypeVar("E_Types", Element, CommentNode, TextNode)


def firstChildOfType(self, base: type[E_Types] | E_Types) -> E_Types | None:
    ...```this i think does what you wanted originally
indigo locust
#

Oooooooo

#

But, doesn't grouping Element, CommentNode, and TextNode like that mean that the input type could be one of those, and the return type could be one of those, but the input and return types are not tied to one another?

#

Or does it specify that E_Types may be one of these types, and whatever that type is will be the return type

trim tangle
#

it's just that it can be assigned to either of the types

#
from typing import TypeVar

AnyStr = TypeVar("AnyStr", str, bytes)

def double(s: AnyStr) -> AnyStr:
    return s + s

a = double("foo")  # a: str
b = double(b"foo")  # b: bytes
indigo locust
#

Most excellent

#

I'm gonna ask my teachers, when the next term starts, which class does type theory

#

Now, I've decided to go with the overload route for now, just because its more explicit

#
    @overload
    def firstChildOfType(self, selector: Element | Literal[0]) -> Element | Undefined:
        ...

    @overload
    def firstChildOfType(self, selector: CommentNode | Literal[1]) -> CommentNode | Undefined:
        ...

    @overload
    def firstChildOfType(self, selector: TextNode | Literal[0]) -> TextNode | Undefined:
        ...

    def firstChildOfType(self, selector: Node | Literal[0, 1, 2]) -> Node | Undefined:

        if issubclass(selector, Node) or isinstance(selector, Node):
            selector = selector.enumeration()

        selectifier = lambda child : child.enumeration() == selector
        selection   = tuple(filter(selectifier, self.childNodes))
        
        return selection[0] if selection else undefined
#

Just wanted to get your thoughts. Beyond that, can I get a quick reminder on how overloads work with stub files? I'm supposed to remove the overloads completely from the implementation and just leave the actual function. Then I put the overloads in the type stub, and then do I also put an unoverloaded stub of the actual function? Or do I omit that?

soft matrix
#

dont use stubs unless you have to

#

they just make things more annoying as you separate everything from itself

indigo locust
#

I have to — this is all coded in cython

soft matrix
#

but if you are in a stub for overloads

@overload
def something(x: int) -> bool: ...
@overload
def something(x: float) -> str: ...
#

you dont need anything beyond that

#

no un-decorated something function

indigo locust
#

So, no overloads in the implementation and no function-actual in the stub

soft matrix
#

well you can use overloads in the implementation, which is what id recommend but yeah

indigo locust
#

Rockin 🙂 Many thanks

hearty shell
#

Is anyone aware of a gql library that takes in a query string + schema and returns a typeddict based on that query? Is that even something that would work well with type checkers?

#

If it doesn't exist I am thinking about implementing it, at it's surface it doesn't seem hard, just dynamically create typeddicts by parsing the query and looking at the schema, however I have tried dynamic class creating with mypy and it usually doesn't like that

brisk hedge
#

How would you expect a type checker to validate against a dynamically created typeddict?

#

I'm not sure it's possible unless you do this as a "preprocessing step"

hearty shell
#

Humm I should test a minimal example but it doesnt seem far fetched, given a query

some_query(...) {
    data {
        id
        name
    }
}

could I not get a function to read that and somehow annotate it that it would return

some_query = TypedDict('some_query', {'id': int, 'name': str})
some_query_data = TypedDict('some_query_data', {'data': list[some_query ]})
#

after checking with the graph schema for types

trim tangle
#

@hearty shell You mean, you want to generate code for the type dicts?

hearty shell
#

Yes

trim tangle
hearty shell
#

Humm, I will test around with graphql-py, thanks!

trim tangle
#

Hey, pyright just got functools.partial supported hacked in

soft matrix
#

if only it could actually be typed lemon_angrysad

trim tangle
bright dome
olive oar
#

I have the following TS code, what's the Python way of doing it? Specifically looking for compile time type safety.

type Prop = 'width' | 'height' | 'area'

type Data<T extends Prop> = {
    prop: T
    value: number
}

const foo: Data<'width'> = { prop: 'width', value: 5 } // ok
const bar: Data<'height'> = { prop: 'width', value: 5 } // error
const baz: Data<'area'> = foo // error
trim tangle
#

generic TypedDicts are not allowed, unfortunately.

olive oar
trim tangle
#

@olive oar That's because "width" is not a valid type. Literal["width"] is

olive oar
#

Oh so you need to do Data[Literal[...]]

trim tangle
#

yep

#

if you're learning Python's gradual typing coming from TS, you will be disappointed pretty often 🙂

#

Well, "width" is a valid type. It's a forward reference to a type called width

olive oar
#

That sets my expectation straight, but I wouldn't fault any language for not being able to do a lot of TS stuffs to begin with, some of those feel like black magic territory.

#

Is there a guide somewhere I can reference?

trim tangle
#

I am procrastinating on making a somewhat compete guide lol

upbeat wadi
#

The pins of this channel have some resources that should help

trim tangle
#

so there's no equivalent of "TypeScript handbook"

#

Another issue is that the type checkers are somewhat incompatible between each other, even though they use the typing PEPs as reference.
So you need to put in extra effort if you want to make sure that the code works on more than one type checker

olive oar
#

Oh huh

#

Does type hinting get erased in runtime like TS?

trim tangle
rough sluiceBOT
#

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

{'x': <class 'int'>, 'return': <class 'str'>}
trim tangle
#

but they don't actually do anything beyond that

olive oar
#

Ah, so it's like metadata.

trim tangle
#

yep

#

There are libraries like dataclass_factory that do some cool stuff with it

#

!pypi dataclass_factory

rough sluiceBOT
trim tangle
#

TL;DR it automatically serializes/deserializes a dataclass to/from a JSON-like object based on its type hints

olive oar
#

Oh, that seems like something my library can use.

#

Back to the point of type checkers behave differently, is that a big issue?

#

Currently I'm in the process of porting some dev tooling over from TS to Python, and if say my library only works for this type checker, wouldn't that be pretty bad?

trim tangle
#

This works with pyright though.

#

It's not that big of a deal. But I would run some example problems through both mypy and pyright

#

mypy is widely used in CI because it's the "default" type checker (and it supports plugins), and pyright powers type-checking in VSCode

#

PyCharm has its own, completely separate type checker

olive oar
#

😬

#

Well I guess if it's not a huge issue then I should be good then, my libraries don't use much type magics.

#

Do I need an extension for type checking in VS Code, or is it just built in?

trim tangle
#

If you want mypy, you can go to settings, search for mypy and there's going to be an option to enable it (in the Python extension)

#

or you can use the Pylance extension, which is based on pyright

indigo locust
#

Question

#

Lets say I have a custom mapping class which also inherits from a base primitive

olive oar
#

Which one is the most widely adopted?

trim tangle
olive oar
#

Thanks, I'll go with that then.

indigo locust
#
class MyMap(Mapping[KT, VT], Primitive):
  ...
```Is this how I'm supposed to type it?
olive oar
#

Ngl I've just been checking out Python stuffs in preparation, and I already feel quite lost 😂

indigo locust
#

Just give it a few weeks :3

olive oar
#

I'll reserve my judgment for sure, but I'm also not one to refuse to work with a language just because I dislike it.

#

I guess so far I just feel like the ecosystem is a bit fractured, but it could also just be coincidence.

trim tangle
#

maybe like flow vs typescript

blazing nest
trim tangle
#

But I think most production systems doing type-checking use mypy, at least because it has plugins

indigo locust
#

And I have high hopes for it

#

Oh

#

XD You should gives the docs a read

#

This guy has no business coding — he should be writing for SNL XD XD XD

#

There are good reasons to believe that O(1) type-checking is preferable. Violating that core precept exposes your codebase to scalability and security concerns. But you're the Big Boss, you swear you know best, and (in any case) we can't stop you because we already let the unneutered tomcat out of his trash bin by publishing this API into the badlands of PyPI.

trim tangle
#

it's not really a competitor to mypy, is it?

indigo locust
#

My apologies if I implied it did

#

Its a fascinating project which, like I said, I have high hopes for

#

I just thought I'd mention it is all

trim tangle
#

ah

olive oar
#

Is it a good choice to base my library on 3.9+, or does it alienate too many users?

soft matrix
#

you can use lowercase classes with python 3.7+

#

you just need to import future annotations

brisk hedge
#

hm, I realized callable syntax wouldn't be possible in lower versions even with future annotations

#

because annotations are expressions in the grammar

soft matrix
#

yep you need to quote them

upbeat wadi
#

Pylance has been giving me this error recently (Type "BaseApplicationCommandResponse" is already specialized) though it usually goes away if I delete the line BaseApplicationCommandResponse is defined on, and then undo the deletion (the pyright CLI also doesn't detect any errors in the file). This seems like a bug, but what does "type is already specialized" mean?

hasty hull
#

e.g. ```py
class Foo(Generic[T]):
pass

FooInt = Foo[int]
FooStr = FooInt[str] # error

upbeat wadi
#

Ah thanks

olive oar
#

After the frustrating experience with mypy, I tried Pyright and it works right out of the box

#

I guess that's what I'll be using instead of dealing with whatever mypy issues I was having.

twilit badge
#

How should I type hint a function which takes a class and a method of that same class: ```py
class Foo:
def x():
...
def y():
...

def bar(klass: ?, method: ?):
...

bar(Foo, Foo.y)

#

no, I want bar function to take any type of class and the method parameter doesn't take the result of that function but the function itself

#

Foo class was just an example, klass should probably be annotated as object, but I'm not sure how would I signify that method is a boundmethod belonging to that class

#

I will probably want to use typevars here, but I'm not sure how would I annotate a method of a given class

#
from typing import TypeVar

T_o = TypeVar("T_o", bound=object)

def bar(klass: T_o, method: method_of[T_o]):  # I want to know how to actually annotate method of the T_o class
    ...
soft matrix
#

Because in python 3 there is no way to determine where an inbound method comes from I don't think this is possible

twilit badge
#

oh, that's a shame, so I suppose just doing this would be the best way? ```py
def barr(klass: object, method: typing.Callable):
...

boreal ingot
twilit badge
#

nothing, I was just wondering if type-hinting something like this was possible, it's purely educational question

boreal ingot
#

ah I see, I think the closest you can come is requiring method to take T_o as its first argument

twilit badge
#

hmm, but if I'm specifying arguments, doesn't callable just take a list of them, how would I signify that T_o is first and the rest is unknown?

boreal ingot
# twilit badge hmm, but if I'm specifying arguments, doesn't callable just take a list of them,...

you can use a combination of Concatenate and ParamSpec ```py
from typing import TypeVar, Callable, Any, ParamSpec, Concatenate

T_o = TypeVar("T_o", bound=object)
P = ParamSpec("P")

class Foo:
def x(self, a: int, b: str) -> int:
...
def y(self, k: int) -> str:
...

def bar(klass: type[T_o], method: Callable[Concatenate[T_o, P], Any]):
...

def not_actually_a_method(abc: Foo) -> None:
...

bar(Foo, Foo.y) # ok
bar(Foo, abs) # error
bar(Foo, not_actually_a_method) # ok```

#

it is not perfect, as for example not_actually_a_method gets accepted as it takes Foo as a first argument, but it is closer

#

the problem is that there is nothing about x and y themself that make them specifically methods for Foo

pastel egret
#

Not actually a method is just as valid a callable as the ones on the class.

boreal ingot
#

!e ```py
class Foo:
def x(self):
print(self)

print(f"{type(Foo.x)=}")
print(f"{type(Foo().x)=}")```

rough sluiceBOT
#

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

001 | type(Foo.x)=<class 'function'>
002 | type(Foo().x)=<class 'method'>
boreal ingot
#

they are only actually methods when they are tied to an instance ^

little hare
#

How can i implement a generic or something.... I have no idea what I'm doing, all I know is i want to say a variable can have a max of x and min of x, and it'll be either an int or a float

#

The end goal is to have something like Range[0,5,int] & not have a typechecker complain

soft matrix
#

Having the first two args be in anyway meaningful isn't currently possible

#

And you probably need a constrained type var for the last arg

trim tangle
soft matrix
#

you could probably do something with Literal[int] TypeVars with pep 675

little hare
#

!pep 675

rough sluiceBOT
#
**PEP 675 - Arbitrary Literal Strings**
Status

Draft

Python-Version

3.11

Created

30-Nov-2021

Type

Standards Track

soft matrix
#

but again theres no point

#

oh this only applies to strings

#

thats dumb

trim tangle
#

typing seems to move in the direction of a set of very specific hacks

little hare
#

I've never written a custom typehint or something, so not sure where i'd start

soft matrix
#

class Range(Generic[MinT, MaxT, T]):...

trim tangle
little hare
#

no

soft matrix
#

oh

trim tangle
#

however, if you want to have a type that constraints an integer between 0 and 5, you can't really do that

little hare
#

!d typing.Generic

rough sluiceBOT
#

class typing.Generic```
Abstract base class for generic types.

A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as:

```py
class Mapping(Generic[KT, VT]):
    def __getitem__(self, key: KT) -> VT:
        ...
        # Etc.
```  This class can then be used as follows...
little hare
trim tangle
#

I guess

#

that's what Pydantic does

little hare
#

that's the main purpose of this

trim tangle
#

well, it will break in 3.11 😉

little hare
#

to validate it elsewhere, and have good typechecking

trim tangle
#

(probably)

little hare
#

why

trim tangle
#

from __future__ import annotations will become default IIRC

little hare
#

but typing.get_type_hints doesn't help that?

trim tangle
little hare
#

🤦‍♀️

#

how do you get those then!

trim tangle
#

oh wait, there's a flag for it

#

hmm, I don't get how this works:

#

!e

from __future__ import annotations
from typing import Annotated, get_type_hints

class Foo:
    bar = [1, 2, 3]
    baz: Annotated[int, {"allowed_values": bar}]

print(get_type_hints(Foo, include_extras=True))
rough sluiceBOT
#

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

{'baz': typing.Annotated[int, {'allowed_values': [1, 2, 3]}]}
trim tangle
#

how does it resolve bar??

soft matrix
soft matrix
#

typing isnt doing anything weird

trim tangle
rough sluiceBOT
#

Lib/typing.py line 1802

base_locals = dict(vars(base)) if localns is None else localns```
soft matrix
#

this

#

vars(base) is gonna be Foo.dict

trim tangle
#

oh wait, it just special-cases this

#

right

#

yikes

soft matrix
#

idk if its really a special case

#

thats more just how class namespaces work right?

trim tangle
#

I meant more like the whole stringly annotation thing is a hack

rustic gull
#

I can do Type[MyClass] to type hint the class itself, rather than an instance of it. But if I have a TypeVar...

T = TypeVar("T", MyClass, MyOtherClass)

Can I similarly do Type[T] to indicate that the classes MyClass and MyOtherClass themselves are allowed? Or does that not work with TypeVars?

soft matrix
soft matrix
rustic gull
#

Thanks

soft matrix
#

it means type[MyClass] and type[MyOtherClass]

rustic gull
#

This might be a limitation of type hinting and/or VSC, but I can't get the right type hint on my class:
base.py

_OC = TypeVar("_OC", Anime, Episode, Song, User, UserStory)


class ObjectHelperBase(HelperBase, Generic[_OC]):

    @abc.abstractmethod
    def __init__(self, aniapi: "AniAPI", object_class: Type[_OC]) -> None:
        super().__init__(aniapi)
        self._object_class = object_class

    def __call__(self, id_: int) -> _OC:
        return self._object_class(self._aniapi, id_)

anime.py

class AnimeHelper(ObjectHelperBase):

    def __init__(self, aniapi: "AniAPI") -> None:
        super().__init__(aniapi, Anime)

api.py

class AniAPI:

    def __init__(self) -> None:
        self._anime = AnimeHelper(self)

test.py

api = AniAPI()
anime = api.anime(1) # VSC shows type of "anime" as "Any" instead of "Anime"

I thought by using the generic _OC it would pick up on Anime as the return type of __call__(), but that hasn't worked. Is this not possible?

soft matrix
#

whats API?

#

its not in any of those snippets

rustic gull
#

Sorry meant to be AniAPI

#

I've also omitted some unnecessary details

soft matrix
#

also not sure where the anime method is defined?

#

is it a dynamically typed thing?

#

or is it a proxy for self._anime

rustic gull
#

This is related to my question yesterday: #software-architecture message

I'm trying to avoid overriding ObjectHelperBase.__call__() in AnimeHelper just for the Anime type hint

rustic gull
#

Basically I want the return of AnimeHelper.__call__() to be type hinted as Anime without having to override the method with a different type hint, i.e. I want to parameterise the type hint for the return value of ObjectHelperBase.__call__() based on the object_class passed to it.

#

At the moment my editor just sees the return of AnimeHelper.__call__() as Any, I guess because it isn't smart enough to trace _OC through all the methods.

soft matrix
#

yeah you need to change

class AnimeHelper(ObjectHelperBase):
# to 
class AnimeHelper(ObjectHelperBase[Anime]):
#

and then it should work

rustic gull
#

Is that really all I needed to do lol

#

Yeah that worked lmao

#

Thanks

#

Oh that's so much easier

rustic gull
#

What type hint should I use for a container that supports + concatenation, e.g. list?

trim tangle
rustic gull
void panther
trim tangle
#

and int is not a Sequence

void panther
#

If it also needs to be a container/iterable/something then I think you can inherit from the abc in the protocol class

rustic gull
#

I think I'll just type hint Iterable and convert it to list

rustic gull
#

    def __init__(self, ctx: commands.Context,style=Union[discord.ButtonStyle.danger, discord.ButtonStyle.gray]) -> None:
 ``` is the style type hinting correct ![thinkCat](https://cdn.discordapp.com/emojis/882473068881145899.webp?size=128 "thinkCat")
soft matrix
#

you probably should be using Literal around ButtonStyle.danger cause its an enum member right?

rustic gull
#

also i made a small change ,


    def __init__(self, ctx: commands.Context,style : Union[discord.ButtonStyle.danger, discord.ButtonStyle.gray] =discord.ButtonStyle.danger ) -> None:``` what are enums ?
soft matrix
#

!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
#

in discord.py they dont actually use this anymore but its docs are still useful

rustic gull
#

i'm confused on what enums got to do with this. and yes the discord.ButtonStyle are constant , they are methods in discord.ButtonStyle class

rustic gull
#

woah, i need to learn about enums

rough sluiceBOT
#

discord/enums.py lines 161 to 165

if TYPE_CHECKING:
    from enum import Enum
else:

    class Enum(metaclass=EnumMeta):```
little hare
#

ah

soft matrix
#

shhhh

rustic gull
#

metaclasses sweating

soft matrix
#

dont worry about it

#

just know that if you are type hinting something to be certain enum members you need to use Literal[EnumName.member1, EnumName.member2]

rustic gull
#

oh okay

soft matrix
#

if you want any member just use x: EnumName

rustic gull
#

like wise or ButtonStyle.1 ,ButtonStyle.4 ?

soft matrix
#

.1 or .4 isnt a valid attribute name in python

#

but that is correct now

rustic gull
#

thx

rustic gull
soft matrix
#

Literal is a special form that takes literal types (str, int, bytes, NoneType, Enum) and if you try and type check something against a literal it will fail unless it is in the Literal's arguments

x: Literal[0, 1] = 0  # fine
x = 1  # also fine
x = 2  # error
#

!d typing.Literal

rough sluiceBOT
#

typing.Literal```
A type that can be used to indicate to type checkers that the corresponding variable or function parameter has a value equivalent to the provided literal (or one of several literals). For example:

```py
def validate_simple(data: Any) -> Literal[True]:  # always returns True
    ...

MODE = Literal['r', 'rb', 'w', 'wb']
def open_helper(file: str, mode: MODE) -> str:
    ...

open_helper('/some/path', 'r')  # Passes type check
open_helper('/other/path', 'typo')  # Error in type checker
```...
soft matrix
#

the docs might be helpful to read aswell

rustic gull
#

ohh

#

got it

#

but its causing some error

#

class Delete_button(Button):
    """
    Some discord button that deletes the author and the bots message if everything goes smoothly
    Arguments
    
    ctx : discord.Context should be passed
    class discord.ButtonStyle
    or discord.ButtonStyle.danger, discord.ButtonStyle.gray
    ----
    """

    def __init__(self, ctx: commands.Context,style : Literal[discord.ButtonStyle.danger, discord.ButtonStyle.gray] = discord.ButtonStyle.danger ) -> None:
        self.ctx = ctx
        self.style = style 
        super().__init__(
            style=style,
            emoji=f"{random.choice(Emojis.trash_emojis)}",
        )

    async def callback(self, interaction) -> Coroutine:
        """
        A discord.ui.button callback function returns a coroutine
        deletes the bot message and the authors message
        -----
        when the if statment fails
        it sends to the user a random error_reply with a random "pepe" sad emoji
        ephemeral=True so only the user can sees it'
        """
        if interaction.user.id == self.ctx.author.id:
            # checking if the author id meets the user interaction id
            await self.ctx.message.delete()
            # deleting the author message
            return await interaction.message.delete()
            # and finally deleting the interaction message which is the bots message
        return await interaction.send_message(
            f"{random.choice(Replies.error_replies)} {random.choice(Emojis.pepe_sad_emojis)}",
            ephemeral=True,
        )
``` my brain confused ![thinkCat](https://cdn.discordapp.com/emojis/882473068881145899.webp?size=128 "thinkCat")
soft matrix
#

try #discord-bots cause this isnt type hinting related (also idk anything about how buttons actually work)

faint dust
#

Anyone know if mypy dropped support for pyproject.toml exclude syntax:

[tool.mypy]
python_version = "3.10"
exclude = ["tests/", "test.py"]
spare mauve
#

is it just a flat regex? or did the syntax you're doing work previously?

spare mauve
faint dust
spare mauve
faint dust
rustic gull
#
from __future__ import annotations

from typing import Union

FooBar = Union[Foo, Bar] # name 'Foo' is not defined

class Foo:
    def __init__(self, baz: FooBar) -> None:
        self.bar = baz

class Bar:
    def __init__(self, baz: FooBar) -> None:
        self.bar = baz

does putting FooBar = Union[Foo, Bar] above the class definitions of Foo and Bar not work for 3.8? i get a runtime error locally with python 3.8.10, I noticed this worked on mypy playground with 3.8
https://mypy-play.net/?mypy=latest&python=3.8&gist=c1d1c45b69bdb862fd8abe6a3df78d26

grave fjord
rustic gull
soft matrix
#

use typing_extensions

indigo locust
#

I'm a bit confused about typevars

#

T = TypeVar('T')

class Thing(Generic[T]):

  def __init__(self, attribute: T) -> None:
    ...

  def __call__(self, *args, **kwargs) -> T:
    ...
#

How do I 'define' T? Do I not have to declare something like this in order to actually fill T with with a value?

#
thing : Thing[int] = Thing()
soft matrix
#

T can be inferred

#

Thing(1234) has type Thing[Literal[1234]] (if youre using pyright)

indigo locust
#

Hmmmm

#

Okay, one sec

#
from typing import Any
from typing import Optional
from typing import TypeVar
from typing import Type

T_Typehint = TypeVar('T_Typehint')

class Attribute(Generic[T_Typehint]):

    def __init__(self, type: Type, 
            typehint: Optional[T_Typehint] = None, privacy: str = 'public', scope: str = 'instance',
    ) -> None:

        self.typehint = typehint or type
        self.privacy  = privacy
        self.scope    = scope
        self.type     = type

    def __set_name__(self, prototype: Type[CyObject], name: str) -> None:

        self.prototype = prototype
        self.name = name

    def __get__(self, instance: None | CyObject, prototype: Type[CyObject]) -> T_Typehint:
        ...
#

Here's where I'm at. I'm writing this descriptor, a property basically

#

Unless I decided to use Annotated, type and typehint need to be different, as the former is called to actually fill the initial value and the latter is used for typing purposes and may be generic

soft matrix
#

idu why you need to use Annotated here

indigo locust
#

I need the return value of __get__ to be the typehint, unless that typehint is None at which case it needs to be the type

soft matrix
#

ok so you need Attribute to be generic over type and type hint then add overloads

#

so```py
@overload
def get(self: Attribute[None, T2], *rest) -> T2: ...
@overload
def get(self: Attribute[T, Any], *rest) -> T: ...

#

except correct

twilit badge
#

How should I annotate a contextmanager function's return value?

from contextlib import contextmanager

@contextmanager
def foo() -> ?:
    yield 5
soft matrix
#

!d collections.abc.Generator

rough sluiceBOT
#

class collections.abc.Generator```
ABC for generator classes that implement the protocol defined in [**PEP 342**](https://www.python.org/dev/peps/pep-0342) that extends iterators with the [`send()`](https://docs.python.org/3/reference/expressions.html#generator.send "generator.send"), [`throw()`](https://docs.python.org/3/reference/expressions.html#generator.throw "generator.throw") and [`close()`](https://docs.python.org/3/reference/expressions.html#generator.close "generator.close") methods. See also the definition of [generator](https://docs.python.org/3/glossary.html#term-generator).

New in version 3.5.
indigo locust
twilit badge
#

isn't there something like typing.ContextManager?

soft matrix
twilit badge
#

or perhaps I could make my own protocol

#

why not?

soft matrix
#

cause thats just not how it works?

#

the contextmanger decorator uses generators to do its magic

#

not more contextmanagers

#

cause under the hood it calls foo

twilit badge
#

ohh, yeah that makes sense, I wanted to annotate the return value after the decoration but yeah, that makes no sense now that I think about it

#

thanks!

void panther
#

can also just use Iterator

soft matrix
#

if you want to type hint something that can be used as a context manager thats when you use collections.AbstractContextManager or whatever its called

indigo locust
#
from typing import Any
from typing import Generic
from typing import Optional
from typing import TypeVar
from typing import Type
from typing import overload


T_Type     = TypeVar('T_Type', bound=Type)
T_Typehint = TypeVar('T_Typehint', bound=None | Type)

class Attribute(Generic[T_Type, T_Typehint]):
    
    def __init__(self, type: T_Type, typehint: Optional[T_Typehint] = None) -> None:
        ...
        
    @overload
    def __call__(self: Attribute[T_Type, Type], *positional, **encyclopedic) -> T_Typehint:
        ...
    
    @overload
    def __call__(self: Attribute[T_Type, None], *positional, **encyclopedic) -> T_Type:
        ...
        
    def __call__(self, *positional, **encyclopedic):
        ...
        
        
class Thing1():
    ...
    
class Thing2():
    ...
        
reveal_type(Attribute(Thing1)())
#

Not working 😐 (checked on mypy)

#
reveal_type(Attribute(Thing1)())
#
reveal_type(Attribute(Thing1, Thing2)())
#

main.py:35: note: Revealed type is "def () -> main.Thing2"

soft matrix
#
from typing import Any, Generic, TypeVar, overload

T_Type     = TypeVar('T_Type', bound=type[Any], covariant=True)
T_Typehint = TypeVar('T_Typehint', None, type[Any], covariant=True)

class Attribute(Generic[T_Type, T_Typehint]):
    def __init__(self, type: T_Type, typehint: T_Typehint = None) -> None: ...
    @overload
    def __call__(self: Attribute[T_Type, None]) -> T_Type: ...
    @overload
    def __call__(self: Attribute[T_Type, T_Typehint]) -> T_Typehint: ...
    def __call__(self) -> T_Type | T_Typehint: ...
```this works for me
indigo locust
#

Hmmmm

#

It might just be an error with mypy playground

#

But its not working for me — its only returning the first type

#

Wether a typehint is provided or not

rustic gull
#

sorry another question, is there not a -q/--quiet flag on mypy to suppress the success message when there's no error?

indigo locust
#

So I think I've decided what I'm going to do. The purpose of the first argument (type) is to initialize the value when the instance is created, while the purposes of the second is the typehint (for enforcement purposes). But automatically initializing members is a bit of a rabbit hole in its own right. What I'm going to do is eliminate the second argument. The first argument will become the typehint and, if it is a primitive type (int, float, str, etc) initialize with the default call. Otherwise, if the type is a class with a 'default()' static method then that will called, and otherwise the initial value will simply be set to null and it will be up to the user to set the initial value in the constructor

indigo locust
#

Would someone mind testing this with mypy?

#
from typing import TYPE_CHECKING
from typing import Any
from typing import Callable
from typing import Generic
from typing import Optional
from typing import ParamSpec
from typing import TypeVar
from typing import Type

T_Paramaters   = ParamSpec('T_Paramaters')
T_Return       = TypeVar('T_Return')
T_Decoratee    = Callable[T_Paramaters, T_Return]


class Function(T_Decoratee):

    def __init__(self,  decoratee: T_Decoratee, privacy: str = 'readonly', scope: str = 'instance') -> None:
        ...

    def __set_name__(self, prototype: Type, name: str) -> None:
        ...

    def __call__(self, *positional, **encyclopedic) -> T_Return:
        ...
    
    @staticmethod
    def create(decoratee: T_Decoratee) -> T_Decoratee:
        return Function(decoratee)
    
@Function.create
def function(a: int, b: int) -> str:
    return f'{a} | {b}'

function()
#

I don't have it installed (wouldn't even know where to begin) and mypy playground doesn't seem to support ParamSpec very well — even though mypy itself supposedly supports it just fine

#

Specifically, I want to make sure that the decorated function is recognized as a callable that accepts two integers and returns a str

#

I'm pleased to report that this seems to be working with PyCharm though

merry current
#

For type hinting class attributes, what is the correct format?py class A: """Class docstring for A. Attributes: a (float): ... b (float): ... __b (float): ... Keyword Parameters: a (float): ... b (Optional[float]): ... """ def __init__(self, a: float, b: float = 10): self.a: float = a self.__b: float = b @property def b(self) -> float: """Attribute docstring for A.b""" return self.__b Which of these are incorrect, or unnecessary? What's missing? Should keyword parameters be mentioned in the class docstring or should there be an init docstring?

trim tangle
# merry current For type hinting class attributes, what is the correct format?```py class A: ...
  1. Type checkers generally ignore docstrings, so that's not the place.
  2. Attributes in __init__ only need annotations if they can't be inferred otherwise
class A:
    def __init__(self, a: float, b: float = 10.0):
        self.a = a
        self._b = b
        self._c = 42
        self._things: list[int] = []
        self._connection: Optional[Connection] = None

    @property
    def b(self) -> float:
        """Attribute docstring for A.b"""
        return self._b
merry current
trim tangle
merry current
#

Ok so Python describes parameter formats in the class docstring, without listing them as keyword parameters, whereas some other modules list them in the class docstring as if it was a function (like numpy.array).

#

I checked only dict and numpy.array for the above observation

merry current
blazing nest
void panther
#

can mypy or pyright be configured to ignore errors that occur on objects from a certain library? Stubs for it are provided, but are incomplete and sometimes wrong so it produces a lot of false positives

deep pendant
#

Can I refer to the TypedDict value by its name as in a namedtuple?
How to convert it?

#

(instead of my_instance_of_typed_dict["something"] I want to use my_converted_previous_instance.something)

soft matrix
#

you cant

#

its a dictionary

hollow copper
#

Do you want to use a types.SimpleNamespace instead?

soft matrix
#

if you want to convert a dictionary to something that supports item lookup maybe use pydantic

hollow copper
#

sn = types.SimpleNamespace(**some_dict); sn.attr

soft matrix
#

thats going to be untyped

#

i think they want something that supports type checking

hollow copper
#

Ah

loud locust
#

I have an amazing bot that can trigger your messages and send a message back.. the problem is when i do that the bot takes about 30 seconds to say the message back how can I fix this to normal time?

hollow copper
#

Forgot where I was

deep pendant
#

okey, next question - how to contruct TypedDict with total=False from another TypedDict (using the same fields)?

#

E.g.:

class PlayerStatus(TypedDict):
    manuallyStopped: bool
    errorOccured: bool
    duringBreak: bool
    trackUrl: str

and so:

class FieldsQuery(PlayerStatus, total=False):
    pass

Is it valid?

soft matrix
#

so all the fields only 'may be present' in fields query?

deep pendant
#

Yea - they are optional 😄

soft matrix
#

lemme check but im pretty sure what you have doesnt work

deep pendant
#

Is it therefore possible with inline one?

#

(Bussiness background and proof that is a real usage - MongoDB's result is required to fulfill PlayerStatus's fields completeness but any field in $set updating query is optional)
😄

soft matrix
#

theres no way that ik of to do this without just redefining the entire thing

deep pendant
#

😦

void panther
#

How can I do something like this?

class A:
    @classmethod
    def from_string(cls: type[Self], string: str) -> Self:
        return A()
class B:
    @classmethod
    def from_string(cls: type[Self], string: str) -> Self:
        return B()

def f(cond: bool) -> Union[list[A], list[B]]:
    if cond:
        type_ = A
    else:
        type_ = B
    return [type_.from_string(s) for s in "123"]
soft matrix
#
class A:
    @classmethod
    def from_string(cls: type[Self], string: str) -> Self:
        return A()
class B:
    @classmethod
    def from_string(cls: type[Self], string: str) -> Self:
        return B()

@overload
def f(cond: Literal[True]) -> list[A]: ...
@overload
def f(cond: Literal[False]) -> list[B]: ...
def f(cond: bool) -> Union[list[A], list[B]]:
    if cond:
        type_ = A
    else:
        type_ = B
    return [type_.from_string(s) for s in "123"]
#

import overload and Literal from typing

trim tangle
#

No HKTs 😔

#

But yes, that's the way

#

If just make both branches have a listcomp

void panther
#

Ah, if the actual cond depends on some runtime stuff, do I just make both branches list comps like fix error suggested?

soft matrix
#

how does hkt help here?

trim tangle
#

Hmm, it doesn't

trim tangle
soft matrix
void panther
rough sluiceBOT
#

auto_neutron/windows/new_route_window.py lines 315 to 328

def _route_from_csv(self, path: Path) -> t.Optional[RouteList]:
    try:
        with path.open(encoding="utf8") as csv_file:
            reader = csv.reader(csv_file)
            header = next(reader)
            if len(header) == 5:
                row_type = NeutronPlotRow
            elif len(header) == 7:
                row_type = ExactPlotRow
            else:
                self.status_bar.show_message("Invalid CSV file.", 5_000)
                return
            log.info(f"Parsing csv file of type {row_type.__name__} at {path}.")
            return [row_type.from_csv_row(row) for row in reader]```
soft matrix
#

yeah thats fine

void panther
#

Well, with pyright I get

error: Expression of type "list[NeutronPlotRow | ExactPlotRow]" cannot be assigned to return type "RouteList | None"
    Type "list[NeutronPlotRow | ExactPlotRow]" cannot be assigned to type "RouteList | None"
      TypeVar "_T@list" is invariant
        Type "NeutronPlotRow | ExactPlotRow" cannot be assigned to type "ExactPlotRow"
          "NeutronPlotRow" is incompatible with "ExactPlotRow"
      TypeVar "_T@list" is invariant
        Type "NeutronPlotRow | ExactPlotRow" cannot be assigned to type "NeutronPlotRow"
          "ExactPlotRow" is incompatible with "NeutronPlotRow"
      Type cannot be assigned to type "None"
#

I'm not too concerned about having correct typing, but I'm doing a pass through the code and trying to fix some of the simpler things

soft matrix
#

oh maybe you cant do that then

void panther
#

also, quite a few things I have are initialized as None and then assigned values after the user interacts with the app, would the best choice there to prevent the checker from seeing it as None be there to just assert before every use?

#

most of the errors I get now that are not pyside's stubs being wrong are just error: "rating_const" is not a known member of "None" etc.

soft matrix
#

setting the None to Any and just dont mess up

#

and making it not an optional typee

void panther
#

so either var: type = None #type:ignore or give it an union with Any?

soft matrix
#

no var: type = Any

void panther
#

ah

#

I guess I could make everything a property that raises on accessing None but that'd be a lot of properties

indigo locust
#

Would someone running mypy (or any other other static checker aside from PyCharm) mind giving this a run?

#
from typing import overload
import typing


def overloaded(function):
    return function
    
typing.overload = overloaded



@typing.overload
def function(a: int, b: int) -> int:
    ...


@typing.overload
def function(a: float, b: float) -> float:
    ...


@typing.overload
def function(a: str, b: str) -> str:
    return ''


def function(a, b):
    return a + b
#

I'm just curious if its possible to override the function of the overload decorator without affecting how its read by checkers

void panther
#

you can stick it under if not TYPE_CHECKING

#

although in there I think you'll be using the old reference to the original overload

indigo locust
#

Just noticed that

#

But you're saying that 'if TYPE_CHECKING is False', which happens at runtime but not during the static checking phase, the decorator will be changed?

void panther
#

Yeah, but no type checker should see that change

indigo locust
#

Nice. I'm working on an actual overloading decorator, and it'd be nice to keep the syntax clean

#

Though I know that changing anything in the stdlib is a dangerous game. I'm just measuring my options

grave fjord
indigo locust
#

I did test that already, and it all looked good 🙂

trim tangle
#

I see what you did

indigo locust
#

And what would that be, Mr Fix? 😉

soft matrix
#

you know you could do this in one line with a protocol + type var

indigo locust
#

Oh totally

#

I'm just messing around with the decorator itself — I'd like to add some runtime functionality

#

Actually — lemme ask you guys (and I know this would be better in another channel, but since we're all here

#

Lets say I did want to modify the decorator at runtime, something I know is a no-no in general

void panther
#

I did do the same thing when I needed the signatures at runtime for validation

indigo locust
#
def new_overload_decorator(function):
  
  if SOME_SIGNALING_BOOLEAN:
    return CUSTOM_BEHAVIOUR

  else:
    return DEFAULT_BEHAVIOUR
void panther
#

It's not like changing the default raising callable is a huge changer of what it does

indigo locust
#

This isn't too intrusive is it? Do the new thing if and only if a specific, intentional signal is received, else do the default

#

Yeah, totally. Its just that as a general rule, changing the bahviour of something that other libraries rely on makes for bad interactions

#

But as you say, this might be a exception to that, since the original literally does nothing

void panther
#

also, can change it back after your module runs if for some reason others need the function

indigo locust
#

🙂

#

Yesterday I tinkered with a fun little pattern matching tool called Pampy, and I used it to assemble an overloading decorator pretty quickly, though it was just a mockup I rigged together at 2 in the monring

void panther
indigo locust
#

But I have to ask, with the ease it was put together by my, someone with like, no actual skills — why is there not an language standard one so far

#

Oh, let me guess. It isn't "Pythonic"

void panther
#

Do you mean patter matching?

indigo locust
#

No, method overloaded

void panther
#

There isn't as big for it as in other languages is there?

#

typehint with a protocol that describes what you need and you don't have to care about the types

indigo locust
#

I suppose

#

It would save on having to route functionality yourself though

void panther
#

in a get on a generic dict, why is mypy expecting the second argument to be the value type?

hollow copper
#

I would like to add an attribute to a function as a way of creating a static variable that persists across function calls. Like:

def something():
    something.counter += 1
    print(f"Called {something.counter} times")
something.counter = 0

How can I do this with type hints, or at least without mypy complaining about adding the counter attribute?

tight cliff
#

why do i get incompatible types here? (mypy)

brisk hedge
upbeat wadi
tight cliff
#

ahhh

#

cheers

#

nevermind, that makes the assignment to empy list problematic

#

(expression has type "List[<nothing>]", variable has type "Union[List[str], List[Dict[Any, Any]]]")

upbeat wadi
#

it works with pyright, i'm not sure if that's a mypy bug or intentional behaviour

tight cliff
#

hmmm ive thought about this a little more

#

having multiple types in a list like that doesnt really make sense right

#

from a provable type safety perspective

void panther
#

I'm trying to fix the typing for https://github.com/Numerlor/Auto_Neutron/blob/f4867a5a8b8ca530376dbab32ca3ebec8c5ee762/auto_neutron/utils/recursive_default_dict.py, why does adding a dict cast on self_value at L63 fix

  D:\pycon\Auto_Neutron\auto_neutron\utils\recursive_default_dict.py:63:65 - error: Argument of type "RecursiveDefaultDict[Unknown, Unknown]* | RecursiveDefaultDict[_KT@RecursiveDefaultDict, _VT@RecursiveDefaultDict] | RecursiveDefaultDict[Unknown, Unknown]" cannot 
be assigned to parameter "dict_" of type "dict[_KT@RecursiveDefaultDict, _VT@RecursiveDefaultDict]" in function "update_from_dict_recursive"
    Type "RecursiveDefaultDict[Unknown, Unknown]* | RecursiveDefaultDict[_KT@RecursiveDefaultDict, _VT@RecursiveDefaultDict] | RecursiveDefaultDict[Unknown, Unknown]" cannot be assigned to type "dict[_KT@RecursiveDefaultDict, _VT@RecursiveDefaultDict]"
      TypeVar "_VT@dict" is invariant
        Type "Unknown | RecursiveDefaultDict[Unknown, Unknown]" cannot be assigned to type "_VT@RecursiveDefaultDict"
      TypeVar "_VT@dict" is invariant
        Type "Unknown | RecursiveDefaultDict[Unknown, Unknown]" cannot be assigned to type "_VT@RecursiveDefaultDict" (reportGeneralTypeIssues)
#

I also wanted to make the dict value type recursive with t.Union[_VT, "RecursiveDefaultDict[_KT, ...]]" but I'm not sure how to extract it with the typevars

blazing nest
#

Is this on strict?

void panther
#

whatever the defaults are, just ran pyright .\auto_neutron\utils\recursive_default_dict.py

hollow copper
grave fjord
#

I think it's clearer to use a global than mutate the function

void panther
#

or a class

grave fjord
#

I think I'd use a context manager, closure and itertools.count

#

So you can join the thread when you exit the cmgr

#

Wait there's no thread I'm thinking of a different question from someone else on a different discord server

indigo locust
#

I asked about this yesterday, but never really got anywhere

#
from typing import Any
from typing import Callable
from typing import Generic
from typing import ParamSpec
from typing import TypeVar

T_Parameters = ParamSpec('T_Parameters')
T_Return     = TypeVar('T_Return')
T_Args       = T_Parameters.args
T_Kwargs     = T_Parameters.kwargs

class Decorator(Generic[T_Parameters, T_Return]):

  def __init__(self, decoratee: Callable[T_Parameters, T_Return]):
    self.decoratee = decoratee

  def __call__(self, *args: T_Args, **kwargs: T_Kwargs) -> T_Return:
    return self.decoratee(*args, **kwargs)
#

What is the right way to do this?

#

Specifically, I'm having two problems. The first is that PyCharm can't predict either the arguments or the return type of the object when called unless I create it using a static method as so:

#
@staticmethod
def create(decoratee: Callable[T_Parameters, T_Return]) -> Callable[T_Parameters, T_Return]:
  return Decorator(decoratee)
#

MyPy playground, by the way, can't even cope with this, and throws throws a fit saying that ParamSpecs have no args or kwargs attributes

#

The second is that doing it this way does give Pycharm the ability to predict the signature and return types when the object is called, but it can't tell that the object is of type Decorator, it just assumes that it's a generic callable

#

I'm hoping that most of these problems have to do with the relative recent-ness of the ParamSpec protocol. Hopefully it all gets ironed out sooner or later

#

Anyway, any thoughts?

soft matrix
#
from typing import Callable, Generic, ParamSpec, TypeVar

P = ParamSpec('P')
T = TypeVar('T')


class Decorator(Generic[P, T]):
  def __init__(self, decoratee: Callable[P, T]):
    self.decoratee = decoratee

  def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T:
    return self.decoratee(*args, **kwargs)

@Decorator
def something(foo: int) -> str:
    return "hi " * foo

reveal_type(something(1234))```this all works for me with pyright
indigo locust
#

In which checker, so I know?

#

Oh, mypy

soft matrix
#

pyright

indigo locust
#

Is there an online checker for that?

#

Also, checking with PyCharm I get this:

#

So it can't deduce the call signature, but it can deduce the return type and also is aware that the object is of type Decorator

soft matrix
#

pycharm is bad

void panther
#

I wouldn't put much faith in pycharm

soft matrix
#

atleast the type checking parts of it

indigo locust
#

As a type checker, or as an editor?

soft matrix
#

the actual editor's nice

void panther
#

type checking

#

it tries, but I found it fails on most of the more complex things

soft matrix
#

yep this is the main reason i stopped using it

indigo locust
#

I understand the gold standard is mypy, followed by... what?

soft matrix
#

i think the gold standard is pyright tbh

#

it gets such frequent updates and always supports the newest peps

void panther
#

It's nice when coding to catch the quick things and doesn't want you to do pretty much anything with the typing, but apart from that a proper type checker will help much more

void panther
#

Yes

indigo locust
#

I didn't realize it was so out of favor

void panther
#

actual type checkers are annoying to get right

indigo locust
#

As a checker

#

What I'd like is to have some schema where I can check my code in mypy and whatever other checkers and common and trusted — such as pyright

#

And if it works in PyCharm, great

#

How would I do that? Additionally, is there some pay I can configure PyCharm to use mypy/pyright as a check-as-you-type static checker in place of the one it uses automatically?

soft matrix
#

no

#

the plugin for mypy with pycharm is really slow and leaks memory

#

and theres no builtin pyright integration so youd need to setup a file watcher or something

void panther
#

running them with the builtin terminal should be simple enough

indigo locust
#

Rockin, thanks

rustic gull
#

why is there a whole channel for type-hinting, if typehinting doesnt do anything to the interpretter at all

#

they are useless, except for yourself

buoyant swift
#

because tools like pyright and mypy exist, which help you statically verify types

soft matrix
buoyant swift
#

really

trim tangle
#

Yeah, they're not just for documentation

#

They also provide great IDE support

indigo locust
#

For metaclasses

#
def __call__(prototype: Type[T_Instance], *positional: Any, **keywords: Any) -> T_Instance:
  ...
#

Like that?

soft matrix
#

T_Instance is a little confusingly named for a metaclass

#

but yeah

indigo locust
#
T_Instance  = TypeVar('T_Instance')
T_Prototype = TypeVar('T_Prototype', bound=Type[T_Instance])

def __call__(prototype: T_Prototype, *positional: Any, **keywords: Any) -> T_Instance:
  ...
#

Better?

#

Or would it be T_Prototype = Type[T_Instance]?

blazing nest
#

One type variable can't be bound to another right?

pastel egret
#

It cannot no, you use Type[T_Instance] directly like in the 2nd-last code block.

indigo locust
#

I'm wanting to make a custom generic that types exactly like what's inside it

#

ReadOnly[bool] would type exactly as bool

#

How do I do that?

pastel egret
#

That's not really possible. You could cast it to bypass the check, and have checkers treat it as the variable exactly, but you'd lose any checks on mutability. __getattr__ is understood by checkers, so you could use that to indicate any attribute can be read but not written, but you can't really specify the specifics that way. If you want both you'd need a plugin, or have to re-define checker-only versions of the classes.

indigo locust
#

Huh!

#

Interesting

indigo locust
#

I've found a halfway decent solution, I think

#
from typing import TypeVar

T = TypeVar('T')

def ReadOnly(prototype: T) -> T: return prototype
def Private(prototype: T) -> T: return prototype
def Public(prototype: T) -> T: return prototype

thing : ReadOnly(bool)
#

Seems to work okay

brisk hedge
#

ReadOnly: TypeAlias = typing.Annotated[T, ReadOnlyMarkerType]?

mint inlet
dire bobcat
#

what do you typehint the return to in classmethod

#

you cant -> self

#

or classname

mint inlet
# dire bobcat you cant ` -> self`

Class name in quotes, typing_extensions.Self if your type checker supports it, or def f(cls: Self, ...) -> Self where Self = TypeVar('Self')

dire bobcat
#

would it be "self"?

mint inlet
#

It's just a typevar

#

Just that it gets bound to the class/instance

tranquil turtle
mint inlet
#

Does anyone have any largescale examples of ParamSpec and Concatenate?

trim tangle
#

Works with pyright, not sure about mypy

mint inlet
mint inlet
soft matrix
mint inlet
#

Tm

soft matrix
#

I'm thinking about it

trim tangle
mint inlet
#

Mypy PR sitting unreviewed gang

soft matrix
#

I need to figure out if i was just really stupid when I initially implemented it

#

Cause i don't like the current thing I have

mint inlet
#

Lol

trim tangle
#

I hope TypeVarTuple gets accepted. ParamSpec is extremely limited when it comes to positional-only functions

mint inlet
#

Ya :(

fierce ridge
#

And ProtocolFrom

#

I had started drafting proposals for typing-sig but never finished...

soft matrix
#

ProtocolFrom already has an issue open on the typing repo doesnt it?

fierce ridge
#

I'll look

blazing nest
#

Can you not have a generic TypedDict?

#

Hmm, I fixed it by putting the generic first but I thought it always came last?

fierce ridge
#

Nope unfortunately, i have wanted it though

blazing nest
#

This is huge

mint inlet
#

oh wait took a better look

#

i missed a property cause otherwise they would be indistinguishable xd

#

well maybe stuff like

class RoleOptionData(TypedDict):
    type: Literal[8]
    name: str
    description: str
    required: NotRequired[bool]


class MentionableOptionData(TypedDict):
    type: Literal[9]
    name: str
    description: str
    required: NotRequired[bool]

can be merged together, I think?

blazing nest
mint inlet
#

yeah

#

to be honest i wouldn't care too much about it being quite chonky

#

(I've had several hundred lines of types for audit logs, lol, though i really hated it and there must have been a better way)

#

codegen'd ofc :)

blazing nest
#

And that's roughly the same amount of code^^^

blazing nest
mint inlet
#

I think

class TypicalOptionData(TypedDict):
    type: Literal[8, 9]
    name: str
    description: str
    required: NotRequired[bool]

is the same as the union of both, but I might be missing some variance thing lol

#

(like I said, whether you can combine depends on the uses, if you have something that depends on only getting one kind of option then that won't work)

blazing nest
mint inlet
#

hm maybe you can make the base part of every option a generic typeddict

#
class _TypicalOptionData(Generic[T], TypedDict):
    type: T
    name: str
    description: str
    required: NotRequired[bool]

RoleOptionData = _TypicalOptionData[Literal[8]]
MentionableOptionData = _TypicalOptionData[Literal[9]]

...

class MinMaxNumberCommandOptionData(_TypicalOptionData[Literal[4]]):
    min_value: NotRequired[Union[int, float]]
    max_value: NotRequired[Union[int, float]]

something like that... maybe? idk if that would type well lmao

blazing nest
blazing nest
#

I really like that

mint inlet
blazing nest
#

Yeah I remember reading that, doesn't float have different (more) methods than int though?

mint inlet
#

i think like 3?

#

lemme check

blazing nest
#

lmao

#

3? I thought it was more

mint inlet
blazing nest
#

Ah, well then I'll just stick with Union[int, float] - I don't win a lot with only float

mint inlet
#

yea

mint inlet
#

looks like mypy has missing support for this

#

meanwhile pyright is doing... whatever it does

pastel egret
#

Pyright uses the notation T@_Something to indicate what thing the typevar was bound to.

blazing nest
#

I thought it did, although I do remember some confusion from the errors where it's all a bunch of TypeVars

mint inlet
#

it's a cool convention tho

#

wonder if there's going to ever be some standardization of outputs

#

(random thought: it would nice to at have some unified way to mark type ignores as for a single type checker)

trim tangle
#

What's the way to annotate a task group from anyio?

#

Do I really have to import the private _tasks module?

#

oh wait, I need to do from anyio.abc import TaskGroup, nevermind

indigo locust
#

I'm wondering if maybe I've found a bug in Cython

#

__init_subclass__ is not implicitly a class method when defined inside the class

#

For cdef classes. Is this by design?

soft matrix
#

init subclass is implicitly a classmethod

indigo locust
#

Well, python's object model dictates as such

#
cdef class Thing(object):

    def __init_subclass__(*positional) -> None:
        print('hello')

class OtherThing(Thing):
    ...
#

TypeError: unbound method Thing.init_subclass() needs an argument

soft matrix
indigo locust
#

Ahhhh

#

Thank you!

nova venture
#

Is there anyway to check if a class is annotated with @final? I looked at the function itself (def final(f): return f), and the docstring confirms that because it just returns f there is no check at runtime that can be performed.

#

But I need to confirm that no subclasses of a particular class exist, and am wondering if there is way, even hacky, to check if final decorator is present

trim tangle
soft matrix
#

yeah

#

its cool

trim tangle
#

Pyright gets better day by day

soft matrix
#

not sure why it wasnt in the pep to start off with tho

trim tangle
#

?

#

ah

#

I guess because it was hard to add to mypy 😄

#

or something

soft matrix
#

eric wrote the pep

trim tangle
#

hm, what pep are you talking about?

soft matrix
#

oh this reminds me, does the pyright playground always use the newest version

trim tangle
#

no

soft matrix
#

647(?) the one for type guards

trim tangle
#

I'm planning to add some settings soon

trim tangle
# soft matrix 647(?) the one for type guards

Pyright supported TypeGuard for a long time.
The update is talking about how e.g. if an object has a __bool__(self) -> Literal[False], then you can do:

x: Union[AlwaysFalseyObject, int]

if not x:
    raise Exception

reveal_type(x)  # int
#

wait

#

I think I understand what you're talking about

#

the TypeGuards that raise exceptions instead of returning bool

#

yeah, that's something I remember wanting some time ago

soft matrix
#

i meant i think the two form version should have been in the pep from the start

trim tangle
#

I guess it was too much to implement all at once

trim tangle
twilit badge
#

Is it possible to use ParamSpec in python 3.6 in a way which pyright would be fine with?

trim tangle
#

also... I'm pretty sure 3.6 doesn't receive security updates anymore 👀

soft matrix
#

thank you

twilit badge
#

well, it's not my project and the maintainer insist on compatibility...

#

but thanks, I'll check typing_extensions out

trim tangle
#

I am extremely lazy as you can see

soft matrix
#

i didnt know it was public

#

also my js is very not good

trim tangle
#

ah

#

well

#

In production I'm using a messy system where the POST /pyright endpoint goes to a serverless function, and all the other ones go to the backend from the repo

#

Because my VPS sucks, and it certainly can't take more than 1-2 POST /pyright requests at a time

nova venture
#

i wonder why pyright is based on js instead of python like mypy. makes it a bit heavyweight, since it requires node as a py package

trim tangle
#

I think it's a big factor in why it is faster than mypy

soft matrix
#

js is jit compiled

#

cpython isnt

void panther
#

I'd guess it's most probably because of vscode

soft matrix
#

mypy is probably a lot faster using pypy

nova venture
#

mypy uses mypyc i believe

#

so if mypyc gets faster than mypy will get faster

void panther
#

not that much of an advantage to be in python apart from some of the basic framework

trim tangle
#

because now you need to add node.js

soft matrix
#

ast module makes things easy

trim tangle
#

Actually, maybe I can deploy the github repo thing as a serverless function lemon_surprised

#

my cloud provider supports "serverless containers"

#

This sounds ridiculously cheap 🤔 what's wrong?

soft matrix
#

It's free?

trim tangle
#

?

#

it's not free, but it's unimaginably cheap

#

for reference, ₽75 = $1

soft matrix
#

How much traffic do you get a month?

#

I wouldn't have thought it was a huge amount

#

Or which things are you mentally charging yourself for?

trim tangle
#

so far cloud functions have been free

#

I'm storing some Rust containers in there, so I got billed ₽0.07 for storage

#

@soft matrix Seems like there were about 300k requests to the lambda in the last month, huh

soft matrix
#

Oh damn

trim tangle
#

seems like too many 🤔 not sure if I'm using the stats correctly lol

#

they have their own query language... and I'm confused

hollow sage
#

Hey all, apologies if this is poor etiquette (asking for forgiveness rather than permission), but I have a challenging static typing issue which I wonder if anyone in here might be able to lend some help with: #help-cherries message

trim tangle
#

@hollow sage Maybe you can do it with a curried function?

hollow sage
trim tangle
#

ahh

#

instead of using annotations, I would use a separate type, like Schema = Callable[[object], T]. For example, you could have int_schema: Schema[int], or optional_schema: Callable[[Schema[T]], Schema[Optional[T]]]

#

but if you really need annotations, I'm not sure

#

You could do some class magic and abuse generic classes with something like Converter[Optional[int]].convert(42)

hollow sage
rustic gull
#
$ tree
.
├── main.py
└── main.pyi

0 directories, 2 files
$ python3 -m mypy .
Success: no issues found in 1 source file
$ python3 -m mypy main.py
main.py:13: error: Library stubs not installed for "backports.zoneinfo" (or incompatible with Python 3.10)
[...]

why is mypy . not checking the file?

soft matrix
#

Add a py.typed

#

Also don't use stubs unless you have to

twilit badge
#

yeah, I've always wondered, why would you ever want to use stub files if you can just put the type hints to the file itself? What's the actual use-case for them?

soft matrix
#

If you can't add type hints for the file itself

#

Although at this point it should only really be used for extension modules and stubs for libraries

twilit badge
#

oh, I see so if an external library didn't define their type-hints properly, a stub file can be used to define them, right?

mint inlet
#

yes

indigo locust
#

Wow

#

PyCharm's type checker really is trash

#

It can't even figure out a custom descriptor

stray summit
#

anyone aware of a way to make mypy/mypyc consider a folder as a different package name? i want ot to consider the folder "src" as if it was "my_own_pkg" in site-packages

summer berry
#

Maybe you could cheat by creating a symlink

little hare
#

eg since they support py2, they'd define the type hints in the stubs

soft matrix
#

At this point you shouldn't be supporting py2

dire bobcat
#
class FolderModel(Model, ABC):
    file_urls: List[str] = None
    folders: List[FolderModel] = None
    name: str
    create_epoch: int = None
    last_edit_epoch: Optional[float] = None
#

from odmantic import Model

#

and i'm getting; Extension 'src.cogs.ide.ide' raised an error: TypeError: Unhandled field definition FolderModel: typing.List[str] = None

frigid nova
#

hello, don't hesitate to redirect me to the right channel if this one is not:

I noticed that running inspect.signature on a function imported from a module that does not import from __future__ annotations will build the return annotation as an actual Python object (for example <class 'int'>) instead of a string, even if my own module, where I run inspect.signature imports it.

  1. if this expected behavior?
  2. if no, where should I report it?
  3. if yes, what would be the best way to get back a string instead of an object? for simple types it's easy (if hasattr(annotation, "__name__"): annotation = annotation.__name__), and for mildly complex types it seems I can rely on repr(annotation), byt maybe you have more robust suggestions?
trim tangle
#

All from __future__ import annotations does is it stores strings instead of actual objects in the __annotations__ metadata when creating a function with def or a class with class

void panther
#

After it's evaluated, there's no way to get the string apart from inspecting the source

#

Why do you need the strings? Usually you'd be doing it the other way around where the strings would be evaluated when you need the annotation

frigid nova
#

I need the strings because I'm building a data extractor that can support both parsing the source code, and inspecting live objects: https://github.com/pawamoy/griffe (it will be used in a new Python handler for mkdocstrings, if you happen to know that project)

trim tangle
#

does odmantic inspect annotations?

dire bobcat
soft matrix
dire bobcat
trim tangle
trim tangle
dire bobcat
#

raised an error: ConfigError: field "__root__" not yet prepared so type is still a ForwardRef, you might need to call ParsingModel[ForwardRef('List[FolderModel]')].update_forward_refs().

#

when doing file_urls: Optional[List[str]]

dire bobcat
trim tangle
#

Are you using from __future__ import annotations?

dire bobcat
#

yes

trim tangle
#

I assume that breaks everything

dire bobcat
#

o

soft matrix
soft matrix
#

!d inspect.signature

rough sluiceBOT
#

inspect.signature(callable, *, follow_wrapped=True, globals=None, locals=None, eval_str=False)```
Return a [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature "inspect.Signature") object for the given `callable`...
soft matrix
#

Oh it's not by default lol

trim tangle
#

ah, it automatically evals the annotations

#

like typing.get_type_hints?

soft matrix
#

Yeah

void panther
dire bobcat
# trim tangle Are you using `from __future__ import annotations`?

ive done ```py
class FolderModel(Model, ABC):
name: str
file_urls: Optional[List[str]]
folders: Optional[List[str]]
create_epoch: Optional[int]
last_edit_epoch: Optional[float] = None

class FileModel(Model, ABC):
user_id: int
folder: Optional[FolderModel]
name: str
file_url: str
create_epoch: Optional[int]

#

without __annotations__

#

how do i typehint the List[] to FolderModel (itself) without __annotations__?

#

I need to do folders: Optional[List[FolderModel]]

frigid nova
void panther
#

well there's not much you can do for arbitrary annotations, if you only need type hints then you probably could at least make something representing the same typehint, but those also destroy some data when they're evaluated

frigid nova
#

thanks for the help 🙂

trim tangle
#

If someone does a hack like this, the runtime annotation will be quite different from a string annotation

if TYPE_CHECKING:
    TkEvent = tkinter.Event[W]
else:
    from collections import defauldict
    TkEvent = defaultdict(lambda: tkinter.Event)
#

I hate these types which should support __class_getitem__ but don't!!!

void panther
#

ah forgot about TYPE_CHECKING, that messes things up completely

void panther
trim tangle
#

but the runtime behaviour shattered my dreams

frigid nova
#

did you read the "pure-Python codified rant aspiring to a world where numbers and types can work together." from posita/numerary on github 😄 ? it was quite a fun read

trim tangle
#

Speaking of complexity... I just realized how Python's type system is way more complex than the Haskell's (base) type system. Just look at how huge type checkers are

soft matrix
#

running a type checker on code and actually using it to resolve types is a pain

#

but it would actually make stuff like this work

#

and maybe take a look at pyanalyse as i think it has actual support for this

dusk void
#

I'm using pydantic and need to have a list of possible values that come from typing.Literal[] array. But I would like to generate what's inside this array dynamically. How would I go about doing that?

soft matrix
#

a subscript is just a regular function call

#

so you can basically whatever in there

#

although you lose any static type checking of that field

dusk void
#

How do I get a function to return a custom typing.Literal[]

soft matrix
#

typing.Literal[some_function()]

#

is valid

dusk void
#

Ah

trim tangle
#

Yeah, typing constructs aren't special in any way

soft matrix
#

so is ```py
def something() -> Any:
return typing.Literal[1,2,3,4]

dusk void
#

[1,2,3,4] ?

soft matrix
#

could be

#

actually it might have to be a tuple of that

trim tangle
#

it should be either a single element or a tuple

#

foo[a, b, c] actually just means foo[(a, b, c)]

dusk void
#
import typing

def getA():
    return ["grape", "melon", "banana"]

names = typing.Literal["apple", "pear", "peach"]
names2 = typing.Literal[getA()]

print(type(names))
print(type(names2))
#

It worked, thanks guys!

trim tangle
soft matrix
#

!e
import typing

names2 = typing.Literal[["grape", "melon", "banana"]]
print(names2)

rough sluiceBOT
#

@soft matrix :white_check_mark: Your eval job has completed with return code 0.

typing.Literal[['grape', 'melon', 'banana']]
soft matrix
#

yeah thats not gonna work if pydantic tries to inspect it

#

!e

import typing
names2 = typing.Literal[["grape", "melon", "banana"]]
print(names2.__args__)
names2 = typing.Literal["grape", "melon", "banana"]
print(names2.__args__)
rough sluiceBOT
#

@soft matrix :white_check_mark: Your eval job has completed with return code 0.

001 | (['grape', 'melon', 'banana'],)
002 | ('grape', 'melon', 'banana')
trim tangle
#

yep

dusk void
soft matrix
#

you need to return a tuple not a list

dusk void
#

I'm iterating a larger list to derive elements in the end, can I turn them into a tuple somehow?

soft matrix
#

yeah use the tuple constructor

dusk void
#

Got it, thank you!

chrome dust
#

How do refined a callable by the number of arguments it takes? E.g. a function parameter with the type Callback[[], None] | Callback[[int], None]

trim tangle
chrome dust
#
def foo(arg: Callable[[], None] | Callable[[int], None]):
  if arg.__code__.co_argcount == 0:
    arg()
  else:
    arg(1)

In the code above, how do I make mypy happy.

#

With the code above, mypy can't tell that I'm calling arg with the correct number of arguments. Mypy does not know that arg.__code__.co_argcount == 0 discriminates between to the two Callables in the union. I'm wondering if there something I can do to let mypy know what's going on.

soft matrix
#

you cant i dont think

#

oh use a type guard

chrome dust
#

@soft matrix Just Googled TypeGuard looks like the best appoard to me.

soft matrix
#
def takes_one_arg(func: Callable[[], None] | Callable[[int], None]) -> TypeGuard[Callable[[], None], Callable[[int], None]]:
    return arg.__code__.co_argcount == 0

def foo(arg: Callable[[], None] | Callable[[int], None]):
  if takes_no_args(arg):
    arg()
  else:
    arg(1)
chrome dust
#

LGTM

soft matrix
#

lemme just check that the last argument is correct to the TypeGuard cause i havent used it yet

#

but fyi this wont work with mypy as its support is only provisional

#

yeah its correct

trim tangle
#

ehhhh

#

not sure I like this idea.

#

If you wrap this in a callable class (like functools.partial) it will completely break

#

And what if the function is a bound method?

soft matrix
#

its got to have a type ignore in it anyway

trim tangle
#

And what if the function is actually an overloaded function that behaves differently based on whether it gets 0 or 1 arguments?

#

IMO just make two separate functions (or an overloaded function with two different kwargs)

#

1 if less, and solves all of the above issues

#

inspecting __code__ is a hack that only works on functions, and only on CPython

chrome dust
#

@trim tangle thanks but I don't think these things matter too much for the use case. The code I'm writing requires a lots of on-the-fly visitors to process a database schema. I'm just making like easier for myself.

result = handle(
  json,
   on_bool=lambda: 'I dont need the value',
   on_str=lambda value: f'I need the value {value}',
)
trim tangle
#

Why not just lambda _: "I don't need the value" or something?

chrome dust
#

I think I just prefer keeping the visitors as clean a possible

trim tangle
#

I would prefer an unused argument to a hack with __code__ any day... But, well, that's your code 🙂

#

I think the interface with just one argument is really more simple. Users will have less code to read and understand. And there isn't a chance that they'll screw it up by passing in a decorated function, or a functools.partial object

chrome dust
#

@trim tangle 🙂 I'm do my best to not misuse my API. @soft matrix My tools are picking up the type guards so looks like like whatever versions I've got it's supported

soft matrix
#

i mean if it works it works (although idk if mypy already has support for the change to the pep if its not even actually been edited yet)

chrome dust
#

I'm happy with results. If anyone interesting to see it "production" code, I can post a snippet.

void panther
#

If I have an installed package which creates a module that's importable directly as module, how would I create stubs for that in the package?

soft matrix
#

just call it module.pyi?

void panther
#

wouldn't that create a stub for package.module instead of module

soft matrix
#

oh i see

#

just make a module.pyi file at the root of the project

#

if this isnt a library you dont need to include it

indigo locust
#

Has anyone had any luck getting PyCharm to recognize custom descriptor behaviour?

acoustic thicket
#

I don't really keep high expectations for pycharm's typechecker tbh

indigo locust
indigo locust
#
from typing import Any
from typing import Generic
from typing import Optional
from typing import TypeVar
from typing import Type
from typing import overload

class Object:
    ...

class Prototype:
    ...

T_Instance = TypeVar('T_Instance', Prototype, Object)
T_Return   = TypeVar('T_Return', bound=Type)

class Attribute(Generic[T_Return]):
    
    def __init__(self, type: T_Return) -> None:
        self.type = type

    @overload
    def __get__(self, instance: None, prototype: Type[Prototype] | Type[Object]) -> 'Attribute[T_Return]':
        ...

    @overload
    def __get__(self, instance: T_Instance, prototype: Type[T_Instance]) -> T_Return:
        ...

    def __get__(self, instance, prototype):

        if instance:
            return self.type()

        return self


class Thing(Object):

    attribute = Attribute(Object)

reveal_type(Thing.attribute)
reveal_type(Thing().attribute)
#

This is how I'm supposed to do it, right?

tranquil crag
tranquil crag
#

haha

indigo locust
#

Separate imports just feels more explicit to me, and, I prefer the look of it

#

That said, I've got the general idea right?

blazing nest
void panther
#

I usually import typing as t, although I haven't come up with a nice one for collections.abc

blazing nest
trim tangle
#

yes

#

well... you shouldn't be using Python 2 🙂

brisk hedge
#

Every # type: list[int] is only a few keypresses away from # type: ignore, which I think is a positive

trim tangle
#

inb4 a black plugin that automatically adds # type: ignore at the end of each line

soft matrix
#

That doesn't mean anything to a type checker

#

I mean why not if you are taking the time to do all this?

trim tangle
#

if I'm sketching something, I also use types like (a -> b) which don't make sense to the type checker

soft matrix
#

what does that mean to you?

trim tangle
soft matrix
#

that almost works with pyright doesnt it?

#

you just need brackets around the a

#

unless it was reverted

trim tangle
trim tangle
soft matrix
#

async def amap[A, B](aiter: AsyncIterator[A], fn: (A) -> B) -> AsyncIterator[B]: soon™️