#type-hinting
1 messages · Page 58 of 1
That will accept Foo as well, which I don't want
ah I see
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
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
I don't like it, looks ugly
Why not? I think it should
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"
its only the same stuff as __or__
yeah
So we'd also need a construct for "exactly the Foo class object"
Literal[Foo] 🙂
supported by pyanalyze 😄
Why?
class InstanceOf(Protocol[T_co]):
__class__: T_co
~Foo & InstanceOf[Foo]
```i think this works
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
This seems like overengineering so I think I'll just stick with the Union of subclasses lol
why are you doing this in the first place?
The method that takes the subclass as an argument wouldn't work on the superclass
But why?
Because the subclasses all define an attribute that the method accesses, but the superclass doesn't
Maybe you need an abstract class then?
So it would break if it accepted the superclass
Means you can't instantiate the superclass.
I don't think it would :)
They need the class, not the instance
Yeah
Ah yeah.
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.
We just need a "hybrid" type system. You want static OK "@static-typing😋
Let me be quiet lol
That does sound like an ABC. Are you ever going to have instances of the superclass? What you do is define the path segment with an annotation but don't give a value - PATH_SEGMENT: ClassVar[str].
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]?
ClassVar indicates it should be a class variable, not an instance variable.
how long before they realise class variables arent really class variables in python?
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?
i don't think so don't quote me
you can use whatever syntax you want
p-pardon?
They're not no.
some imports might not work
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
There's actually a set of typing docs now, with a page going through all the things you can/can't use:
https://typing.readthedocs.io/en/latest/source/stubs.html#syntax
yeah that works in pyis
You can use those indeed, also forward referencing, import cycles and the like aren't an issue.
!e ```py
from typing import ClassVar
class Foo:
bar: ClassVar = 1 # ah yes this is totally a class only variable
print(Foo().bar) # whoops
@soft matrix :white_check_mark: Your eval job has completed with return code 0.
1
Perefect, this is most likely where I got that from - I couldn't find a source when searching around
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?
!e ```py
from typing import ClassVar
class Foo:
...
print(Foo().name) # this doesn't work
@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__'?
name is in the class dictionary
!e ```py
class Foo: ...
print(Foo.dict) # this doesn't work
@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}
The name, dict and weakref aren't in the dict, they're pointers in the class structure.
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?
That's more of a special case.
They're metaclass properties indeed:
!e
print(list(type.__dict__))
@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__']
__name__'s in there, as a <member>.
wait if you type a dict to a list it only does the keys?
Iterating over a dict gives keys.
!e print(list({'a':'b'}))
@little hare :white_check_mark: Your eval job has completed with return code 0.
['a']
I believe there was a very fierce debate over how it iterate.
sounds fun
Anyway also on that page at the bottom is a set of links to typeshed issues tracking which checkers support some other newer features.
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
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: ...
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
just make profit a float
int and float are compatible bar like 2 methods or something
You could also start buy = prices[0].
right, that's cleaner thanks
I need to stare at this for a bit but a link to what I am trying to annotate. So that MethodType should take no arguments but self and the return type doesn't matter. https://gist.github.com/Melendowski/a6351456e3d17483faaaaff78dd48239#file-descriptors-py-L87
descriptors.py line 87
def register(method: MethodType) -> None:```
:|
wheres the code for this?
i can think of three options here
- unpack the tuple yourself again so mypy can infer its tuple[int, int]
- typing.cast it
- 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
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).
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
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'
@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.
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
@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
That said, there is another type of Node which can't be a child
(and will probably lead to an inconsistent type system)
Document and Fragment are Node types, but they are roots. Now, its no biggie if I'm not 100% precise
You could make a ChildNode class if you want to be precise 🙂
Blegghghghghghg
Nooooooooope
This object model is convoluted enough, thank you XD
or a type alias storing a union
I guess the only way to be 100% specific would be to create a dedicated subset class for each type
Making the type variable invariant?
If you have a list[Node], you can append subtypes of Node to it
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)
Ah
What happens if I bind to a literal?
you'll only be able to use the literal for the typevar
So, here's what I want...
def firstChildOfType(self, base: Type[Element] | Literal[CommentNode, TextNode]):
...
what does a Literal[CommentNode] mean?
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
you shouldnt be using literal here
@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?
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
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
The whole point of a type var is that it tied two types
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
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?
dont use stubs unless you have to
they just make things more annoying as you separate everything from itself
I have to — this is all coded in cython
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
So, no overloads in the implementation and no function-actual in the stub
well you can use overloads in the implementation, which is what id recommend but yeah
Rockin 🙂 Many thanks
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
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"
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
@hearty shell You mean, you want to generate code for the type dicts?
Yes
I guess you can use a parser like https://github.com/ivelum/graphql-py
or maybe you can take the output of this TypeScript code generator https://www.graphql-code-generator.com/plugins/typescript and parse it
Humm, I will test around with graphql-py, thanks!
Hey, pyright just got functools.partial supported hacked in
if only it could actually be typed 
are you actually proposing to make the type system useful?!
So I made a comment yesterday about @static-type.... Then I stumbled across Cython! https://cython.readthedocs.io/en/latest/src/quickstart/cythonize.html very interesting....
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
Prop = Literal["width", "height", "area"]
T = TypeVar("T", bound=Prop)
@dataclass
class Data(Generic[T]):
prop: T
value: float
foo: Data[Literal["width"]] = Data("width", 5)
bar: Data[Literal["height"]] = Data("width", 5) # error
baz: Data[Literal["area"]] = foo # error
generic TypedDicts are not allowed, unfortunately.
Does it work? Someone gave a similar implementation and it doesn't seem to #help-cherries message
@olive oar That's because "width" is not a valid type. Literal["width"] is
Oh so you need to do Data[Literal[...]]
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
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?
I am procrastinating on making a somewhat compete guide lol
The pins of this channel have some resources that should help
@olive oar
There's mypy reference:
https://mypy.readthedocs.io/en/stable/
There's pyright's documentation
https://github.com/microsoft/pyright#documentation
And then there are type hinting PEPs like PEP 484 and PEP PEP 589
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
!e
You can inspect type hints at runtime:
def f(x: int, y) -> str:
...
print(f.__annotations__)
@trim tangle :white_check_mark: Your eval job has completed with return code 0.
{'x': <class 'int'>, 'return': <class 'str'>}
but they don't actually do anything beyond that
Ah, so it's like metadata.
yep
There are libraries like dataclass_factory that do some cool stuff with it
!pypi dataclass_factory
TL;DR it automatically serializes/deserializes a dataclass to/from a JSON-like object based on its type hints
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?
For example, try this with mypy
from typing import Callable
class DebuggingFunction:
_fun: Callable[[int], str]
def __init__(self, fun: Callable[[int], str]):
self._fun = fun
def __call__(self, arg: int) -> str:
print("Argument: {0!r}".format(arg))
rv = self._fun(arg)
print("Result: {0!r}".format(rv))
return rv
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
😬
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?
Depends on what type checker you want
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
Question
Lets say I have a custom mapping class which also inherits from a base primitive
Which one is the most widely adopted?
mypy
Thanks, I'll go with that then.
class MyMap(Mapping[KT, VT], Primitive):
...
```Is this how I'm supposed to type it?
Ngl I've just been checking out Python stuffs in preparation, and I already feel quite lost 😂
Python is harmless. I've seen a lot of my classmates, after they get used to it, actually have crises of faith where they question their whole understanding of coding and why they ever used any other language to start with
Just give it a few weeks :3
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.
the typing ecosystem is indeed fractured
maybe like flow vs typescript
I think you want Primitive first?
But I think most production systems doing type-checking use mypy, at least because it has plugins
Beartyping should be nearing completing by the end of 2222
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.
...but it's not a static type checker?
it's not really a competitor to mypy, is it?
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
ah
So the lowercase list requires Python 3.9, how widely adopted is that?
#type-hinting message
Is it a good choice to base my library on 3.9+, or does it alienate too many users?
you can use lowercase classes with python 3.7+
you just need to import future annotations
hm, I realized callable syntax wouldn't be possible in lower versions even with future annotations
because annotations are expressions in the grammar
yep you need to quote them
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?
Pretty sure it means that a generic type has already been given type arguments
e.g. ```py
class Foo(Generic[T]):
pass
FooInt = Foo[int]
FooStr = FooInt[str] # error
Ah thanks
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.
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
...
Because in python 3 there is no way to determine where an inbound method comes from I don't think this is possible
oh, that's a shame, so I suppose just doing this would be the best way? ```py
def barr(klass: object, method: typing.Callable):
...
just wondering, what is your use case? (what does bar actually do? )
nothing, I was just wondering if type-hinting something like this was possible, it's purely educational question
ah I see, I think the closest you can come is requiring method to take T_o as its first argument
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?
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
Not actually a method is just as valid a callable as the ones on the class.
!e ```py
class Foo:
def x(self):
print(self)
print(f"{type(Foo.x)=}")
print(f"{type(Foo().x)=}")```
@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'>
they are only actually methods when they are tied to an instance ^
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
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
Range[Literal[0], Literal[5], int]
you could probably do something with Literal[int] TypeVars with pep 675
!pep 675
typing seems to move in the direction of a set of very specific hacks
so how even would I begin to define Range?
I've never written a custom typehint or something, so not sure where i'd start
class Range(Generic[MinT, MaxT, T]):...
Are you familiar with generic classes?
no
oh
Then maybe start with something more simple, like a box:
https://pyright-playground.decorator-factory.su/?gist_id=a35a4f71b0cd63183d6bb899eb2ce717&filename=example.py
I don't have a nice explanation right now, but mypy has something about it:
https://mypy.readthedocs.io/en/stable/generics.html
however, if you want to have a type that constraints an integer between 0 and 5, you can't really do that
!d typing.Generic
class typing.Generic```
Abstract base class for generic types.
A generic type is typically declared by inheriting from an instantiation of this class with one or more type variables. For example, a generic mapping type might be defined as:
```py
class Mapping(Generic[KT, VT]):
def __getitem__(self, key: KT) -> VT:
...
# Etc.
``` This class can then be used as follows...
so i can make it so the type checker knows what type at least (eg int or float), and then use the annotation's metadata to do validation elsewhere?
that's the main purpose of this
well, it will break in 3.11 😉
to validate it elsewhere, and have good typechecking
(probably)
why
from __future__ import annotations will become default IIRC
but typing.get_type_hints doesn't help that?
get_type_hints ignores Annotated
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))
@trim tangle :white_check_mark: Your eval job has completed with return code 0.
{'baz': typing.Annotated[int, {'allowed_values': [1, 2, 3]}]}
how does it resolve bar??
its up in the air
bar is in the class namespace already
typing isnt doing anything weird
But at runtime, Foo.__annotations__.baz is a string
Lib/typing.py line 1802
base_locals = dict(vars(base)) if localns is None else localns```
I meant more like the whole stringly annotation thing is a hack
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?
well yeah thats what pep 649 the deferred evaluation of annotations aims to solve
yes, that works
yes Type[T] works with type vars
Thanks
it means type[MyClass] and type[MyOtherClass]
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?
also not sure where the anime method is defined?
is it a dynamically typed thing?
or is it a proxy for self._anime
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
It's a property returning self._anime, yes. self._anime is an AnimeHelper instance, sorry should've explained
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.
yeah you need to change
class AnimeHelper(ObjectHelperBase):
# to
class AnimeHelper(ObjectHelperBase[Anime]):
and then it should work
Is that really all I needed to do lol
Yeah that worked lmao
Thanks
Oh that's so much easier
What type hint should I use for a container that supports + concatenation, e.g. list?
class Plusable(Protocol):
def __add__(self, other: Self) -> Self:
...
# or if you don't have typing_extensions:
T = TypeVar("T", bound="Plusable")
class Plusable(Protocol):
def __add__(self: T, other: T) -> T:
...
What about just typing.Sequence?
Sequences don't need to implement add https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes
If it also needs to be a container/iterable/something then I think you can inherit from the abc in the protocol class
I think I'll just type hint Iterable and convert it to list
def __init__(self, ctx: commands.Context,style=Union[discord.ButtonStyle.danger, discord.ButtonStyle.gray]) -> None:
``` is the style type hinting correct 
you probably should be using Literal around ButtonStyle.danger cause its an enum member right?
enum ?
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 ?
!d enum
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.
in discord.py they dont actually use this anymore but its docs are still useful
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
woah, i need to learn about enums
discord/enums.py lines 161 to 165
if TYPE_CHECKING:
from enum import Enum
else:
class Enum(metaclass=EnumMeta):```
ah
shhhh
metaclasses 
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]
oh okay
if you want any member just use x: EnumName
like wise or ButtonStyle.1 ,ButtonStyle.4 ?
thx
can you further explain whats Literal ?
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
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
```...
the docs might be helpful to read aswell
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 
try #discord-bots cause this isnt type hinting related (also idk anything about how buttons actually work)
Anyone know if mypy dropped support for pyproject.toml exclude syntax:
[tool.mypy]
python_version = "3.10"
exclude = ["tests/", "test.py"]
is it just a flat regex? or did the syntax you're doing work previously?
like should it be excluede = "<regex>" @faint dust ?
Worked previously and it follows other toolings syntax.
Regex syntax probably works as that's worked in the past too.
Just didn't see any deprecation warning for this.
huh, I don't know. but it sounds like a regression.
Yepp, a friendly person found a PR that will bring it back: https://github.com/python/mypy/pull/11828
ah, nice!
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
You need FooBar: TypeAlias = "Foo | Bar"
that's the 3.9 Union right, it won't let me import TypeAlias in 3.8
ImportError: cannot import name 'TypeAlias' from 'typing' (/usr/lib/python3.8/typing.py)
use typing_extensions
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()
T can be inferred
Thing(1234) has type Thing[Literal[1234]] (if youre using pyright)
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
idu why you need to use Annotated here
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
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
How should I annotate a contextmanager function's return value?
from contextlib import contextmanager
@contextmanager
def foo() -> ?:
yield 5
!d collections.abc.Generator
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.
This... this is interesting...
isn't there something like typing.ContextManager?
from contextlib import contextmanager
from collections.abc import Generator
@contextmanager
def foo() -> Generator[int, None, None]:
yield 5
```yes but its not for this situation
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
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!
can also just use Iterator
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
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)())
main.py:35: note: Revealed type is "<nothing>"
reveal_type(Attribute(Thing1, Thing2)())
main.py:35: note: Revealed type is "def () -> main.Thing2"
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
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
sorry another question, is there not a -q/--quiet flag on mypy to suppress the success message when there's no error?
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
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
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?
- Type checkers generally ignore docstrings, so that's not the place.
- 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
- Thanks
- For this, it is still a good practice to describe keyword parameters somewhere that a user can see by invoking
help(A)right? Should this be in the class docstring itself?
You should put the docstring for __init__ under __init__
So the parameter descriptions will need to be obtained via help(A.__init__)?
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
This is wrong, if there are optional keyword parameters python lists them in the class docstring too
class A:
"""..."""
a: float
__b: float
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
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)
Do you want to use a types.SimpleNamespace instead?
if you want to convert a dictionary to something that supports item lookup maybe use pydantic
sn = types.SimpleNamespace(**some_dict); sn.attr
Ah
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?
Forgot where I was
Yea, but it's nice to see that, thanks anyway 😄
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?
so all the fields only 'may be present' in fields query?
Yea - they are optional 😄
lemme check but im pretty sure what you have doesnt work
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)
😄
theres no way that ik of to do this without just redefining the entire thing
😦
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"]
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
Ah, if the actual cond depends on some runtime stuff, do I just make both branches list comps like fix error suggested?
how does hkt help here?
Hmm, it doesn't
Why not pass the class into the function?
if the two type_s are both compatible in the method you call like it is currently its fine, id need to see an actual example otherwise
The actual function looks like this https://github.com/Numerlor/Auto_Neutron/blob/4ec54dea1631ea25e2875b3201cbaefd74e47aa6/auto_neutron/windows/new_route_window.py#L315-L328
where the types are dataclasses that implement the classmethod
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]```
yeah thats fine
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
oh maybe you cant do that then
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.
so either var: type = None #type:ignore or give it an union with Any?
no var: type = Any
ah
I guess I could make everything a property that raises on accessing None but that'd be a lot of properties
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
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
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?
Yeah, but no type checker should see that change
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
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
I did test that already, and it all looked good 🙂
oh jesus 👀
I see what you did
And what would that be, Mr Fix? 😉
you know you could do this in one line with a protocol + type var
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
I did do the same thing when I needed the signatures at runtime for validation
def new_overload_decorator(function):
if SOME_SIGNALING_BOOLEAN:
return CUSTOM_BEHAVIOUR
else:
return DEFAULT_BEHAVIOUR
It's not like changing the default raising callable is a huge changer of what it does
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
also, can change it back after your module runs if for some reason others need the function
🙂
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
I use it here https://github.com/Numerlor/Auto_Neutron/blob/master/auto_neutron/settings/toml_settings.py to validate the called signature and get around type checkers complaining about parameters with names that don't match the overload
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"
Do you mean patter matching?
No, method overloaded
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
in a get on a generic dict, why is mypy expecting the second argument to be the value type?
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?
Use a proper global?
why do i get incompatible types here? (mypy)
A decorator @counted that returns a wrapped type defining .counter and .__call__()?
#type-hinting message
it should be strings_with_dicts: Union[List[str], List[Dict]]
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]]]")
it works with pyright, i'm not sure if that's a mypy bug or intentional behaviour
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
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
Is this on strict?
whatever the defaults are, just ran pyright .\auto_neutron\utils\recursive_default_dict.py
Thanks @brisk hedge and @grave fjord - I guess this falls in the category of "dynamically adding attributes == BAD". It makes me a little sad to see this aspect of Python's object model getting the heave-ho, though I do appreciate that it has been the source of many bugs in Python code.
I think it's clearer to use a global than mutate the function
or a class
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
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?
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
pyright
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
pycharm is bad
I wouldn't put much faith in pycharm
atleast the type checking parts of it
As a type checker, or as an editor?
the actual editor's nice
yep this is the main reason i stopped using it
I understand the gold standard is mypy, followed by... what?
i think the gold standard is pyright tbh
it gets such frequent updates and always supports the newest peps
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
PyCharm you mean?
Yes
I didn't realize it was so out of favor
actual type checkers are annoying to get right
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?
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
running them with the builtin terminal should be simple enough
Rockin, thanks
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
because tools like pyright and mypy exist, which help you statically verify types
they also do do things in the interpreter cause they still require evaluation assuming you dont have future annotations
really?
really
For metaclasses
def __call__(prototype: Type[T_Instance], *positional: Any, **keywords: Any) -> T_Instance:
...
Like that?
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]?
I don't think that's valid
One type variable can't be bound to another right?
It cannot no, you use Type[T_Instance] directly like in the 2nd-last code block.
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?
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.
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
ReadOnly: TypeAlias = typing.Annotated[T, ReadOnlyMarkerType]?
Or use Final?
Typing.Annotated or I had a hack from before Annotated was a thing (or it just wasn't supported not in typing_extensions for the python versions I used)...
Class name in quotes, typing_extensions.Self if your type checker supports it, or def f(cls: Self, ...) -> Self where Self = TypeVar('Self')
what does TypeVar 'Self' do
would it be "self"?
@classmethod
def f(cls: type[Self]) -> Self:
...
# or
def f(self: Self) -> Self:
...
Does anyone have any largescale examples of ParamSpec and Concatenate?
There's a proposal for an actual typing.Self. It's actually in typing_extensions right now
Works with pyright, not sure about mypy
(By largescale I mean any-scale cuz I can't seem to find many real life examples due to mypy support)
Not with mypy, there's a draft pr but it's not merged
Soon
Tm
I'm thinking about it
Classic 😔
Mypy PR sitting unreviewed gang
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
Lol
I hope TypeVarTuple gets accepted. ParamSpec is extremely limited when it comes to positional-only functions
Ya :(
I still want ParamSpecFrom that derives a paramspec from an existing function
And ProtocolFrom
I had started drafting proposals for typing-sig but never finished...
ProtocolFrom already has an issue open on the typing repo doesnt it?
I'll look
Can you not have a generic TypedDict?
Hmm, I fixed it by putting the generic first but I thought it always came last?
Nope unfortunately, i have wanted it though
Anybody has any tips for less verbose "overloads"/"cases"? https://paste.pythondiscord.com/reyoqaxima.properties
This is huge
some of them can be combined together I think? (depends on what you're using this for)
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?
Yeah haha I change like one key, there are some that are mutually exclusive which cause me to repeat the whole thing
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 :)
Not really. I would have to do ```python
class _TypicalOptionData(TypedDict):
name: str
description: str
required: NotRequired[bool]
class RoleOptionData(_TypicalOptionData):
type: Literal[8]
class MentionableOptionData(_TypicalOptionData):
type: Literal[9]
And that's roughly the same amount of code^^^
Really lol? I just made that Any haha
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)
Ah, right yeah.
I wasn't planning on exposing that but yeah that's true.
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
I did that with CommandOptionChoiceData: ```python
class CommandOptionChoiceData(Generic[T], TypedDict):
name: str
value: T
Aaaaahh, that's good.
I really like that
(fwiw Union[int, float] is just float by pep 484 (https://www.python.org/dev/peps/pep-0484/#the-numeric-tower), though not really at runtime and users may think they are different)
Yeah I remember reading that, doesn't float have different (more) methods than int though?
Ah, well then I'll just stick with Union[int, float] - I don't win a lot with only float
yea
main.py:12: error: Generic TypedDict types not supported
oh oof
looks like mypy has missing support for this
meanwhile pyright is doing... whatever it does
Pyright uses the notation T@_Something to indicate what thing the typevar was bound to.
Right, but wouldn't it pick up Literal[4] since it is known what T is?
I thought it did, although I do remember some confusion from the errors where it's all a bunch of TypeVars
yep, my comment was more geared towards how T isn't being picked up when I supply it in
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)
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
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?
init subclass is implicitly a classmethod
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
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
actually nvm. It looks like a subclass registers itself with the parent class (https://stackoverflow.com/questions/3862310/how-to-find-all-the-subclasses-of-a-class-given-its-name/3862957), so I can just rely on this method
yoo pyright got some new type narrowing patterns 🎉
https://github.com/microsoft/pyright/releases/tag/1.1.203
Pyright gets better day by day
not sure why it wasnt in the pep to start off with tho
eric wrote the pep
hm, what pep are you talking about?
oh this reminds me, does the pyright playground always use the newest version
no
647(?) the one for type guards
I'm planning to add some settings soon
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
i meant i think the two form version should have been in the pep from the start
I guess it was too much to implement all at once
I just manually updated 👍
Is it possible to use ParamSpec in python 3.6 in a way which pyright would be fine with?
You can use the typing_extensions package
also... I'm pretty sure 3.6 doesn't receive security updates anymore 👀
thank you
well, it's not my project and the maintainer insist on compatibility...
but thanks, I'll check typing_extensions out
Maybe you could make a PR with the ability to select the library version? 🙂
I am extremely lazy as you can see
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
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
I think it's a big factor in why it is faster than mypy
I'd guess it's most probably because of vscode
mypy is probably a lot faster using pypy
not that much of an advantage to be in python apart from some of the basic framework
well, it makes it a bit harder to run in CI
because now you need to add node.js
ast module makes things easy
Actually, maybe I can deploy the github repo thing as a serverless function 
my cloud provider supports "serverless containers"
This sounds ridiculously cheap 🤔 what's wrong?
It's free?
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?
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
Oh damn
seems like too many 🤔 not sure if I'm using the stats correctly lol
they have their own query language... and I'm confused
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
@hollow sage Maybe you can do it with a curried function?
Curry what bit? This is what I came up with in the end if you're interested: https://github.com/python/mypy/issues/9003#issuecomment-1003675586
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)
Thats kinda what the Returns class i came up with is doing, but it does need to pass through the raw annotation at runtime because those are used by cattrs
$ 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?
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?
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
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?
yes
Wow
PyCharm's type checker really is trash
It can't even figure out a custom descriptor
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
Maybe you could cheat by creating a symlink
used to also be used for py 2 supporting libraries, right?
eg since they support py2, they'd define the type hints in the stubs
At this point you shouldn't be supporting py2
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

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.
- if this expected behavior?
- if no, where should I report it?
- 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 onrepr(annotation), byt maybe you have more robust suggestions?
This is the expected behaviour. inspect.signature, or any other function, cannot know what your module does or does not import. It just inspects the annotations of an object.
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
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
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)
D:
I don't know anything about odmantic, but None is not assignable to List[str]
does odmantic inspect annotations?
idk i think so
Just force people to be on 3.10+ ;)
how do i assign List[str] to optional?
3.11 *
Optional[List[str]]?
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]]
but i dont particularly understand this
Are you using from __future__ import annotations?
yes
I assume that breaks everything
o
3.10 resolves annotations with get_annotations by default
!d inspect.signature
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`...
Oh it's not by default lol
Yeah
I think just require the users to use string annotations then, the type checker should understand them
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]]
I can't, the project will be used to visit/inspect arbitrary libraries ^^
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
yeah I guess I'll just use ann.__name__ and repr(ann) to start, and we'll see if we can improve along the way
thanks for the help 🙂
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!!!
ah forgot about TYPE_CHECKING, that messes things up completely
Ones that use the syntax in typeshed but don't actually support it are even better
Yeah, tkinter.Event is like that.
but the runtime behaviour shattered my dreams
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
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
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
but i do actually have this https://github.com/Gobot1234/steam.py/blob/main/docs/extensions/annotations.py if you want to take a look it might be helpful
and maybe take a look at pyanalyse as i think it has actual support for this
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?
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
What do you mean by that?
How do I get a function to return a custom typing.Literal[]
Ah
Yeah, typing constructs aren't special in any way
so is ```py
def something() -> Any:
return typing.Literal[1,2,3,4]
What would be the return type of some_function() in this case?
[1,2,3,4] ?
it should be either a single element or a tuple
foo[a, b, c] actually just means foo[(a, b, c)]
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!
I'm pretty sure it needs to be a tuple, not a list
!e
import typing
names2 = typing.Literal[["grape", "melon", "banana"]]
print(names2)
@soft matrix :white_check_mark: Your eval job has completed with return code 0.
typing.Literal[['grape', 'melon', 'banana']]
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__)
@soft matrix :white_check_mark: Your eval job has completed with return code 0.
001 | (['grape', 'melon', 'banana'],)
002 | ('grape', 'melon', 'banana')
yep
So what is the solution?
you need to return a tuple not a list
I'm iterating a larger list to derive elements in the end, can I turn them into a tuple somehow?
yeah use the tuple constructor
Got it, thank you!
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]
Can you elaborate what you mean?
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 Just Googled TypeGuard looks like the best appoard to me.
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)
LGTM
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
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?
its got to have a type ignore in it anyway
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
@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}',
)
Why not just lambda _: "I don't need the value" or something?
I think I just prefer keeping the visitors as clean a possible
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
@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
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)
I'm happy with results. If anyone interesting to see it "production" code, I can post a snippet.
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?
just call it module.pyi?
wouldn't that create a stub for package.module instead of module
oh i see
just make a module.pyi file at the root of the project
and then include that in your setup.py/pyproject.toml
if this isnt a library you dont need to include it
Has anyone had any luck getting PyCharm to recognize custom descriptor behaviour?
I don't really keep high expectations for pycharm's typechecker tbh
Yeah 😐 I'm starting to get to that point. I'd figure I'd ask in case anyone had any bright ideas
To be clear
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?
btw, you don't have to have all of those imports on separate lines, you can just do
from typing import Any, Generic, Optional, etc...
😮 REALLY!?
XD I'm teasing
haha
Separate imports just feels more explicit to me, and, I prefer the look of it
That said, I've got the general idea right?
from typing import (
Any,
Generic,
Optional,
TypeVar,
Type,
overload
)
I usually import typing as t, although I haven't come up with a nice one for collections.abc
Other than the missing type annotations in the implementation of __get__() that looks alright
Every # type: list[int] is only a few keypresses away from # type: ignore, which I think is a positive
inb4 a black plugin that automatically adds # type: ignore at the end of each line
That doesn't mean anything to a type checker
I mean why not if you are taking the time to do all this?
if I'm sketching something, I also use types like (a -> b) which don't make sense to the type checker
what does that mean to you?
async def amap(aiter: "AsyncIterator[a]", fn: "a -> b") -> "AsyncIterator[b]":
that almost works with pyright doesnt it?
you just need brackets around the a
unless it was reverted
as opposed to ```py
A = TypeVar("A")
B = TypeVar("B")
async def amap(aiter: AsyncIterator[A], fn: Callable[[A], B]) -> AsyncIterator[B]:
haven't tried but theoretically yes
async def amap[A, B](aiter: AsyncIterator[A], fn: (A) -> B) -> AsyncIterator[B]: soon™️
