#type-hinting

1 messages · Page 54 of 1

indigo locust
#

Something like this?

oblique urchin
#

you need to put self: T_ThisNodeType

indigo locust
#

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?

pastel egret
#

Yep. self is special when annotated, if the instance doesn't match then the checker acts as if that method is invalid.

indigo locust
#
    @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:
        ...
pastel egret
#

So that should work.

upbeat wadi
#

property overloading isn't supported currently

#

(by mypy and pyright)

indigo locust
#

XD Well shit

upbeat wadi
#

you could make a feature request for it

indigo locust
#

Hmmmm

#

In the mean time, what's the right way?

upbeat wadi
#
@property
def firstSibling(self: T_ThisNodeType) -> Undefined | T_ThisNodeType
#

assuming that TextNode, CommentNode and ElementNode all subclass Node

indigo locust
#

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

upbeat wadi
#

i might be misunderstanding, but do all the nodes subclass Node?

indigo locust
#

Yes, they do. To give you an example of the hierarchy

upbeat wadi
#

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

indigo locust
#
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)

upbeat wadi
#

that would be # HTMLDivElement | Undefined

indigo locust
#

Yeah sorry, just fixed

upbeat wadi
#
class Element(Node):
  if TYPE_CHECKING:  # assuming it isn't being overriden already
    @property
    def firstSibling(self) -> Element | Undefined: ...
indigo locust
#

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?

upbeat wadi
#

since property overloading isn't supported currently, you couldn't type this in the Node class alone

indigo locust
#

Documents can't have siblings 😄

indigo locust
#
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

trim tangle
#

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,
}

fickle lava
#

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)
undone carbon
fickle lava
#

that's OK though, SpecificThing is a subclass of Thing, and it doesn't complain about that part

undone carbon
#

where does it complain then?

fickle lava
#

the last line

undone carbon
#

oh

#

okay

#

tat's strange, i dun c anything wrong honestly, maybe i ma just bad

#

wait amin

fickle lava
#

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

undone carbon
#

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

pastel egret
#

What you need is a typevar and generic.

fickle lava
#

yeah, I figured, but I don't know how to set generics up for a whole class like this

pastel egret
#
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.

fickle lava
#

Yeah, this seems to work great. Thanks!

undone carbon
#

@pastel egret using beartype with multiple dispatch doesnt seem to work. how should i fix it??

pastel egret
#

@overload? Not implemented yet.

undone carbon
#

sad. i wonder how long it would take

pastel egret
undone carbon
#

ya u told me b4

fickle lava
#

it's already been resolved though

grave fjord
#

I don't think you necessarily need generics here

fickle lava
grave fjord
#

Ooh

fickle lava
#

I think generics are what I needed to document the intended use

grave fjord
#

I think you just need:

class SpecificThingUser(ThingUser):
    thing: SpecificThing
fickle lava
#

oh, as a class scoped name?

grave fjord
#

Well it's not class scoped unless you use attr: ClassVar[T]

#

so it depends really do you often use ThingUser with ad-hoc SpecificThings ?

#
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)
fickle lava
#

I have multiple "specific things"

fickle lava
grave fjord
#

?

#

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

fickle lava
#

I mean should I be using the class annotation as opposed to generics

grave fjord
#

I don't know, I was just giving you an alternative

pastel egret
#

Generics are probably more flexible, since then any methods you add could also be type hinted correctly.

fickle lava
#

I'll stick with generics then. Now if only pycharm could handle @asynccontextmanager properly...

grave fjord
fickle lava
#

don't have that, and not sure. isn't that just a backport?

#

I'm on 3.9

ivory owl
#

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.

https://stackoverflow.com/a/67479141

lone widget
#
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?

grave fjord
fierce ridge
fierce ridge
lone widget
#

aww, damn

#

so -> Iterator[Union[bool, T]]?

fierce ridge
# lone widget aww, damn

that was what i've had to do in the past, unfortunately... can you subclass from typing.NamedTuple?

