#type-hinting
1 messages · Page 54 of 1
you need to put self: T_ThisNodeType
Hmmmmmm...
Actually, things are even more complex
Since I don't need 'of exactly this type'. Basically, there are leaf and nonterminal nodes. There are two types of LeafNode (Text and Comment), but hundreds of different types of nonterminal
So self-type isn't 'type of myself', but rather 'TextNode if I am exactly of type TextNode', 'CommentNode if I am exactly of type CommentNode', else 'Element'
But that seems like something I can solve with an overload, no?
Yep. self is special when annotated, if the instance doesn't match then the checker acts as if that method is invalid.
@overload
@property
def firstSibling(self: TextNode) -> Undefined | TextNode:
...
@overload
@property
def firstSibling(self: CommentNode) -> Undefined | CommentNode:
...
@overload
@property
def firstSibling(self: TextNode) -> Undefined | TextNode:
...
@overload
@property
def firstSibling(self: ElementNode) -> Undefined | ElementNode:
...
So that should work.
XD Well shit
you could make a feature request for it
@property
def firstSibling(self: T_ThisNodeType) -> Undefined | T_ThisNodeType
assuming that TextNode, CommentNode and ElementNode all subclass Node
Again though, there's hundreds of different nodes broken into three categories
So if a given node is of type "DivElement", I want it to match all element classes and not match Text or Comment nodes
i might be misunderstanding, but do all the nodes subclass Node?
Yes, they do. To give you an example of the hierarchy
would this work? ```py
NodeT = TypeVar('NodeT', bound='Node')
class Node:
@property
def firstSibling(self: NodeT) -> NodeT | Undefined: ...
class TextNode(Node): ...
reveal_type(Node().firstSibling) # Node | Undefined
reveal_type(TextNode().firstSibling) # TextNode | Undefined
Nucleus —> Target —> Node —> Document
—> DocumentFragment
—> TextNode
—> CommentNode
—> Element —> RootElement
—> HeadElement
—> BodyElement
—> DivElement
—> FormElement
—> ParagraphElement
Using your strategy, this would occur, no? ```py
reveal_type(HTMLDivElement().firstSibling) # Undefined | HTMLDivElement
# (Should be any ElementNode)
that would be # HTMLDivElement | Undefined
Yeah sorry, just fixed
class Element(Node):
if TYPE_CHECKING: # assuming it isn't being overriden already
@property
def firstSibling(self) -> Element | Undefined: ...
Hmmmmm
This might be best served by reimplementing the function on each of the three base categories
firstSibling on a CommentNode will be Undefined | CommentNode
on a TextNode will be Undefined | TextNode
on a Element will be Undefined | Element
To be clear though — I just need to implement this once, and I can redefine the typing into the .pyi stub of each class without having to actually reimplement the logic?
for the first 2 this should work, unless you don't want this behaviour for Document and DocumentFragment as well
since property overloading isn't supported currently, you couldn't type this in the Node class alone
Documents can't have siblings 😄
from positron.ObjectModel.Auxiliary.CommentNode cimport CommentNode
from positron.ObjectModel.Auxiliary.TextNode cimport TextNode
from positron.ObjectModel.Bases.Element cimport Element
from typing import Type
from typing import TypeVar
from typing import Union
T_ThisType = TypeVar('T_ThisType', bound='Node')
T_CommentType = Union[Type[CommentNode] | CommentNode]
T_ElementType = Union[Type[Element] | Element]
T_TextType = Union[Type[TextNode] | TextNode]
class Node:
@overload
def firstSiblingOfType(self, type: T_CommentType) -> Undefined | CommentNode:
...
@overload
def firstSiblingOfType(self, type: T_ElementType) -> Undefined | Element:
...
@overload
def firstSiblingOfType(self, type: T_TextType) -> Undefined | TextNode:
...
def firstSiblingOfType(self, type: Type[Node] | Node) -> Undefined | Node:
...
@property
def firstSiblingNode(self) -> Undefined | Node:
...
@property
def firstSibling(self: T_ThisType) -> Undefined | T_ThisType:
...
This aughta do it. And I'll just reimplement firstSibling on the Element class
no, those libraries are for parsing JSON/YAML/whatever into a typed structure
I meant Rust's serde library btw, not the pypi package
Rust's serde is a library for parsing data from different formats. It has the same core (looks like this) ```rs
#[derive(Debug, Deserialize)]
pub enum UpdateBody {
#[serde(rename = "message")]
NewMessage(Message),
#[serde(rename = "edited_message")]
EditedMessage(Message),
#[serde(rename = "chat_member")]
ChatMember(ChatMemberUpdated),
}
#[derive(Debug, Deserialize)]
pub struct ChatMemberUpdated {
pub old_chat_member: ChatMember,
pub new_chat_member: ChatMember,
}
With the following code, IDE complains that I try to access self.thing.specific_thing_param because self.thing is typed-hinted as Thing and doesn't realize it will always be SpecificThing. What do I need to change to get type hinting working here? ```py
class Thing:
def init(self):
self.thing_param = "thing"
class SpecificThing(Thing):
def init(self):
super().init()
self.specific_thing_param = "specific thing"
class ThingUser:
def init(self, thing: Thing):
self.thing = thing
class SpecificThingUser(ThingUser):
def init(self, thing: SpecificThing):
super().init(thing)
def do_specific_thing(self):
print(self.thing.specific_thing_param)
ig it's cos ur SpecificThingUser __init__ method passes thing which u hinted is a SpecificThing into ThingUser __init__ method, which hinted thing should be Thing
that's OK though, SpecificThing is a subclass of Thing, and it doesn't complain about that part
where does it complain then?
the last line
oh
okay
tat's strange, i dun c anything wrong honestly, maybe i ma just bad
wait amin
strictly speaking there is nothing wrong with the code, just my IDE is complaining and I can't use autocomplete properly because it thinks self.thing will always be Thing, even though in SpecificThingUser it will always be SpecificThing
maybe do tiz:
if type(self.thing) is SpecificThing: print(self.thing.specific_thing_param)
honestly tho, the thing, specific thing n specific thing param is making my head spin lmaooo
What you need is a typevar and generic.
yeah, I figured, but I don't know how to set generics up for a whole class like this
from typing import TypeVar, Generic
ThingT = TypeVar('ThingT', bound=Thing)
class ThingUser(Generic[ThingT]):
def __init__(self, thing: ThingT):
self.thing = thing
class SpecificThingUser(ThingUser[SpecificThing]):
def __init__(self, thing: SpecificThing):
super().__init__(thing)
def do_specific_thing(self):
print(self.thing.specific_thing_param)
bound means the TypeVar accepts it and subclasses of it only. Then inheriting from Generic makes the class generic, so in the body the variable is scoped to the class.
Yeah, this seems to work great. Thanks!
@pastel egret using beartype with multiple dispatch doesnt seem to work. how should i fix it??
@overload? Not implemented yet.
sad. i wonder how long it would take
Well you could watch this issue: https://github.com/beartype/beartype/issues/54
ya u told me b4
Then your typehint is wrong
Show your code?
it's already been resolved though
I don't think you necessarily need generics here
code here if you're interested
Ooh
I think generics are what I needed to document the intended use
I think you just need:
class SpecificThingUser(ThingUser):
thing: SpecificThing
oh, as a class scoped name?
Well it's not class scoped unless you use attr: ClassVar[T]
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
so it depends really do you often use ThingUser with ad-hoc SpecificThings ?
btw if you do use the generic you don't need the __init__ https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=42be889f48cf4173d42310233fc09c04
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
hey look it's a soundness bug in mypy: https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=e0b7be103f628ca7c856caf2927b06ee
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
class Thing:
def __init__(self) -> None:
self.thing_param = "thing"
class SpecificThing(Thing):
def __init__(self) -> None:
super().__init__()
self.specific_thing_param = "specific thing"
class ThingUser:
def __init__(self, thing: Thing):
self.thing = thing
class SpecificThingUser(ThingUser):
thing: SpecificThing
def do_specific_thing(self) -> None:
print(self.thing.specific_thing_param)
v = SpecificThingUser(Thing())
reveal_type(v.thing)
in my actual use-case the base classes are abstract
I have multiple "specific things"
Is this a standard way of "overriding" typing in a derived class?
?
well it should be detected as a violation of liskov substitution
but it seems as though it's not
eg class ThingUser: assumes it can call self.thing: Thing = thing
I mean should I be using the class annotation as opposed to generics
I don't know, I was just giving you an alternative
Generics are probably more flexible, since then any methods you add could also be type hinted correctly.
I'll stick with generics then. Now if only pycharm could handle @asynccontextmanager properly...
Does it work for contextlib2.asynccontextmanager ?
I got it working by overloading the unstructure_attrs_asdict, although this only works for the Converter class, couldn't find a solution for GenConverter as that actually generates the code on the fly.
from typing import Generic, TypeVar
T = TypeVar('T', bytes, int)
class CmpxchgResult(Generic[T]):
def __init__(self, success: bool, expected: T):
self.success: bool = success
self.expected: T = expected
def __repr__(self) -> str:
return f"{self.__class__.__name__}(success={self.success}, " \
f"expected={self.expected})"
def __bool__(self) -> bool:
return self.success
def __iter__(self):
return iter((self.success, self.expected))
if __name__ == "__main__":
ok, exp = CmpxchgResult(True, 5)
is there any way to make it so that ok and exp's types can be deduced?
It's got slightly newer type annotations
i knew there were adapters, but i didn't realize it was "rust syntax", i thought it was some binary thing. that's really interesting
i think the best you can do is Union, iirc this is an open issue in mypy (and probably also pyright et al)
that was what i've had to do in the past, unfortunately... can you subclass from typing.NamedTuple?
Yes… why?
frustratingly close https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=906063faadba8b0a6f9882090db57705
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=2a7719d1ad5dc5495a0efa61d8ec5c60 i made it give a different error if that helps
wait, can i just straight up use a named tuple?
i mean, that's what this is
it just doesn't know how to handle the generic
pyright explicitly doesn't support this
No configuration file found.
No pyproject.toml file found.
Assuming Python platform Linux
Searching for source files
Found 1 source file
/home/runner/PyrightPlayground/pyright.py
/home/runner/PyrightPlayground/pyright.py:5:7 - error: Multiple inheritance with NamedTuple is not supported (reportGeneralTypeIssues)
/home/runner/PyrightPlayground/pyright.py:20:13 - info: Type of "ok" is "bool"
/home/runner/PyrightPlayground/pyright.py:21:13 - info: Type of "exp" is "int"
1 error, 0 warnings, 2 infos
Completed in 2.935sec
(thanks for the pyright playground @trim tangle !)
hmm, in general Python doesn't like multiple inheritance of builtin types (idk if that error is pyright's issues tho)
like they explicitly warn you against multiple inheritance with exceptions
weird
i wonder if that's because of how Exception.__init__ is implemented
and because of weirdness in the except clause
a while back i implemented an exception in C how CPython does it (they have their own Exception_HEAD thing), and @runic tapir said it would break with multiple inheritance because some mro reason
but i forget what it was :/
but yeah it was __init__
basically just didn't do super().__init__() (and it's not like they couldn't have added it, so idk maybe they were feeling lazy)
lmao did you actually use that?
i just did!
that's where i got the pyright output i posted above
So I know this in a function like this
T_Something = TypeVar('T_Something')
def someFunc(inputType: T_Something) -> T_Something:
...
I've specified that a certain type of thing will coming in, and I can expect that same type of thing coming out
How do I put constraints on that T_Something can be?
Where T_Something can be one of three types
Oh
Is it
T_Something = TypeVar('T_Something', TypeA, TypeB, TypeC)
Where I assume those types can themselves be generics?
i think that should work
How do I specify I want a class but not one of its subtypes?
Make it invariant
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
Basically, what I want is to break nodes into three categories
So if I send in the type Type[NodeA] or any of it subclasses, or, an instance of NodeA or any of its subclasses, the return will be NodeA
But if I send in NodeA_1 or NodeA_2, I want the return type to be NodeA
Well in that case just don't use the type variable?
I don't understand what you mean with this
T_NodeType = TypeVar('T_NodeType', bound='NodeA | NodeB | NodeC')
def function(type: Type[T_NodeType] | T_NodeType) -> T_NodeType:
...
And I do beg your pardon because I could be missing something really basic. Typing is hard XD
The way its set up now, if I send in NodeA_1, I get back NodeA_1. I want to get back the supertype NodeA
That's what's happening with the overloads, the last non-decorated function is actually only for the function itself. The user will only see the overloads
Is it not? I am still quite confused
Yes, and I misspoke early by posting the overloads at all. What I'm trying to do is express this operation without the overloads
Just to see if its possible
So NodeA_1 is a subclass of NodeA?
So I'm trying to rig up something like this:
NodeA, NodeA_1, NodeA_2 —> NodeA
NodeB, NodeB_2, NodeB_2 —> NodeB
NodeC, NodeC_1, NodeC_2 —> NodeC
XD Oh yeah, its supposed to be
Would I perhaps have a second typevar bound to T_NodeType, but specified as being not-a-subtype?
I've been thinking about this but I can't come up with any way to do this
Because by definition, when you tell the type checker that you will accept and return the same type that will fundementally cause this behaviour
Well at least this means I'm not crazy for having trouble 🙂
Do you think I should fill a pull request though?
Seems like a useful behaviour
Pull request where?
That was going to be my second question
In any case, I only have the three types. I have a Node class which can have three types of children, CommentNodes, TextNodes, and other Elements. I have a function 'getChildrenOfType', and for convenience I want to be able to supply either the 'CommentNode' type or an instance of CommentNode and only get CommentNode children
And the same for Elements (of which there are hundreds of different classes) and TextNodes
Plus, to keep things simple, I'm going to let you pass in str (the type) or '' to specify text nodes and bytes (the type) or a bytes object to specify comments
Just because if you've got an element at hand and you want to grab its text nodes, it might be cumbersome to scroll to the top of the file and import TextNode
What kind of behaviour were you gonna implement?
Because there's many ways to accomplish this (SuperType[...] annotation, if-statements inside annotations??).
Right - the type variable how you have it setup works nicely with this in mind
You mean the TypeVar T_NodeType? That doesn't work
I need the overloads to get the desired behaviour — which as you say does the job just fine
What do you want the return type to be for this? Element, or whatever class supplied?
Lemme see if I can specify this functionally
@overload
def getChildrenOfType(category: Type[CommentNode] | CommentNode) -> CommentNode:
...
@overload
def getChildrenOfType(category: Literal[bytes] | bytes) -> CommentNode:
...
@overload
def getChildrenOfType(category: Type[TextNode] | TextNode) -> TextNode:
...
@overload
def getChildrenOfType(category: Literal[str] | str) -> TextNode:
...
@overload
def getChildrenOfType(category: Type[Element] | Element) -> Element:
...
def getChildrenOfType(category: Type[Node] | Node) -> Node:
...
So here if I send in a class object of type or inheriting from CommentNode, or, an instance of CommentNode, I'll get all the children of the Node which are commentnodes
If I send in the class bytes (but not any user defined subclasses of it), or, a bytes object, I'll get all the children of the Node which are of type CommentNode
(why Literal[bytes] and not type[bytes] ?)
Meh, just seemed prudent. I guess there's no real reason for it other than I consider my users to be psychopaths trying to get me at every turn
Which, now that I think of it, calls for more redundance and not less. So yeah, Type, not Literal
this channel will love my newest project
In [1]: from gradual_untyping import untype
In [2]: print(untype("""
...: async def f(a: int, b: list[
...: list[str]
...: ], c) -> None:
...: x: int = "a"
...: """))
async def f(a, b, c):
x = "a"
no need to thank me
"don't you miss the good old python 2.6 days? when everything quacked in ascii?"
I'm honestly not sure what I'm looking at?
you're looking at typed code going in and untyped code coming out
yeah, salt
no, i'm a rock
"Its not just a boudler... sniff... Its a rock" 😂
One last bit of input from you guys would be nice
If I'm accessing a text node and I want to get its elemental siblings (using siblingsOfType(), similar to childrenOfType), and I don't have an element or the Element class handy
What's a good standin? Like, you can supply getSiblingsOfType('') to specify text nodes
What shorthand could I do for elements
getSiblingsOfType(object), maybe?
meaning, you want siblings of the same type as the current node?
otherwise how about an enum of valid type identifiers?
e.g. nodeType.ELEMENT or something like that
No, siblings specifically of type Element. You can use the adjacent 'siblings' property to get siblings of the same type
But attaching an emun to the base node class is brilliant!
Not just a hack rack there, Salt
Also, a lamp (which may be why you're always getting those bright ideas ;))
elementalSiblings = someTextNode.getSiblingsOfType(someTextNode.T_Element)
Works for me! 😄
Hi there, can I somehow give hints to the linter about dynamically defined methods? (in my case pylint but i'm willing to replace it)
There are some tools in the typing library you can use
Can't really suggest anything without knowing the situation though 😛 Why don't you give us an example
let me quickly create one 🙂
some context: I'll have to mock many functions for whole test cases, so I'd like to just list the functions to mock in an array, and then enjoy it
class A:
functions = []
def __init__(self):
for fn in self.functions:
mock_fn = lambda _: None
setattr(self, fn, mock_fn)
class B(A):
functions = [
"add",
"sub"
]
def test_example(self):
# no-member error from pylint
self.add()
self.sub()
Hmmmmm
Two secs
Well, the way to type function as if it were a value is
func : Callable[..., RETURN_TYPE]
So the Callable generic accepts any number of types as arguments, and then a return type
For a function that takes no arguments and returns nothing, you would no
funcNoReturn : Callable[typing.NoReturn] or
funcNoReturn : Callable[None]
A function that takes two numbers and returns a number is
mathOpFunc : Callable[int | float, int | float, int | float]
I explain this base stuff because typehints generated at runtime mean mean nothing — a static type checker doesn't run anything
So your container need to have some idea of what's going inside it before you run
And please, anyone more experienced, just in any time
So anyway, if you have a list that will contain math operations which take in two numbers and return one, it'd be
functions : List[Callable[T_Number, T_Number, T_Number]] where T_Number = Union[int, float]
thank you, very helpful.
I think the only thing I don't know now, is that these functions won't be defined in an array but as a field in the class using setattr so is do you know any way to solve this issue?
but probably if i'd put the functions in a dict, my issues would be resolved
Yeah, that would be good
As far as I know there is no way to type that an object can have an arbitrary set of variables of type whatever
So if you know the names and number of them beforehand you just type them
class Thing():
dynamicFuncA : None | Callable[Arg1, Arg2, ReturnType]
dynamicFuncB : None | Callable[Arg1, Arg2, ReturnType]
dynamicFuncC : None | Callable[Arg1, Arg2, ReturnType]
This way the static checker knows that these attributes might be callables, but at any given time they might also be None
A dictionary is probably a bit more elegant though
You could even turn your object into a dictionary
class MyThing(Dict[SOME_KEY_TYPE, Callable[Arg1, Arg2, ReturnType]]):
someInstanceAttributeA : SOME_TYPE
someInstanceAttributeB : SOME_TYPE
someInstanceAttributeC : SOME_TYPE
and still access the attrs like MyThing.someInstanceAttributeA ?
So the functions are contained by the object, but are not attributes of it. There miiiiiiiiiight be some shenangigans you can pull to pipe the static typing through a modified __getattr__ which simultaneously looked up attributes from inside the dict portion of the object, and from its normal attributes
Lemme try something
from typing import Callable
from typing import Dict
class Thing(Dict[str, Callable[str, str]]):
def __getattribute__(self, attribute: str) —> SOMETHING:
if attribute in self:
return self[attribute]
return super().__getattribute__(attribute)
What you're looking at is something like this
I'm not sure what 'SOMETHING' would be though
thank you so much, I think I can try to figure this out on my own
I'll share my results later 🙂
Yeahhhhh... it looks like for __getattr__, the default return type is 'Any' — and this makes sense because __getattr__ has no way of knowing what's going to be coming out
And there's no way to specify based on the value of the attribute string that goes in what comes out, other than with literals
@overload
def __getattr__(self, attribute: Literal["some-predefined-string"]) -> Some_Known_Return_Type:
...
def __getattr__(self, attribute: ) -> Any:
...
@chrome thicket @indigo locust actually, Union[float, int] is the same as float in this case
because int is kind of a subtype of float
(only in type hints)
yeah
Oh good to know! Thanks
tyvm
well, not perfect but does not show errors, and the test will fail anyway if the function name is mistyped.
sadly, for some reason, in test_example my vscode does not realize that anything in MockStorage should be a MagickMock instance 🤷♂️
(btw if anyone else knows a lib that solves my issue is perfect as well, i don't want to reinvent the wheel)
class ViewServiceTestCase(TestCase):
functions_to_mock: List[str]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
test_functions = list(filter(lambda x: x.startswith("test_"), dir(self)))
for function_location in self.functions_to_mock:
_, __, function_name = function_location.rpartition(".")
mock_fn = MagicMock()
setattr(self.MockStorage, function_name, mock_fn)
for test_function in test_functions:
setattr(self, test_function, patch(function_location, mock_fn)(getattr(self, test_function)))
@property
def mocks(self):
return self.MockStorage
class MockStorage(Dict[str, MagicMock]):
def __getattribute__(self, attribute: str) -> MagicMock:
if attribute in self:
return self[attribute]
raise ValueError("will figure this out")
class MeetingViewServiceTestCase(ViewServiceTestCase):
functions_to_mock = ["hermes.services.meeting.MeetingService.create_zoom_meeting"]
def test_example(self):
MeetingViewService.test()
self.mocks.create_zoom_meeting.assert_called_once() # OK: does not show no-member
self.mocks.this_should_raise_no_member() # NOT OK: does not show no-member
meta-programming in tests...aaaaaaa
Your def mocks doesn't have a type annotation
so today I found this 😄 https://github.com/python/mypy/issues/11492
we're done guys, deleting this channel
don't worry if you need to fix your code, i got you
don't forget #type-theory-discussion aka #internals-and-peps
lmao
If I have a function that raises a NotImplemented error
Do I type its return as NoReturn or NotImplemented?
NoReturn, since it always raises an exception and doesn't return to the caller.
The point of the hint is that type checkers can know that like raise/return/break/continue, execution stops and they don't need to include that branch in type unions.
If I wanna write code for like python 3.2, but still want type hints, can I put them in a .pyi file and remove them from my code? And then python will ignore them but type checkers with support will see and use them?
i think so?
not sure if you can find a type checker that supports 3.2 still tbh
if ever
I don’t need the type checker to support 3.2
I want my code to support 3.2
(Well, I want it to support all of py3 so I can set the package tag to py3 rather than doing py36.py37.py38…)
Well, there's really no point supporting before 3.6, those are all no longer supported at all.
The typing module backport on PyPi only supported 3.4+.
if that's your only reason i would seriously rethink why
IMO you will have a harder time supporting 3.0 to 3.3 at least (if not 3.4) than you would supporting 2.7 😛
Well, my code doesn’t have anything tying it to a higher version of Python, other than using f-strings and typing
So I figured it would be nice to support all of py3 🙂
sometimes i get confused on what types to put
like here ,
async def cog_command_error(self, ctx, error) -> str:
return await ctx.send(embed=discord.Embed(title='oh no an error occured',description=error))
```
The documentation shows the types for the arguments
i meant like returning and errors
As for the return type, it's a Message object in this case since that's what send returns
Also documented
What about the errors? The type is documented
oh alright.
It's a CommandError
also is their a chart with all the types i can use in the typing module
I don't know of a chart for that module, but I know collections.abc does have a table in its docs.
collections.abc ?
!d collections.abc
New in version 3.3: Formerly, this module was part of the collections module.
Source code: Lib/_collections_abc.py
This module provides abstract base classes that can be used to test whether a class provides a particular interface; for example, whether it is hashable or whether it is a mapping.
An issubclass() or isinstance() test for an interface works in one of three ways.
- A newly written class can inherit directly from one of the abstract base classes. The class must supply the required abstract methods. The remaining mixin methods come from inheritance and can be overridden if desired. Other methods may be added as needed:
If you're on it 3.9+ you can use those types instead of their equivalents in the typing module, which are now deprecated
omg, thanks @grave fjord!
I've tried using self.MockStorage and it didn't work so I thought it doesn't matter, but now that I've added the MockStorage to the return type it highlights!
That's odd it shouldn't work
do you mean that
@property
def mocks(self) -> "MockStorage":
return self.MockStorage
this should not result in self.mocks.create_zoom_meeting recognized as a MagickMock function?
class DatabaseManager:
def __init__(self, db_name: str, engine_type: str):
if engine_type in ("sqlite3", "sqlite"):
self.sql_engine: SQLEngine = SQLiteManager(db_name)
elif engine_type == "mysql":
self.sql_engine: SQLEngine = MySQLManager(db_name)
elif engine_type == "postgresql":
pass```
i'm trying to wrap my head around how i can type this instance attribute, as-is mypy will tell me i've already defined it
i know i can just toss the type hint away in the mysql if statement & get mypy to stop whining, but it looks ugly and inconsistent
couldn't you specify it once as an attribute? something like
class DatabaseManager:
sql_engine: SQLEngine
def __init__(...):
...
Why don't you attach a sql_engine in the postgresql case?
not implemented yet, was just there for skeleton
ah yes
👍
@crimson raft I like to use a function: https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=ae3b3f1267c92505bde1fe8549415cd1
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 actually realized it was a bit odd as well and i put a raise NotImplementedError("PostgreSQL is not supported yet.") in its place
from typing import TypeVar, Callable
Callback = Callable[['SlashCommand', 'int'], str]
CallbackT = TypeVar('CallbackT', bound=Callback)
def deco(fn: CallbackT) -> CallbackT: ...
class SlashCommand:
@deco
def foo(self, a: int) -> str: ...
class B(SlashCommand):
@deco
# mypy: error: Argument 1 to "deco" has incompatible type "Callable[[B, int], str]"; expected "Callable[[SlashCommand, int], str]"
def bar(self, a: int) -> str: ...```pyright doesn't give an error for this, is this a mypy bug?
after updating mypy the error seems to be changed to error: Value of type variable "CallbackT" of "deco" cannot be "Callable[[B, int], str]"
hm?
Yes I ran into this issue myself, Eric himself told me hold on.
Wait no they were contravariant
My memory fails me yet again
right, they are contravariant in the arguments
Callable[[B, int], str] is not a subtype of Callable[[SlashCommand, int], str] because functions are contravariant in their argument types. So B.bar doesn't satisfy the TypeVar constraint
What you can do is: ```py
from typing import Callable, TypeVar
T = TypeVar("T", bound="Bar")
Callback = Callable[[T, 'int'], str]
def deco(fn: Callback[T]) -> Callback[T]:
return fn
class Bar:
@deco
def fizz(self, buzz: int) -> str:
return str(buzz)
class Baz(Bar):
@deco
def quack(self, duck: int) -> str:
return str(duck)
.wa contravariant
wikipedia's is good
Thanks!
Need help with pylance (pyright?)
I have this code
class MyClass():
if TYPE_CHECKING:
pool: asyncpg.Pool
async def start(self):
# ...
self.pool = await asyncpg.create_pool(**db_info) # I get pylance error here
assert self.pool is not None # Does not work
assert isinstance(self.pool, asyncpg.Pool) # Does not work
The thing is, the create_pool function returns either Pool, or None. So pylance screams at me.
The error I get is,
Expression of type
Pool | Nonecannot be assigned to member pool of MyClass
Shouldn't the assert prevent pylance from "erroring" at it?
Sorry for this noob question. Kinda new to type hinting stuff 😅
I think it wants you to assert to a local variable first, then narrow it, then put it in the instance variable
So something like pool = await create_pool(); assert pool is not None; self.pool = pool
but shouldnt asserting the instance variable directly also work?
No, because by that time the mistyped variable is already there
You could do try: self.start() except: pass and now you have None in your self.pool even though the type doesn't allow it
hmm
Any python class gurus who can help in #help-popcorn with some dynamic inheritance?
So the only way to stop it from erroring is by defining a local var first?
That's the best way, of course you can also type: ignore or cast
what does cast do
lol. Alright
The right thing would be to create a local and check the local. As Jelle said, you can end up in an inconsistent state like this.
Yeah thats exactly what I did
However, I face a new issue
so, I have a class Bot. It has a variable, called user. It can be of type User or None. Its None, when bot is not logged in. Now, I use bot.user inside some functions and these functions are called by the bot itself, implying bot is actually logged in, and that bot.user is not None. However, I havent figured out how to tell that to pylance automatically. So for now, I just put a assert bot.user is not None at the top of the function lol. I don't like doing this tho
i'm back with another apparent random mypy issue after leaving my vsc for hours
@overload
def execute(self, query: str, *args, fetch_all: Literal[True] = True) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[False]) -> SQLResult:
...
def execute(
self, query: str, *args, fetch_all: bool = True
) -> SQLResults | SQLResult:
with self.connection as cursor:
result: Cursor = cursor.execute(query, args)
if fetch_all:
return result.fetchall()
return result.fetchone()```
this is the code, the error starts at the first overload
@abstractmethod
def execute(
self, query: str, *args, fetch_all: bool = True
) -> SQLResults | SQLResult:
pass```
this is from the base class
had no errors with mypy until now and i haven't changed this method in quite some time, i have no earthly idea why i'm getting this error now or what to even do to fix it 
use isinstance(obj, type) to type guard methods that rely on the existence of user being a Bot type
hmm alright
i might just give in and create two separate methods for this instead, i don't know
i'm getting too many issues with this snippet, randomly it seems (i have literally not touched this in about a week and mypy now does not enjoy it)
if anyone has an idea, ping me 👍
I would make 2 methods here even if type hints didn't exist
it is settled then
@crimson raft What if you do this? ```py
@overload
def execute(self, query: str, *args) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[True]) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[False]) -> SQLResult:
...
? Or thispy
@overload
def execute(self, query: str, *args) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[True]) -> SQLResults:
...
@overload
def execute(self, query: str, *args, fetch_all: Literal[False]) -> SQLResult:
...
@overload
def execute(self, query: str, *args, fetch_all: bool) -> SQLResult | SQLResults:
...
Yeah there isn't really a better way to handle this. Perhaps you could reorganize your code so the variable isn't Optional
even if those worked i wouldn't want to ever have to write that
lmao
I don't think I can actually do that. Since its kinda internal library code
i'll just write 2 separate methods
You can turn it off, but that will turn off all nullability checks. Which would be a nightmare
Yeah
sometimes
I mean, CPython is written in C
it also works
mostly, of course
and sometimes, as Jelle pointed out
So... I was thinking of writing a toy type checker. Mainly in Python, but some parts could be ported to a Rust extension for performance. Has anyone tried that? Sounds like a really big accomplishment even to do the very basic stuff.
I wrote my own type checker, it's really not that bad. It's also what we mostly use internally at Quora: https://github.com/quora/pyanalyze
isinstance isn't preferred here to let the type checker know the type has been narrowed for their issue?
But how hard it is I guess also depends on how much of the type system you want to implement strictly
I've so far gotten away with a very simple implementation of TypeVars, for example
It's fine, it's the preferred way actually. There are alternatives like TypeGuard
👍 thought so
But you see, the error comes from you assigning it to the attribute before you know that it is not None.
huh
Yeah. I understand that now
Ideally I would implement my own type system of course 😉
well, not really
but I wanted to implement some basic stuff, and then add some experimental features to just play around with
That's what I ended up doing, though I'm trying to get closer to the standard type system 🙂
I mainly wanted to implement Forall. Something like ```py
identity: Forall[[T], Callable[[T], T]] = lambda x: x
not sure if it's possible with mypy plugins
It would also be cool to write plugins in some kind of declarative DSL without touching the library internals
That's what stub files are 🙂
Though obviously they're not as powerful as plugins
But if we find certain patterns that are common in plugins we can promote them to syntax that can be used in stubs
well... they're not plugins, they're stub files
you can't describe dataclasses with a stub
yeah I've seen this
I guess it is a necessary hack until we have some kind of official plugin spec
i do wish the types were annotated inline, but I think most people don't
assuming there's a way to generate the stdlib stub files so they don't have to be manually kept in sync, it'd also be cool to know where the type information is ultimately coming from
As a frequent efftwelver, I do wish the same
you go deep into some library and you're like... wtf is everything?
I think type annotations were the static typing red pill for me
I'm often using editors that aren't even smart enough for that, so grep ends up being my F12 😄
does it sometimes navigates to the stub file instead of the real implementation?
yeah, and sometimes it doesn't
and sometimes it's like cannot navigate to the symbol at caret 🥲
Hi, I got a strange problem with type hint a class extends metaclass.
def get(cls,x:T)->T:
return x
m1 = type('Meta',(type,),{'__getitem__': lambda cls,x: x})
m2 = type('Meta',(type,),{'__getitem__': get})
class N1:
__metaclass__ = m1
class meta(type):
def __getitem__(cls,x:T)->T: return x
class N2(metaclass=meta):
...
N3 = type('N3',(),{'__metaclass__':m1})
N4 = type('N4',(),{'__metaclass__':m2})
x1 = N1[1]
x2 = N2[1] # fine
x3 = N3[1]
x4 = N4[1]
only N2[1] works and others will get Pylance's error Expected no type arguments for class "N_" .
I don’t know much about type hint or metaclass. Does anyone know the specific reason? Thanks.
Well, Python 3 no longer supports the __metaclass__ variable name, so that's just a regular class.
Also, calling type() to dynamically create a class isn't something that type checkers are likely to support, since it'd be really complicated to figure out what's happening.
Thank you for your answer.
Is that means I can not use type() function to create a class which extends a metaclass in Python 3?
You can do it perfectly fine, you just need to instead do metaclass= in the bases field.
And the issue is moreso that type checkers don't understand it.
They'll know that m1 and m2 are classes at least, but won't interpret the __getitem__ or anything
Sounds reasonable
Here's the stub in typeshed: https://github.com/python/typeshed/blob/master/stdlib/builtins.pyi#L142
stdlib/builtins.pyi line 142
class type(object):```
If your code is using type() to make classes, you'll probably want to "insulate" that inside a function, then throw casts/type-ignores around so the untypable nature is contained there.
Fortunately, I just happened to encounter this problem and didn't really want to do it that way. The work flow of the type function is may too complicated for me.👀
The standard class declaration is simpler and more practical
Yep, type() is useful for dynamically creating classes on the fly, where a type checker would have no clue what's going on.
i think this should be simple but i cant do it. how do i write a function that allows you to add any two objects with the same type that when added return the same type? i thought it would be something like this but this is very wrong
from typing import TypeVar, Generic
S = TypeVar("S")
def add_same_generic(left: Generic[S], right: Generic[S]) -> Generic[S]: return left + right # Variable "typing.Generic" is not valid as a type
because i assume Generic is a class that other classes inherit from, but im not sure what do to for my function
Generic is used only to inherit from, to indicate a class has type parameters (like dict[key, value]). You just need to use the typevar directly:
from typing import TypeVar
S = TypeVar("S")
def add_same_generic(left: S, right: S) -> S:
return left + right
Though, actually what you'd want to do is use a Protocol also, so you can restrict to only addable types:
from typing import TypeVar, Protocol
T = TypeVar('T')
class AddToSelf(Protocol):
def __add__(self: T, other: T) -> T: ...
SelfAddableT = TypeVar('SelfAddableT', bound=AddToSelf)
def add_same_generic(left: SelfAddableT, right: SelfAddableT) -> SelfAddableT:
return left + right
would Type[A] be the correct way to typehint the actual classes A/B/C here?
class A:
pass
class B(A):
pass
class C(A):
pass```
i.e. does Type[A] cover A and all potential children, or what if you wanted exactly A 
Type[T] is covariant so that should cover all of them
https://docs.python.org/3/library/typing.html#typing.Type has this example
class User: ...
class BasicUser(User): ...
class ProUser(User): ...
class TeamUser(User): ...
# Accepts User, BasicUser, ProUser, TeamUser, ...
def make_new_user(user_class: Type[User]) -> User:
# ...
return user_class()
ConfusedReptile linked same thing
#help-cake
But what is you wanted Type[User] but no children or only certain children 
i think(..?) that would violate LSP
oh that's very neat thank you, ill read that part of the docs
You can use a wrapper type to enforce invariance (i.e. no children allowed) but you can't selectively choose which ones to allow.
that errors cause Foo() is not compatible with type[Foo]
not sure why it shows up as Type[<nothing>]
well just Foo would work
true
/home/runner/PyrightPlayground/pyright.py
/home/runner/PyrightPlayground/pyright.py:7:5 - error: Argument of type "Foo" cannot be assigned to parameter "input" of type "Type[T@bam]" in function "bam"
Type "Foo" cannot be assigned to type "Type[T@bam]" (reportGeneralTypeIssues)
/home/runner/PyrightPlayground/pyright.py:8:5 - error: Argument of type "Bar" cannot be assigned to parameter "input" of type "Type[T@bam]" in function "bam"
Type "Bar" cannot be assigned to type "Type[T@bam]" (reportGeneralTypeIssues)
2 errors, 0 warnings, 0 infos
Completed in 13.368sec```pyright here makes much more sense
hey guys! anyone here using boto3 actively?
The <nothing> is there because it can't work out the generic type parameter
You need to actually have multiple uses of a type variable in a type hint
i feel like even though it isnt used it should still show up as something
like there isnt a warning that the T has no meaning
That's what the <nothing> is afaik
Or you mean there's no warning on the def, but there should be?
but you wouldnt know that without knowing mypy internals or going to the docs or whatever
yeah there wasnt a warning on the def
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
hmmm
pyright doesnt complain about this
Yeah there's a load of stuff where it's valid input but clearly redundant or misleading
how can I type hint a named tuple?
or should I just make my own class
SO says something that doesn't look like actual type hinting
Alternatively, a named tuple can be created from a regular class definition that inherits from tuple and that defines named fields. Such a class can be written by hand or it can be created with the factory function collections.namedtuple().
so i guess, yes?
yeah class it is
Use typing.NamedTuple, which uses variable annotations to specify the types and names of the attributes:
from typing import NamedTuple
class MyVec(NamedTuple):
x: float
y: float
z: float
At runtime this acts the same as regular namedtuple, with the advantage that you can define methods right there.
If you're not using the tuple behavior eg indexing and unpacking, use attrs.org or dataclasses
Imho
Is __optional_keys__ and __required_keys__ official?
It's implemented in typing_extensions https://github.com/python/typing/blob/master/typing_extensions/src_py3/typing_extensions.py#L1069-L1088
Yes, it's documented: https://docs.python.org/3.10/library/typing.html#typing.TypedDict
How do I use it? ```python
class MyDict(TypedDict):
optional_keys = ('optional',)
id: int
name: str
optional: list
Like this then?
There's no example of it from what I can see
You don't set it yourself, the implementation computes iit
Currently you can set total=False to make all keys optional, and use inheritance to get a TypedDict that's part optional and part required
There is an upcoming PEP to allow setting this per key (PEP 655)
So my example above will have to be ```python
class MyDictRequired(TypedDict):
id: int
name: str
class MyDictOptional(TypedDict, total=False):
optional: list
class MyDict(MyDictRequired, MyDictOptional):
...
pyright already supports that, I'll be releasing a new version of typing-extensions with runtime support for it soon
Yeah I've seen that, I wanted to start using it prematurely if possible
I think it can be a bit simpler, you can make MyDictOptional inherit from MyDictRequired and only have two classes
Ah right, still that's a bit of a bummer but I see.
Yeah it's definitely awkward
Sebastian merged Required/NotRequired into typing-extensions as we were talking here, I'll probably make a release for it later today 🙂
I was funnily just about to ask about a new release for that
from typing import Concatenate, ParamSpec, Generic, TypeVar, Any, Callable
_T = TypeVar('_T')
_O = TypeVar('_O')
_P = ParamSpec('_P')
class _callable_alru_cache(Generic[_P, _T]):
async def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _T:
...
class _callable_alru_cache_from_instance(_callable_alru_cache[Concatenate[_O, _P], _T]):
async def __call__(self, *args: _P.args, **kwargs: _P.kwargs) -> _T:
...
def foo(fn: Callable[_P, _T]) -> _callable_alru_cache_from_instance[Any, _P, _T]:
...
@foo
def abc(self, a: int) -> str: ...
abc(2) # Argument missing for parameter "a"
```this is a shortened example of what I'm trying to do, shouldn't the `Concatenate[_O, _P]` make it so the `self` parameter is ignored in this example?
-> "A"
how silly. Thanks
I guess it's a workaround. If A inherits from B, def copy(self) -> B: works just fine
you can also from __future__ import annotations on 3.9.7+ and all annotations will be converted to strings
you can also use typing_extensions.Self
if you care about people inheriting the class
i don't see anywhere in the pep for Concatenate that talks about it being used as a type argument, though pyright doesn't flag this and it doesn't seem to have an effect on _P
That's because A is not defined yet when you're in its body
yeah, I figured as much. But it's a common need so there had to be a way
there's from __future__ import annotations 🙂
so if you're not inspecting annotations (e.g. with pydantic) use that
defaulting it has been delayed to 3.11
Say I have a base typed-dict like this: ```python
class Base(TypedDict):
type: int
data: Any
Is there a way to narrow this down by certain values? ```python
class A(Base):
type = 1
data: str
class B(Base):
type = 2
data: int
So that if sometimes has a union like this Union[A, B] and does if var.type = 2 type-checkers can infer that var is B?
The second codeblock causes PyRight to complain: ```
TypedDict classes can contain only type annotations
@blazing nest If you're using pyright, you can do this:
class Base(TypedDict):
type: int
data: Any
@final
class A(Base):
type: Literal[1]
data: str
@final
class B(Base):
type: Literal[2]
data: int
although then you'll have to use Union[A, B] instead of Base because Python doesn't have sealed classes.
Ah, of course I forgot Literal. Is the @final necessary?
Is the
@finalnecessary?
Yes, see this issue https://github.com/microsoft/pyright/issues/1899
Oh right, thanks
I guess I'll have to scrap this anyways
I can't redefined type from int to Literal[1] ```
Type of TypedDict field "type" cannot be redefined
Type in parent class is "int" and type in child class is "Literal[1]"
Wait what.. even changing Base to Literal[1, 2] and Union[str, int] fails
Type of TypedDict field "type" cannot be redefined
Type in parent class is "Literal[1, 2]" and type in child class is "Literal[1]"
oh i c
MyPy also complains but just as a general issue ```
error: Overwriting TypedDict field "type" while extending
error: Overwriting TypedDict field "data" while extending
@blazing nest @soft matrix ```py
def f(thing: Base):
thing["type"] = 42069
a: A
f(a) # OOPS
you could just not include the type field in the base I guess
or... just not have the base at all
Yeah I suppose that could just be a Union of all the finished typed-dicts
I tried something similar a while back, apparently it's not type-safe?
Yeah Fix error showed why above
Oh I missed that 😅
I would prefer if I could just mark TypedDict as invariant though, like List (mutability messes with static typing). But yeah I can make it work
subtyping is a bit messy ye
yeah im pretty sure you can mix bases of typed dicts with pyright
if you type: ignore
oh wait that wouldnt work to make it immutable
Tada 🎉 ```python
from typing import TypedDict, Literal, final, Union
@final
class A(TypedDict):
type: Literal[1]
data: str
@final
class B(TypedDict):
type: Literal[2]
data: int
Base = Union[A, B]
I just ended up using a weird setup with dataclasses
ResultTypes = Literal["nothing-changed", "reformatted", "failed"]
@dataclasses.dataclass(frozen=True, init=False)
class FileResult:
type: ResultTypes
src: str
@dataclasses.dataclass(frozen=True)
class NothingChangedResult(FileResult):
type: Literal["nothing-changed"] = field(default="nothing-changed", init=False)
@dataclasses.dataclass(frozen=True)
class ReformattedResult(FileResult):
type: Literal["reformatted"] = field(default="reformatted", init=False)
dst: str
@dataclasses.dataclass(frozen=True)
class FailedResult(FileResult):
type: Literal["failed"] = field(default="failed", init=False)
error: str
message: str
pretty sketchy but it works 🤷
You might be able to use Final in some of those
I wonder if an immutable TypedDict type would be useful
:(
It's probably not necessary, namedtuples and frozen dataclasses work well enough
i think a general purpose mixin to make a mapping typed would be very useful
i have this horrific schema in a multidict currently cant really be typed which i think it would be nice to type
class AttributeInfo(TypedDict, MultiDict): # type: ignore
name: str
attribute_class: str
description_string: NotRequired[str]
description_format: str
hidden: Bools
effect_type: Literal["positive", "neutral", "negative"]
stored_as_integer: Bools
armory_desc: NotRequired[str]
```this is what i currently have but this only works with pyright
typing-extensions 4.0.0 has been released, enjoy Self, Required, and NotRequired
thanks Jelle
hell yeah
do you know if typing has any plans for something like this?
identity = ForAll[[T], Callable[[T], T]] = lambda x: x
Not that I know of
People do make occasional noises about typevar scoping being hard
So something concrete might come out of that
I was very disappointed when I realized I couldn't do ```py
@dataclass(frozen=True)
class Iso(Generic[A, B]):
left: Callable[[A], B]
right: Callable[[B], A]
def swap(tup: tuple[A, B]) -> tuple[B, A]:
return tup[1], tup[0]
swap_iso: Iso[tuple[A, B], tuple[B, A]] = Iso(swap, swap)
bumping this since i still haven't figured it out
What made it major version bump? Anything I should watch out for other than the fact that Python versions were dropped?
We decided that the previous versioning scheme was silly and switched to semver
We dropped a bunch of compatibility stuff, details in https://github.com/python/typing/blob/master/typing_extensions/CHANGELOG
You mean my typeshed_client library? No
I wasn't aware anyone else was even using it 😄 I'd be happy to add more of an interface if there's a use case
i was just exploring pypi a few days ago
actually just found a bug
it requires mypy-extensions but doesn't declare that
Oh it does, for NoReturn. Let me fix that
although NoReturn is in the stdlib by a certain version
yes that was back when typing was provisional and we got to just add stuff to bugfix releases
Not really 🙂
i hate importlib.metadata
so many packages require it with different versions
its a pain
New in version 3.8.
Changed in version 3.10: importlib.metadata is no longer provisional.
Source code: Lib/importlib/metadata/__init__.py
importlib.metadata is a library that provides for access to installed package metadata. Built in part on Python’s import system, this library intends to replace similar functionality in the entry point API and metadata API of pkg_resources. Along with importlib.resources in Python 3.7 and newer (backported as importlib_resources for older versions of Python), this can eliminate the need to use the older and less efficient pkg_resources package.
because i only support 3.8 and up on my projects, its awesome for me
use it to get the version of my own project
ah
i love how so much of typing is actually deprecated /s
why is this a thing?
couldn't an isinstance be used for this?
No that's the thing, Film is just dictionary.
!e ```py
from typing import TypedDict
class Film(TypedDict):
title: str
year: int
print(Film.class)```
@little hare :white_check_mark: Your eval job has completed with return code 0.
<class 'typing._TypedDictMeta'>
!e ```py
from typing import TypedDict
class Film(TypedDict):
title: str
year: int
print(Film().class)```
@little hare :white_check_mark: Your eval job has completed with return code 0.
<class 'dict'>
ah
otherwise how would is_typeddict actually work
although
unless it did duck typing
!e ```py
from typing import TypedDict
import typing
class Film(TypedDict):
title: str
year: int
print(typing.is_typeddict(Film()))```
@little hare :white_check_mark: Your eval job has completed with return code 0.
False
Lib/typing.py lines 1928 to 1939
def is_typeddict(tp):
"""Check if an annotation is a TypedDict class
For example::
class Film(TypedDict):
title: str
year: int
is_typeddict(Film) # => True
is_typeddict(Union[list, str]) # => False
"""
return isinstance(tp, _TypedDictMeta)```
but im pretty sure its just cause it would be difficult to add an instance as currently it just type errors
or thats the only reason i can think of
we probably could have made issubclass(Film, TypedDict) work with the right metaclass magic
I remember looking at the TypedDict implementation but yeah I see.
I think I miss-interpreted this line:
https://github.com/python/typing/blob/master/typing_extensions/src_py3/typing_extensions.py#L1092
typing_extensions/src_py3/typing_extensions.py line 1092
TypedDict = _TypedDictMeta('TypedDict', (dict,), {})```
the imports around typing back in those versions were painful 😄
although the fact typed namedtuples only gained additional method support in 3.6.2 was more than a little aggravating haha ^^ /lh
I'm pretty sure I was the one who implemented that 😄
I wouldn't be surprised if this is why we bumped black's minimum to 3.6.2 :p
No big deal in the long run as 3.6.0 is pretty old anyway but it was annoying
lmao
i still use 3.7 sometimes
on a project
written with tabs
why do i do that
Is it cash money?
|| 3.6 here
||
and barely typed at all
we still run 3.6 too. upgrading a hundred third-party dependencies is hard
I'm honestly already at the point where it'd be so nice to drop 3.6
no its a discord bot of a different user which was the first project in python i ever worked on
I better get on it soon though before people like you start dropping 3.6 support 😄
but I'm constrained to whatever black supports 😉
because the only Python OSS projects I maintain (that isn't black) are black development tools 😅
and its pipenv
I wish I had a useful open source project 
i legit thought jelle sent this
.gh repo ichard26/next-pr-number
Get the number the next pull request will be assigned for a public GitHub repository.
and was like, wtf
is probably one of my better OSS projects
how do people find open source projects to contribute to generally?
HEY BANDERSNATCH!!
...is bandersnatch a dev tool for black?
in my case, its basically: tldr @trim tangle what projects are you working on and any open issues?
??
Usually fixing bugs in stuff people use
i ask ppl what projects they're working on to find projects to contribute to
if it interests me i work on it
I wanted to contribute to pyright but it's inpenetrable 😦
They come in here or IRC and have problems and I go fix em
and other stuff doesn't have bugs 😄
i found this a few days ago and it creates very pretty graphs from profiles
also its written in rust
....and we're in #type-hinting, shit
yup
I mean, as long as the author doesn't have an encounter with a bus we're all good, right?
(let's just ignore the fact I've never used pyright)
Does pyright have more than one maintainer?
chirp
okay, it has 2 maintainers
!pip pyright
the wrapper is actually worth it IMO
it violates all rules to work
also
i've been experimenting with pytype
!pip pytype
OH, pyright is not a python application?
oh damn pytype doesn't support windows at all haha
yes its eric and eric
jakebailey has 258k additions and 227k deletions
eric found a way to clone themselves
pair programming, mood: me 😔
How can I type hint that a input str is either 'a' or 'b' or 'c'
Literal["a", "b", "c"]
fix errors pfp 🤣
pyright is actually quite good. And it looks like Eric works 40 hours a week on it, so bugs are fixed in no time
btw, guys, can anyone help me with testing type hinting for boto3? I need someone who actively uses boto3
oh hi I wrote the wrapper for pyright lol
what do you mean it violates all rules to work ahaha
I think pypi packages are supposed to contain predominantly Python
But there's some vague on topic/off topic rules pypi that are applied sparingly
Any point in trying to make a "typed mapping"?
hm?
According to this document: https://typing.readthedocs.io/en/latest/source/libraries.html#best-practices-for-inlined-types
Mapping is preferred over Dict but I have no choice if I want to provide per-key type information
i was actually thinking about this
you can
just its awful
Just overload getitem a tonne
Oh my-
But yeah I think a TypedMappingMixin would be nice
Yeah and then TypedDict could just be a shortcut (would be required for semver)
@blazing nest well, TypedDict is supposed to be for describing APIs that already exist
although I guess that does bring a bit of variance inconvenience
Variance is a pain along with typing types that don't implement dict but are mappings
Yeah asgi is a pain here
Would have been nice to have the types be Protocol instances with a recommended library of dataclasses
hi, nice!
before I explain, I like it, and I appreciate how is ensures pyright exists.
the rules it violates are basically using nodeenv and installing an npm package 😛 although that was mostly just a joke. there's not really another way to do it
last commit in 2012, that will go well 😄
Surely binding to deno or node can't be that hard
Yeah this actually seems like an alright idea
i couldn't find any node bindings the last time i checked
Thank you :)
Yeah I figured, just wanted to check to make sure it wasn't going to get banned somehow lol
nooooo it [likely] won't
idk what the pip rules are
but its different than anything i've seen on there before, in a good way
254 errors, 3 warnings, 0 infos
Completed in 12.491sec
I'm gonna go figure out how to configure pyright cya later
huh it actually found a few bugs
i uh
thanks i guess?
@@ -1180,6 +1180,22 @@ def get_features_used(node: Node) -> Set[Feature]: # noqa: C901
if argch.type in STARS:
features.add(feature)
+ elif n.type == syms.with_stmt:
+ atom = n.children[1]
+ if atom.children[0].type == token.LPAR and atom.children[-1].type == token.RPAR:
+ context_manager = atom.children[1]
+ if context_manager.type == syms.testlist_gexp:
+ # There's actually mutiple context managers within the parentheses.
+ for ctx in context_manager.children:
+ if ctx.type == syms.asexpr_test and ctx.children[-2].value == "as":
+ features.add(Feature.PARENTHESIZED_WITH)
+ break
+
+ elif context_manager.type == syms.asexpr_test:
+ # Just one parenthesized context manager.
+ if context_manager.children[-2].value == "as":
+ features.add(Feature.PARENTHESIZED_WITH)
+
# Python 2 only features (for its deprecation) except for integers, see above
elif n.type == syms.print_stmt:
features.add(Feature.PRINT_STMT)
nothing like writing syntax detection code with music in the background :)
Cool and it broke some tests 😅
parenthesized with is a bit problematic as a detection feature because it exists undocumented in 3.9
so I don't know if we should really emit if the target version is 3.9
and conversely, maybe we shouldn't go into 3.10 mode if we see it
Hmm yeah I don't mind, I just felt like we would eventually have to support it as it does look way cleaner so might as well add in the detection code now.
Yeah it's probably not the best-est of ideas as it would then enable the experimental pattern matching grammar
also we're in the wrong channel 😄
How do you typehint multiple returns? for example
return 'abc', 'def'
def f() -> tuple[str, str]:
Ok thanks!
Yeah the first time using pyright in a project you will probably see a lot of errors, pyright requires the use of certain patterns that other type checkers don't require
I think it should be fine, especially because I've gotten a thumbs up from Eric (the main contributor to pyright)
👍
yeah i like it and it caught a few bugs that were in some except statements.....
str has no attribute name....
i uh, yeah uh
Yeah, I really like pyright, it's also like ten times faster than mypy
Also if anyone needs a database client that plays nicely with type hints, look no further than https://github.com/RobertCraigie/prisma-client-py
tldr?
patterns?
You define models in a Prisma schema file, generate the client and then you can query like so: ```py
from prisma.models import User
user = await User.prisma().find_first(
where={
'name': Robert,
}
)
All models are standard pydantic models
You can read through https://prisma-client-py.readthedocs.io/en/latest/getting_started/quickstart/ for a better explanation
An auto-generated and fully type-safe database client
Yeah, so for example, pyright doesn't like conditional imports:
try:
from typing import TypedDict
except ImportError:
from typing_extensions import TypedDict
Must be written as:
import sys
if sys.version_info >= (3, 8):
from typing import TypedDict
else:
from typing_extensions import TypedDict
All models are standard pydantic models
😔
i'm not a big fan of pydantic
Why not?
Hmmm, I've never thought of pydantic as being too high level, you can customise basically everything
if some things can variably be turned off, that's what I want
i should add, somewhat easily
!pypi pydantic
seems like pydantic has had dataclass support for over 6 months, which is how i use attrs
https://pydantic-docs.helpmanual.io/usage/dataclasses/
Data validation and settings management using python 3.6 type hinting
Yeah that's not great but it's fairly active on GitHub
no no that was my reaction to this page's last edit
ie that feature has existed for at least 6 months
Ah I see
Anyway in terms of how pydantic integrates with my library, as a user you never even have to know that it uses pydantic under the hood
tldr i think i hate pydantic just because
You can subclass the models but can't add or remove fields at the moment however I do plan on changing that in the future
Fair enough, I really like it for the custom validation
attrs supports that too 😛
I think I hate it because.... well, I chose the wrong tool for the job
so my first time with it was a fight
Ah okay what did you try and make it work for?
but its also because there wasn't really a great tool for what I was doing, and the end result uses desert, attrs, and marshmallow
short answer: configuration system
long answer: configuration system with metadata about each value and lazy loading, plus defaults, all stored together, and its fully typed as well.
I should even be able to determine the source of where each configuration value came from, too
was it a default? .env? os.environ? toml? yaml?
Ah yeah that does sound like pydantic would not play well with that
pydantic was problematic for this, as there was no way to create an instance with..... one sec
You can create partial instances, there's a model.construct() function
oh thank god
I just checked something and was very glad that pydantic errored
otherwise.... I would be very annoyed at myself
from pydantic import BaseModel
class Thing(BaseModel):
great: int = 5
print(Thing.great)
if that worked, then it would basically be a configuration system with all of the defaults right there
What's wrong with just constructing the instance once to get the defaults?
iirc that didn't work because there's no sentinel to be used where None is ambiguous in pydantic
lol, that reply also works for constructing the instance once
a few values are required, but i don't want to define them as none
attrs has attr.NOTHING and marshmallow has marshmallow.missing
I don't know if it's exposed but pydantic makes use of an UNSET constant internally
it def doesn't work for this specific problem at this point in time
but i would not be opposed to using it for a program which doesn't have to expose a way for the user to modify its own configuration while running ;-;
Yeah I agree with that, I have no idea how lazy loading or storing where a value came from could be implemented using pydantic
all on lower levels 😛
~also i do not think i have lazy loading implemented, I think that was a buzzword~
Oh okay ahaha
Anyway the main reason I wrote the library was because I couldn't find a database client for python that had any real way for static type checking
true, typechecking is awesome
oh, i'm curious, is [tool.pyright] used officially?
or just by the wrapper?
ah
lmao
bad microsoft 
The wrapper doesn't actually read configuration from pyproject.toml files yet
but pyright itself does, yah?
Yeah
I thought mypy didn't like that either
I actually can't remember, I haven't really used mypy since I started using pyright
Isn't that just an ORM?
Otherwise the type hints don't mean much
Sorry I don't really understand what you mean
The type hints are for both queries and the returned models
But you still need to manually verify that they match the actual query and model, right?
Otherwise it is an ORM, which do exist
The queries are typed using auto-generated TypedDicts specific to your database schema, that's how the type hints can actually work
Ah, so they're autogenerated
Yeah that's the only reason it's possible
I'd say the main benefit is actually the autocomplete support that the auto-generated type hinting provides, just have a look at this gif https://github.com/RobertCraigie/prisma-client-py#what-is-prisma-client-python
we did merge pyproject.toml support into mypy a while ago. not sure if it's been released
No I mean try import vs sys.version_info
Oops, missed what you were replying to
I've only ever had trouble with try/import on mypy (but I've not tried it recently) and it's not supported by pyupgrade, and it makes coverage harder
it's not possible to type hint a lambda, is it
f: Callable[[], str] = lambda: "hi" ig
can't think of any context where you'd want to typehint a lambda
all classes are callable though
oh hmmm
calling the class gives you an instance
So I suppose the best I can do is either type or Type
Sadly I don't think the bounds will be very useful hmm
You don't need an intersection here
Hey. So I decided to use type hints more often and I have a variable that can be three different types. I looked up how to use type hinting with multiple types and they said I should do it like this:
def sum(a: Union[int, float], b: Union[int | float] -> Union[int | float]:
return a + b
But earlier today someone here said I could do:
def sum(a: int | float, b: int | float) -> int | float:
return a + b
Is there any difference between the two ways listed here?
yes, basically the supported python version
So which one is the most recent/newest way of doing this?
The | version is new in 3.10
internally, they both end up as typing.Union, but the second way is only supported on 3.10
although
(And usable in older versions with from __future__ import annotations
(its not)
That's not true, | produces types.Union
So | is basically just shorthand notation for a union?
Yes
yes
oh interesting
Ok cool. Wouldn't it be best to do it the "old fashioned" way though? It would mean broader support, no?
It's because it had to be implemented in C for technical reasons
i mean, yeah lol 😛
Depends on your use case. If you want to support older Python versions, yes
Right of course. I think I'll go with union because at work we can't just update to the newest version of Python, we're restricted to a version that has been cleared by the higher ups I guess
Thanks for the quick replies. Much appreciated!
You can also just use float here
It was just an example, I'm not using the function above
one important thing to note is that typing.Union can take multiple... eg Union[str,int,float,bytes]
Union[int | float] seems odd
more importantly, float already does int | float
wait... really?
no kidding
x: float
y: int = 5
x = y
Yes, PEP 484 says int is implicitly a subtype of float
This is also true for str/unicode in Python 2
very interesting (also semantically kind of weird?)
i guess it makes sense for convenience and compatibility
well, that is how it work in python up to complex
And I think mypy also treats bytearray as compatible with bytes but that's not actually specified
the types are specifically designed so that you can use 1 as a complex literal in just about all cases
which is is why 1 .conjugate() exists
!e print((1).conjugate())
@fierce ridge :white_check_mark: Your eval job has completed with return code 0.
1
the complex conjugate of a real is the real itself
indeed
!e print((1).as_integer_ratio())
@hoary pagoda :white_check_mark: Your eval job has completed with return code 0.
(1, 1)
!e print(1+0j == 1)
@fierce ridge :white_check_mark: Your eval job has completed with return code 0.
True
i was expecting (1).conjugate() to be 1+0j, but i guess if they're == it doesn't matter much
yeah, they also hash the same
which is why the Fraction hash is so strange
the numeric tower is one of the parts of python I really like
yes, numeric tower good
i don't know of any other language with something like it, other than scheme and lisp
too bad the type system doesn't work well with the numeric tower
it doesn't? how so
@fierce ridge :white_check_mark: Your eval job has completed with return code 0.
True
huh, it didn't occur to me that abc virtual subclasses would never work with mypy
but it makes sense
surely this particular case deserves a hard-coded exception in mypy though?
honestly it seems a lot of people think this is a problem but nobody puts in the effort to actually do something about it
also one problem is that Number defines no method, so it's not clear what use it would actually be
well i don't think there's much of a need for this in real code, which is probably why nobody fixed it
numpy numbers are probably the main real use case
how many apis actually support Decimal, Rational, and float?
It is really scary how many exceptions the 'type system' has
is np.float64 a subclass of float?
yes, but int32 is not a subclass of int
probably because int requires arbitrary precision
ick, we probably want some kind of FloatLike and IntLike types then
also known as numbers.Real and numbers.Integral 🙂
oh, neat
well hold on
i knew about Real, but i imagine there are issues with how floats aren't actually real numbers
!e ```python
from numbers import Integral, Real
import numpy as np
print(isinstance(np.float64, Real))
@fierce ridge :white_check_mark: Your eval job has completed with return code 0.
False
also that
Probably define the Protocol as and when you need it
more realistically I'd use a union i think
Is there a way to conditionally target pyright?
Or a type checker that supports ParamSpec?
I'm looking to define types for asgiref's async_to_sync
Eg to work around https://github.com/python/mypy/issues/11362
so you want something like if __typechecker__ == "pyright":?
So https://github.com/django/asgiref/pull/298 can work for pyright users and be plain old Callable Any
that's not possible unfortunately
I guess I could add a new pypi package like asgiref_sync that just casts the types to the ParamSpec corrected versions
its called if not MYPY
problem solved
where MYPY is a constant thats always False but whatever
Does pyright ignore MYPY?
It's actually quite sad because it goes to show how inexpressive the type system is, and limits you unless you're special enough to get an exception
In my __init__ method, do I typehint inline with the variables, or do something like this instead?
class Foo:
_foo: str
_bar: int
_foobar: dict[str, int]
def __init__(self, foo, bar, foobar):
self._foo = foo
...
the typehints you have don't reflect the types of the parameters of the __init__ method
oh i see what you mean
usually inline
https://mystb.in/RipeSurfingSounds.python
why is the type of O not set to A when in_class is accessed from the class itself?
how does typescript get around this? don't they have basically the same problems?
You ideally do both - the parameters for __init__ don't have to match the attributes assigned. Most checkers if you just annotate the __init__ parameters should infer the attributes match, but it's nicer to be explicit.
They mostly don't have abstract types
makes life easier
Instead of ints and floats they just have number (which is a double) and instead of lists and tuples and sequences you just use Array
ah, i see
np.float64 is a type though, isinstance wouldn't give you True anyway
In [2]: from numbers import Integral, Real
...: import numpy as np
...: print(issubclass(np.float64, Real))
True
virtual subclasses... quiet sobbing
what exceptions in the type system? I mean, it's as simple as __bases__, __mro__, __subclasses__, __subclasscheck__, __instancecheck__, abc.register, and GenericAlias which claims to be a type but is its evil doppelganger, and your forward refs and postponed evaluations of things, None vs NoneType, randomly using Ellipsis ....
NotImplemented is typed as Any, so your binops don't have to have unions...
lol for ease of writing?
Oops yes i meant issubclass, ty
I've got one for you guys
What would be the appropriate typehint for a typehint?
XD
I'm writing a descriptor which enforces typing at runtime, but not by introspection. You actually pass the typehint as an argument to the descriptor's constructore
class Thing():
# the descriptor figures out its name within the class using
# '__set_name__', called during class assembly
something = Attribute(typehint: List[str])
That's option A. I suppose, thinking about it now, I could do something like...
class Thing():
# the descriptor figures out its name within the class using
# '__set_name__', called during class assembly
something : Attribute[List[str]] = Attriute()
``` And I *guess* user introspection?
if there were a such thing as typing.TypeHint it would be shortly deprecated
Type[Any] perhaps
I think you'll need Any, since not all type hints are types.
The second would be better, use if TYPE_CHECKING to tell the static checker it’s a generic, then at runtime implement your own getitem so it can capture the parameters.
they're supposed to be types, right
I guess there's typing.Literal but that seems sort of like a type
special treatment of dataclass and namedtuple, int being a subtype of float, TypedDict also being some kind of function-type chimera, and probably more that I can't remember...
whoa, int is a subtype of float?
Yeah we apparently determined that a bit above.. it's weird
There's also some stuff that counts as a subtype of bytes when it's only byteslike
yep ```py
x: float = 42
otherwise it would be slightly inconvenient to write 1.0 instead of 1 and such
But also because an int is usable pretty much almost everywhere a float is
I guess
Well... Unless there's some C API that expects a float
Like a cython function
Although I don't know how it handles that
I think those are automatically converted
what is type hinting
def migrate(model: Model):
...``` How will I typehint the `model` parameter accept any subclasses of the `Model` class?
it is already what it does
you mean instance of a subclass, right?
no not the instance of the subclass
then you want type[Model]
thanks, ill do that
You want the subclasses, like ```py
class FooModel(Model):
pass
migrate(FooModel)
but yeah Type[Model] (or type if you're on 3.9+) will work
You want the subclasses
Yeah
? Seems strange.
Why does it seem strange?
oh wait, you're doing some kind of database migration
then yeah, it makes sense
How do I type hint that a function input is a function
def a():
print('wow')
def b(a):
return a()
b(a)
# >>> wow```
in b I want to type hint that the parameter a is a function
from Typing import Callable ?
this one?
Yes
thank you
More precisely, Callable[[], None] - the first parameter is a list of argument types, the second is the return value.
interesting but I don't think that much type hinting is needed
Well if you just use Callable with no parameters, the reader of your code will be as confused as before
And type checkers won't help you catch mistakes
it will be like def convert(func_to_convert: Callable): -> Callable:
Yeah but the type checker doesn't know what arguments you need to call the callable with and it doesn't know what it returns
also true
how should i check if a var is an iterable?
var[0] ?
huh
not sure if slicing always implies iterable
nah i think it's if type(foo) is Iterable
try:
iter(x)
return True
except TypeError:
return False
huh tat works 2 ig thx
type([1, 2, 3]) would be list, and list is Iterable will be False, though [1, 2, 3] is obviously iterable
tat's beyong strange 😆
well, yes
is checks if two names refer to the same object in memory
and list and Iterable wouldn't refer to the same object
would mypy accept tat tho?
isinstance(x, Iterable) would still work in many situations
wdym
nvm
okayy
i hv a code tat's smth like so
def func(arg: Union[int, list, tuple]):
if arg is an iterable: do smth
n mypy wouldt accept it
even after i do if type(arg) in [list, tuple]