fierce ridge
lone widget
#

wait, can i just straight up use a named tuple?

fierce ridge
#

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

lone widget
#

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

fierce ridge
#

weird

#

i wonder if that's because of how Exception.__init__ is implemented

#

and because of weirdness in the except clause

lone widget
#

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)

trim tangle
fierce ridge
#

that's where i got the pyright output i posted above

indigo locust
#

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?

fierce ridge
indigo locust
#

How do I specify I want a class but not one of its subtypes?

blazing nest
indigo locust
#

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

blazing nest
blazing nest
indigo locust
#
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

blazing nest
#

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

indigo locust
#

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

blazing nest
indigo locust
#

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?

blazing nest
#

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

indigo locust
#

Do you think I should fill a pull request though?

#

Seems like a useful behaviour

blazing nest
#

Pull request where?

indigo locust
#

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

blazing nest
blazing nest
indigo locust
#

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

blazing nest
indigo locust
#

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

fierce ridge
#

(why Literal[bytes] and not type[bytes] ?)

indigo locust
#

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

solid sleet
#

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

fierce ridge
#

"don't you miss the good old python 2.6 days? when everything quacked in ascii?"

indigo locust
#

I'm honestly not sure what I'm looking at?

solid sleet
#

you're looking at typed code going in and untyped code coming out

indigo locust
#

Oh, christ XD

#

Salt, you're a monster

solid sleet
#

yeah, salt

fierce ridge
#

no, i'm a rock

indigo locust
#

"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?

fierce ridge
#

otherwise how about an enum of valid type identifiers?

#

e.g. nodeType.ELEMENT or something like that

indigo locust
#

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

chrome thicket
#

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)

indigo locust
#

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

chrome thicket
#

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()
indigo locust
#

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]

chrome thicket
#

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

indigo locust
#

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
chrome thicket
#

and still access the attrs like MyThing.someInstanceAttributeA ?

indigo locust
#

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

chrome thicket
#

thank you so much, I think I can try to figure this out on my own

I'll share my results later 🙂

indigo locust
#

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:
  ...
trim tangle
#

@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

soft matrix
#

(only in type hints)

trim tangle
#

yeah

chrome thicket
#

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
trim tangle
#

meta-programming in tests...aaaaaaa

grave fjord
trim tangle
#

we're done guys, deleting this channel

solid sleet
#

don't worry if you need to fix your code, i got you

buoyant swift
indigo locust
#

If I have a function that raises a NotImplemented error

#

Do I type its return as NoReturn or NotImplemented?

pastel egret
#

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.

lone widget
#

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?

little hare
#

i think so?

#

not sure if you can find a type checker that supports 3.2 still tbh

#

if ever

lone widget
#

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…)

pastel egret
#

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+.

little hare
#

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 😛

lone widget
#

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 🙂

rustic gull
#

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))
    ```
summer berry
#

The documentation shows the types for the arguments

rustic gull
#

i meant like returning and errors

summer berry
#

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

rustic gull
#

oh alright.

summer berry
#

It's a CommandError

rustic gull
#

also is their a chart with all the types i can use in the typing module

summer berry
#

I don't know of a chart for that module, but I know collections.abc does have a table in its docs.

rustic gull
#

collections.abc ?

summer berry
#

!d collections.abc

rough sluiceBOT
#

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.

  1. 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:
summer berry
#

If you're on it 3.9+ you can use those types instead of their equivalents in the typing module, which are now deprecated

chrome thicket
chrome thicket
# grave fjord 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?

crimson raft
#
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
vast olive
#

couldn't you specify it once as an attribute? something like

class DatabaseManager:
    sql_engine: SQLEngine

    def __init__(...):
        ...
grave fjord
crimson raft
grave fjord
crimson raft
upbeat wadi
#
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]"

blazing nest
#

Callables are invariant

#

I don't know where the B comes from though

trim tangle
blazing nest
#

Wait no they were contravariant

#

My memory fails me yet again

trim tangle
#

right, they are contravariant in the arguments

trim tangle
# upbeat wadi ```py from typing import TypeVar, Callable Callback = Callable[['SlashCommand',...

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)

little hare
#

.wa contravariant

trim tangle
#

sorry I don't have a good article on variance besides mypy

buoyant swift
#

wikipedia's is good

upbeat wadi
#

Thanks!

urban root
#

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 | None cannot 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 😅

oblique urchin
#

So something like pool = await create_pool(); assert pool is not None; self.pool = pool

urban root
#

but shouldnt asserting the instance variable directly also work?

oblique urchin
#

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

urban root
#

hmm

shut granite
#

Any python class gurus who can help in #help-popcorn with some dynamic inheritance?

urban root
oblique urchin
urban root
#

what does cast do

oblique urchin
#

lie to the typechecker about a type

urban root
trim tangle
# urban root 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.

urban root
#

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

crimson raft
#

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 hmm

crimson raft
urban root
#

hmm alright

crimson raft
#

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 👍

trim tangle
crimson raft
#

it is settled then

trim tangle
#

@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:
    ...
oblique urchin
crimson raft
#

lmao

urban root
#

I don't think I can actually do that. Since its kinda internal library code

crimson raft
#

i'll just write 2 separate methods

trim tangle
urban root
#

Yeah

trim tangle
#

although, well, Java programs worked somehow before Option

#

mostly worked

oblique urchin
#

sometimes

trim tangle
#

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.

oblique urchin
crimson raft
#

isinstance isn't preferred here to let the type checker know the type has been narrowed for their issue?

oblique urchin
#

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

oblique urchin
crimson raft
#

👍 thought so

blazing nest
trim tangle
#

well, not really

#

but I wanted to implement some basic stuff, and then add some experimental features to just play around with

oblique urchin
trim tangle
#

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

oblique urchin
#

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

trim tangle
#

you can't describe dataclasses with a stub

oblique urchin
trim tangle
#

yeah I've seen this

#

I guess it is a necessary hack until we have some kind of official plugin spec

spare mauve
#

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

trim tangle
#

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

spare mauve
#

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?

trim tangle
#

yeah, and sometimes it doesn't

spare mauve
#

and sometimes it's like cannot navigate to the symbol at caret 🥲

dense turtle
#

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.

pastel egret
#

Well, Python 3 no longer supports the __metaclass__ variable name, so that's just a regular class.

pastel egret
dense turtle
pastel egret
#

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

dense turtle
#

Sounds reasonable

pastel egret
rough sluiceBOT
#

stdlib/builtins.pyi line 142

class type(object):```
pastel egret
#

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.

dense turtle
#

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

pastel egret
#

Yep, type() is useful for dynamically creating classes on the fly, where a type checker would have no clue what's going on.

rustic gull
#

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

pastel egret
pastel egret
#

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
rustic gull
#

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 lemon_glass

acoustic thicket
#

Type[T] is covariant so that should cover all of them

acoustic thicket
rustic gull
#

But what is you wanted Type[User] but no children or only certain children pithink

acoustic thicket
rustic gull
brisk hedge
soft matrix
#

that errors cause Foo() is not compatible with type[Foo]

#

not sure why it shows up as Type[<nothing>]

spare mauve
#

type(Foo()) might work?

#

the nothing is odd

soft matrix
#

well just Foo would work

spare mauve
#

true

soft matrix
#
/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
dapper trench
#

hey guys! anyone here using boto3 actively?

grave fjord
#

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

soft matrix
#

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

grave fjord
#

That's what the <nothing> is afaik

#

Or you mean there's no warning on the def, but there should be?

soft matrix
#

but you wouldnt know that without knowing mypy internals or going to the docs or whatever

#

yeah there wasnt a warning on the def

#

hmmm

#

pyright doesnt complain about this

grave fjord
#

Yeah there's a load of stuff where it's valid input but clearly redundant or misleading

unborn badger
#

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

little hare
#

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().

little hare
unborn badger
#

yeah class it is

pastel egret
# unborn badger how can I type hint a named tuple?

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.

grave fjord
#

Imho

blazing nest
#

Is __optional_keys__ and __required_keys__ official?

blazing nest
#

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

oblique urchin
#

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)

blazing nest
#

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):
...

oblique urchin
#

pyright already supports that, I'll be releasing a new version of typing-extensions with runtime support for it soon

blazing nest
oblique urchin
blazing nest
oblique urchin
#

Sebastian merged Required/NotRequired into typing-extensions as we were talking here, I'll probably make a release for it later today 🙂

soft matrix
#

I was funnily just about to ask about a new release for that

upbeat wadi
#
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?
radiant cave
#

how can I use the current class as a type hint? ```py
class A:
def init(self, name):
self.name = name

def copy(self) -> A:            # NameError: name 'A' is not defined
    return A(self.name + "2")

a = A("a")
a2 = a.copy()
print(a2.name)```

radiant cave
#

how silly. Thanks

#

I guess it's a workaround. If A inherits from B, def copy(self) -> B: works just fine

upbeat wadi
#

you can also from __future__ import annotations on 3.9.7+ and all annotations will be converted to strings

soft matrix
#

you can also use typing_extensions.Self

#

if you care about people inheriting the class

upbeat wadi
trim tangle
radiant cave
#

yeah, I figured as much. But it's a common need so there had to be a way

trim tangle
#

so if you're not inspecting annotations (e.g. with pydantic) use that

radiant cave
#

but I've been told the future is here!

#

damn that typing hype

trim tangle
#

defaulting it has been delayed to 3.11

blazing nest
#

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

trim tangle
#

@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.

blazing nest
blazing nest
#

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]"
soft matrix
#

just type: ignore

#

that is some questionable behaviour

#

idg how that isnt type safe

trim tangle
#

oh i c

blazing nest
#

MyPy also complains but just as a general issue ```
error: Overwriting TypedDict field "type" while extending
error: Overwriting TypedDict field "data" while extending

trim tangle
#

@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

blazing nest
leaden oak
blazing nest
#

Yeah Fix error showed why above

leaden oak
#

Oh I missed that 😅

blazing nest
#

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

trim tangle
#

subtyping is a bit messy ye

soft matrix
#

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

blazing nest
#

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]

leaden oak
#

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 🤷

grave fjord
oblique urchin
#

I wonder if an immutable TypedDict type would be useful

grave fjord
#

:(

leaden oak
#

It's probably not necessary, namedtuples and frozen dataclasses work well enough

grave fjord
#

Maybe you want an enum here?

#

Or a Try monad?

soft matrix
#

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
oblique urchin
#

typing-extensions 4.0.0 has been released, enjoy Self, Required, and NotRequired

soft matrix
#

thanks Jelle

trim tangle
#

hell yeah

trim tangle
oblique urchin
#

People do make occasional noises about typevar scoping being hard

#

So something concrete might come out of that

trim tangle
# oblique urchin Not that I know of

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)

upbeat wadi
blazing nest
oblique urchin
little hare
#

does typeshed_client have a cli?

#

its not even showing up for me

oblique urchin
little hare
#

wait you wrote that!!

#

👀

oblique urchin
#

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

little hare
#

i was just exploring pypi a few days ago

little hare
#

it requires mypy-extensions but doesn't declare that

oblique urchin
#

Oh it does, for NoReturn. Let me fix that

little hare
#

although NoReturn is in the stdlib by a certain version

oblique urchin
#

3.6.2

#

Do I care about 3.6.0 and 3.6.1?

little hare
#

and 3.5.4 lol

oblique urchin
#

yes that was back when typing was provisional and we got to just add stuff to bugfix releases

little hare
#

ahhh

#

neat

oblique urchin
#

Not really 🙂

little hare
#

true

#

importlib.metadata is my favorite package actually

soft matrix
#

i hate importlib.metadata

#

so many packages require it with different versions

#

its a pain

little hare
#

oh, i mean the stdlib version 😛

#

!d importlib.metadata

rough sluiceBOT
#

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.

little hare
#

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

soft matrix
#

ah

little hare
#

i love how so much of typing is actually deprecated /s

#

why is this a thing?

#

couldn't an isinstance be used for this?

blazing nest
soft matrix
#

(its not)

#

do Film.__class__

little hare
#

!e ```py
from typing import TypedDict

class Film(TypedDict):
title: str
year: int

print(Film.class)```

rough sluiceBOT
#

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

<class 'typing._TypedDictMeta'>
soft matrix
#

it is when you create an instance of it

#

but not the actual class

little hare
#

!e ```py
from typing import TypedDict

class Film(TypedDict):
title: str
year: int

print(Film().class)```

rough sluiceBOT
#

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

<class 'dict'>
little hare
#

ah

soft matrix
#

otherwise how would is_typeddict actually work

little hare
#

although

soft matrix
#

unless it did duck typing

little hare
#

!e ```py
from typing import TypedDict
import typing

class Film(TypedDict):
title: str
year: int

print(typing.is_typeddict(Film()))```

rough sluiceBOT
#

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

False
rough sluiceBOT
#

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)```
soft matrix
#

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

oblique urchin
#

we probably could have made issubclass(Film, TypedDict) work with the right metaclass magic

soft matrix
#

oh that would make more sense

#

i feel like that isnt hard to do

blazing nest
rough sluiceBOT
#

typing_extensions/src_py3/typing_extensions.py line 1092

TypedDict = _TypedDictMeta('TypedDict', (dict,), {})```
leaden oak
#

although the fact typed namedtuples only gained additional method support in 3.6.2 was more than a little aggravating haha ^^ /lh

oblique urchin
#

I'm pretty sure I was the one who implemented that 😄

leaden oak
#

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

little hare
#

lmao

#

i still use 3.7 sometimes

#

on a project

#

written with tabs

#

why do i do that

grave fjord
#

Is it cash money?

trim tangle
#

|| 3.6 here lemon_cut ||

little hare
#

and barely typed at all

oblique urchin
#

we still run 3.6 too. upgrading a hundred third-party dependencies is hard

leaden oak
#

I'm honestly already at the point where it'd be so nice to drop 3.6

little hare
oblique urchin
#

I better get on it soon though before people like you start dropping 3.6 support 😄

grave fjord
#

Only a few days left

#

I've got a 3.4 project still in production

leaden oak
#

because the only Python OSS projects I maintain (that isn't black) are black development tools 😅

trim tangle
#

I wish I had a useful open source project lemon_pensive

little hare
leaden oak
#

.gh repo ichard26/next-pr-number

mighty lindenBOT
#

Get the number the next pull request will be assigned for a public GitHub repository.

little hare
#

and was like, wtf

leaden oak
#

is probably one of my better OSS projects

trim tangle
#

how do people find open source projects to contribute to generally?

little hare
#

...is bandersnatch a dev tool for black?

little hare
trim tangle
#

??

grave fjord
little hare
#

i ask ppl what projects they're working on to find projects to contribute to

#

if it interests me i work on it

trim tangle
#

I wanted to contribute to pyright but it's inpenetrable 😦

grave fjord
#

They come in here or IRC and have problems and I go fix em

trim tangle
#

and other stuff doesn't have bugs 😄

little hare
#

oh also

#

!pip py-spy

rough sluiceBOT
little hare
#

i found this a few days ago and it creates very pretty graphs from profiles

#

also its written in rust

trim tangle
#

yup

leaden oak
leaden oak
#

(let's just ignore the fact I've never used pyright)

#

Does pyright have more than one maintainer?

trim tangle
#

no

#

well, doesn't seem like it does

#

oh wait, it does

leaden oak
#

chirp

trim tangle
#

okay, it has 2 maintainers

leaden oak
#

Seems like one is rather inactive?

#

Oh, lol I see the msft and non-msft users

little hare
rough sluiceBOT
little hare
#

the wrapper is actually worth it IMO

#

it violates all rules to work

#

also

#

i've been experimenting with pytype

#

!pip pytype

rough sluiceBOT
leaden oak
#

OH, pyright is not a python application?

little hare
#

nope ts

#

typescript*

leaden oak
#

oh damn pytype doesn't support windows at all haha

soft matrix
trim tangle
leaden oak
#

eric found a way to clone themselves

trim tangle
unborn badger
#

How can I type hint that a input str is either 'a' or 'b' or 'c'

oblique urchin
twin arrow
#

fix errors pfp 🤣

dapper trench
#

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

hasty hull
#

what do you mean it violates all rules to work ahaha

grave fjord
#

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

blazing nest
#

Any point in trying to make a "typed mapping"?

blazing nest
soft matrix
#

i was actually thinking about this

#

you can

#

just its awful

#

Just overload getitem a tonne

blazing nest
#

Oh my-

soft matrix
#

But yeah I think a TypedMappingMixin would be nice

blazing nest
#

Yeah and then TypedDict could just be a shortcut (would be required for semver)

trim tangle
#

@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

soft matrix
#

Variance is a pain along with typing types that don't implement dict but are mappings

grave fjord
#

Yeah asgi is a pain here

#

Would have been nice to have the types be Protocol instances with a recommended library of dataclasses

little hare
grave fjord
oblique urchin
soft matrix
#

Surely binding to deno or node can't be that hard

#

Yeah this actually seems like an alright idea

upbeat wadi
#

i couldn't find any node bindings the last time i checked

hasty hull
little hare
#

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?

leaden oak
#
@@ -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 😅

oblique urchin
#

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

leaden oak
#

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.

leaden oak
oblique urchin
#

also we're in the wrong channel 😄

leaden oak
#

oh

#

well sorry mods 😅

rustic gull
#

How do you typehint multiple returns? for example
return 'abc', 'def'

acoustic thicket
rustic gull
#

Ok thanks!

hasty hull
hasty hull
little hare
#

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

hasty hull
#

Yeah, I really like pyright, it's also like ten times faster than mypy

hasty hull
# little hare tldr?

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
hasty hull
# grave fjord patterns?

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
little hare
#

i'm not a big fan of pydantic

hasty hull
#

Why not?

little hare
#

its too high-level for me

#

i like to go a bit lower with attrs

hasty hull
#

Hmmm, I've never thought of pydantic as being too high level, you can customise basically everything

little hare
#

if some things can variably be turned off, that's what I want

#

i should add, somewhat easily

hasty hull
#

What specifically would you want to be turned off?

#

The implicit type coercion?

little hare
#

!pypi pydantic

rough sluiceBOT
#

Data validation and settings management using python 3.6 type hinting

little hare
#

...6 months ago??

#

...

little hare
hasty hull
little hare
#

ie that feature has existed for at least 6 months

hasty hull
#

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

little hare
#

tldr i think i hate pydantic just because

hasty hull
#

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

little hare
#

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

hasty hull
#

Ah okay what did you try and make it work for?

little hare
#

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

little hare
#

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?

hasty hull
#

Ah yeah that does sound like pydantic would not play well with that

little hare
#

pydantic was problematic for this, as there was no way to create an instance with..... one sec

hasty hull
#

You can create partial instances, there's a model.construct() function

little hare
#

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

hasty hull
#

What's wrong with just constructing the instance once to get the defaults?

little hare
#

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

hasty hull
#

I don't know if it's exposed but pydantic makes use of an UNSET constant internally

little hare
#

doesn't look to be exposed

#

ngl i don't think i gave pydantic the chance it deserves

little hare
#

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 ;-;

hasty hull
little hare
#

all on lower levels 😛

#

~also i do not think i have lazy loading implemented, I think that was a buzzword~

hasty hull
#

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

little hare
#

true, typechecking is awesome

#

oh, i'm curious, is [tool.pyright] used officially?

#

or just by the wrapper?

#

ah

#

lmao

#

bad microsoft slap

hasty hull
little hare
#

but pyright itself does, yah?

hasty hull
#

Yeah

grave fjord
hasty hull
#

I actually can't remember, I haven't really used mypy since I started using pyright

trim tangle
#

Otherwise the type hints don't mean much

hasty hull
#

The type hints are for both queries and the returned models

trim tangle
#

Otherwise it is an ORM, which do exist

hasty hull
#

The queries are typed using auto-generated TypedDicts specific to your database schema, that's how the type hints can actually work

trim tangle
#

Ah, so they're autogenerated

hasty hull
#

Yeah that's the only reason it's possible

oblique urchin
grave fjord
oblique urchin
grave fjord
spare mauve
#

it's not possible to type hint a lambda, is it

acoustic thicket
#

f: Callable[[], str] = lambda: "hi" ig

#

can't think of any context where you'd want to typehint a lambda

crisp robin
#

Is it possible to typehint a callable class?

#

Basically type & Callable

acoustic thicket
#

all classes are callable though

crisp robin
#

oh hmmm

acoustic thicket
#

calling the class gives you an instance

crisp robin
#

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

soft matrix
#

You don't need an intersection here

light kite
#

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?

little hare
#

yes, basically the supported python version

light kite
#

So which one is the most recent/newest way of doing this?

oblique urchin
#

The | version is new in 3.10

little hare
#

internally, they both end up as typing.Union, but the second way is only supported on 3.10

#

although

oblique urchin
#

(And usable in older versions with from __future__ import annotations

little hare
#

(its not)

oblique urchin
light kite
#

So | is basically just shorthand notation for a union?

oblique urchin
#

Yes

little hare
#

yes

little hare
light kite
#

Ok cool. Wouldn't it be best to do it the "old fashioned" way though? It would mean broader support, no?

oblique urchin
oblique urchin
light kite
#

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!

soft matrix
#

You can also just use float here

light kite
#

It was just an example, I'm not using the function above

little hare
#

one important thing to note is that typing.Union can take multiple... eg Union[str,int,float,bytes]

light kite
#

Yeah it's exactly what I need

#

I am using it with three different types

acoustic thicket
hoary pagoda
#

more importantly, float already does int | float

fierce ridge
#

no kidding

#
x: float
y: int = 5
x = y
oblique urchin
#

This is also true for str/unicode in Python 2

fierce ridge
#

very interesting (also semantically kind of weird?)

#

i guess it makes sense for convenience and compatibility

hoary pagoda
#

well, that is how it work in python up to complex

oblique urchin
#

And I think mypy also treats bytearray as compatible with bytes but that's not actually specified

hoary pagoda
#

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

oblique urchin
fierce ridge
#

!e print((1).conjugate())

rough sluiceBOT
#

@fierce ridge :white_check_mark: Your eval job has completed with return code 0.

1
hoary pagoda
#

the complex conjugate of a real is the real itself

fierce ridge
#

indeed

hoary pagoda
#

!e print((1).as_integer_ratio())

rough sluiceBOT
#

@hoary pagoda :white_check_mark: Your eval job has completed with return code 0.

(1, 1)
fierce ridge
#

!e print(1+0j == 1)

rough sluiceBOT
#

@fierce ridge :white_check_mark: Your eval job has completed with return code 0.

True
fierce ridge
#

i was expecting (1).conjugate() to be 1+0j, but i guess if they're == it doesn't matter much

hoary pagoda
#

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

fierce ridge
#

yes, numeric tower good

#

i don't know of any other language with something like it, other than scheme and lisp

oblique urchin
#

too bad the type system doesn't work well with the numeric tower

fierce ridge
#

it doesn't? how so

oblique urchin
fierce ridge
#

oh, that

#

!e ```python
from numbers import Number
print(isinstance(3, Number))

rough sluiceBOT
#

@fierce ridge :white_check_mark: Your eval job has completed with return code 0.

True
fierce ridge
#

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?

oblique urchin
#

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

fierce ridge
#

well i don't think there's much of a need for this in real code, which is probably why nobody fixed it

oblique urchin
#

numpy numbers are probably the main real use case

fierce ridge
#

how many apis actually support Decimal, Rational, and float?

trim tangle
#

It is really scary how many exceptions the 'type system' has

fierce ridge
#

is np.float64 a subclass of float?

oblique urchin
#

probably because int requires arbitrary precision

fierce ridge
#

ick, we probably want some kind of FloatLike and IntLike types then

oblique urchin
#

also known as numbers.Real and numbers.Integral 🙂

fierce ridge
#

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))

rough sluiceBOT
#

@fierce ridge :white_check_mark: Your eval job has completed with return code 0.

False
fierce ridge
#

also that

grave fjord
fierce ridge
grave fjord
#

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

oblique urchin
#

so you want something like if __typechecker__ == "pyright":?

grave fjord
oblique urchin
#

that's not possible unfortunately

grave fjord
#

I guess I could add a new pypi package like asgiref_sync that just casts the types to the ParamSpec corrected versions

soft matrix
#

its called if not MYPY

#

problem solved

#

where MYPY is a constant thats always False but whatever

grave fjord
#

Does pyright ignore MYPY?

blazing nest
sweet notch
#

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
        ...
upbeat wadi
#

the typehints you have don't reflect the types of the parameters of the __init__ method

#

oh i see what you mean

#

usually inline

fierce ridge
pastel egret
oblique urchin
#

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

fierce ridge
#

ah, i see

acoustic thicket
spare mauve
#

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

pastel egret
#

NotImplemented is typed as Any, so your binops don't have to have unions...

spare mauve
#

lol for ease of writing?

indigo locust
#

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?
spare mauve
#

Type[Any] perhaps

pastel egret
#

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.

spare mauve
#

they're supposed to be types, right

#

I guess there's typing.Literal but that seems sort of like a type

trim tangle
spare mauve
blazing nest
#

Yeah we apparently determined that a bit above.. it's weird

grave fjord
trim tangle
#

otherwise it would be slightly inconvenient to write 1.0 instead of 1 and such

grave fjord
#

But also because an int is usable pretty much almost everywhere a float is

trim tangle
#

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

void panther
#

I think those are automatically converted

karmic harness
#

what is type hinting

trim tangle
#

@karmic harness Check out the pinned messages

#

namely the first one

mossy leaf
#
def migrate(model: Model):
     ...``` How will I typehint the `model` parameter accept any subclasses of the `Model` class?
trim tangle
#

you mean instance of a subclass, right?

mossy leaf
#

no not the instance of the subclass

oblique urchin
#

then you want type[Model]

mossy leaf
#

thanks, ill do that

trim tangle
#

but yeah Type[Model] (or type if you're on 3.9+) will work

mossy leaf
trim tangle
#

then yeah, it makes sense

unborn badger
#

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?

blazing nest
unborn badger
#

thank you

pastel egret
#

More precisely, Callable[[], None] - the first parameter is a list of argument types, the second is the return value.

unborn badger
#

interesting but I don't think that much type hinting is needed

trim tangle
#

And type checkers won't help you catch mistakes

unborn badger
#

it will be like def convert(func_to_convert: Callable): -> Callable:

blazing nest
#

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

unborn badger
#

also true

undone carbon
#

how should i check if a var is an iterable?

unborn badger
#

var[0] ?

undone carbon
#

huh

unborn badger
#

not sure if slicing always implies iterable

undone carbon
#

nah i think it's if type(foo) is Iterable

acoustic thicket
undone carbon
#

huh tat works 2 ig thx

acoustic thicket
undone carbon
#

wat?!

#

lmaoo

acoustic thicket
#

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

undone carbon
acoustic thicket
#

isinstance(x, Iterable) would still work in many situations

acoustic thicket
undone carbon
#

nvm

undone carbon
# acoustic thicket wdym

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]