#type-hinting

1 messages Β· Page 73 of 1

summer berry
#

I'm trying to emulate collections.abc.Sequence, but hashable. It's not even clear if collections.abc.Sequence requires slicing to be supported.

#

@trim tangle You suggested a protocol but I'm having some trouble. Would appreciate advice.

#

I'm just gonna accept that the slice overload shouldn't be there for now.

#

Not sure if my protocol approach is correct but at least it works with a tuple.

trim tangle
#

HashableSequence[C] isn't a subtype of HashableSequence[B] because ``HashableSequence` is invariant

chilly ruin
hearty shell
#

It is just an example usage, ValueRange doesnt actually exists

summer berry
# trim tangle Maybe make `T` covariant?

Yeah, that fixes it. At one point pyright was telling me to make it covariant, and later it said it should be invariant, and now covariant doesn't complain. Not sure what changes I made since then to make it stop complaining about covariance.

#

Granted, I'm still not sure if a Sequence requires slicing or not.

hearty shell
#

According to typeshed it does

summer berry
split jetty
#

good day guys, what is the shortcut for this please?

#

as i type my command, it shows this, but ctrl+space shows a different hint.

loud zodiac
#

How to tell a generic type is a subclass of some existing class?

#

probably T = TypeVar("T", bound=SuperClass)

indigo locust
#

Quick question

#
class Base(object):
  def method(self, argument: int) -> None:
    ...

class Derived(Base):
  def method(self, argument: str) -> None:
    ...
#

Is it bad practice to have methods of the same name but different signatures on a derived class and its base?

oblique urchin
brisk heart
#

Pyright complains for me even if just reorder keyword-only arguments 😭

hearty shell
#

Doesnt seem like it does πŸ€”

brisk heart
#

I remember it complaining so whenever I make a new project I automatically disable it

hearty shell
#

From when? I testing on a really old version of pyright xD

brisk heart
#

Also something to do with adding/removing keyword arguments even when **kwargs are already present

brisk heart
hearty shell
#

Humm, maybe they started complaining after whatever release I am testing with

#

In fact

#

just testing this with latest

class Foo:
    def bar(self, *, a: int, b: str) -> None: ...
    def baz(self, a: int = ..., b: str = ...) -> None: ...

class SubFoo(Foo):
    def bar(self, *, b: str, a: int) -> None: ...
    def baz(self, b: str = ..., a: int = ...) -> None: ...
#

and I get no erros

#

Which is weird since I should get an error on baz

oblique urchin
#

do you have the right checks on?

hearty shell
#

I just did pyright file.py, do I have to enable some special flag?

#

I usually use mypy x)

oblique urchin
#

or just --strict on the command line?

hearty shell
#

Ohh, I thought it just had everything on when running via cli

hearty shell
oblique urchin
#

guess you need a config file

hearty shell
#

Aha, I got the error now, thank you

{
    "strict": ["**"]
}

Luckily no bug reports came from missing that

gusty kelp
#

Is there a way to annotate the return type of a base class method in the subclass? e.g. (something like the way @unkempt plover works)

hearty shell
#

What do you mean?

gusty kelp
#

simplified version

class Base:
    ec: Callable[[WebDriver], Any]  #override in subclass
    def __call__(self, driver: WebDriver) -> Any:
        return self.ec(driver)

class Element(Base):
    def __init__(self, locator, description: str):
        super().__init__(description)
        self.locator = locator
        self.ec: Callable[[WebDriver], Union[bool, WebElement]] = EC.visibility_of_element_located(locator)
        ## I want to type hint that __call__ will return  Union[bool, WebElement]
#

each subclass has a callable 'expected condition' ec that can return various things.. but the base class annotates Any. I'm curious if I can override the annotation in the subclass.

#

my hunch is the only way to do that is to completely override the method

oblique urchin
#

you could make the base class generic

hearty shell
#

You would have to overwrite it directly otherwise, but you dont have to completely override it, annotating it should be enough

blazing nest
gusty kelp
hearty shell
#

__call__: Callable[[WebDriver], Union[bool, WebElement]] :P

#

annotation goes in the class body, but yeah generics would be better

gusty kelp
hearty shell
#

Yup, it is just a name like any other, and I think now both mypy and pyright agree on self being omitted

#

just like you can at runtime do __call__ = lambda self: 5

gusty kelp
#

I dunno why I didn't realize that was possible in the subclass as an override annotation

gusty kelp
#

@oblique urchin like this?

class Base(Generic[T]):
    ec: Callable[[WebDriver], T]  #override in subclass
    def __call__(self, driver: WebDriver) -> T:
        return self.ec(driver)
oblique urchin
gusty kelp
#

^^ that's what I'm afraid of. I barely feel confident in creating a standard generic

gusty kelp
oblique urchin
gusty kelp
#

oh... neat

unborn iris
lethal widget
#

import tkinter as tk
from tkinter import filedialog, text
import os

root = tk.Tk()

canvas = tk.Canvas(root, height=700, width=700, bg="#263D42")
canvas.pack()

frame = tk.Frame(root, bg="white")
frame.place(relwidth=0.8, relheight=0.8, relx=0.1, rely=0.1)

root.mainloop()
is my scriot so far but it says tkinter cant be found or doesnt exist

rustic gull
lethal widget
#

k

pliant gale
#

where do i ask for help in python

#

?

rare scarab
median wharf
#

My API returns a response object like this

{
    "response_code": "0",
    "response_message": "Success",
    "response": ...
}

Where response can be whatever response the api url returns. If one URL returns List[int] how do I generic'ify this object with TypedDict?

Something like ResponseWrapper[List[int]] kinda

hearty shell
median wharf
#

Yes, I want to make that object into a TypedDict but accept a generic and have "response" as T

oblique urchin
#

you're in luck, 3.11 will have that

hearty shell
#

Yeah I just saw this

oblique urchin
median wharf
#

I'm spoiled from typescript, coming to python as req for this project at my work

#

Thank you

#

Is there anything I can use for now? Or just wait

hearty shell
oblique urchin
hearty shell
#

4.2.0

oblique urchin
#

of Python

hearty shell
#

Oh, 3.10, I thought you meant typing-extension versoin

oblique urchin
#

oh nice, I thought it might be only on earlier versions of Python

#

I guess we forgot to disallow this in typing-extensions πŸ™‚

hearty shell
oblique urchin
brisk heart
#

When I have a callable where the return doesn't matter am I supposed to use Any or object? I've noticed that I rarely use object and have Any everywhere instead

#

Actually it's similar in many other projects too

indigo locust
#

As an example

#
class Thing():
  
  attribute : None | str

  def __init__(self, attribute: typing.Optional[str] = ''):
    
    if isinstance(attribute, str):
      self.attribute = attribute
  
    else:
      self.attribute = None
#

As a argument, attribute is optional

#

As an attribute through, attribute is not optional per say, it can just take on one of two types

#

So I find Any to be better in terms of intent. It means "this can return anything" whereas object says "this will return an object"

#

Which, naturally, an object will always be returned so it doesn't help much

trim tangle
#

In TypeScript there's a void type just for this purpose.

#

Any type fits it, but you can't really use it.

#

I think you should use object because the type checker will catch an accidental use of the return value

indigo locust
#

I mean, I think it really comes down to what the callable actually returns

#

There aren't too many situations in which something really can return anything β€” __getattr__ comes to mind

#

But generally a callable is going to serve a purpose, and return something by definition

indigo locust
#

This is why we have Argspecs

trim tangle
#

I think ashlen is talking about accepting a callable argument

brisk heart
#

I was unsure as to whether it's some unspoken rule

indigo locust
trim tangle
#

If it's some sort of callback, maybe just have it return None?

brisk heart
#

Ooh, I saw that too at places

#

but that seems even worse considering it's an arbitrary limitation

#

If it's a return type then I can understand but I don't like using it in parameters

indigo locust
#

Ohhhhh Python ❀️

#

Such a beautiful language

#

A many-layered onion wrapped in a finely tuned machine, wrapped in a song

brisk heart
#

Welp, now the question is whether there's any point in me refactoring my code to add as many objecs to my typehints

indigo locust
#

What exactly is the situation?

trim tangle
brisk heart
#
def register(self callback: typing.Callable[..., typing.Any?]) -> None:
    self.callbacks.append(callback)
#

The return doesn't matter at all to the event manager, it just needs to call the thing

#

None is limiting, Any is too arbitrary, object is too weird

indigo locust
#
from collections.abc import Callable
from typing import Concatenate, ParamSpec, TypeVar

P = ParamSpec('P')
R = TypeVar('R')

def register(self callback: typing.Callable[P, R]) -> None:
  self.callbacks.append(callback)
#

Actually

trim tangle
#

I think using a typevar only once is illegal

indigo locust
#

If you don't need to capture and then forward the return type (example to follow) then you don't even bother using a parameterization of Callable

#

So its just

def register(self, callback: typing.Callable) -> None:
  self.callbacks.append(callback)
trim tangle
#

Also, does the callback accept any possible arguments? πŸ€”
(Not very relevant but you might want to look at generics/ParamSpec in the future)

blazing nest
#

I use object for all user-provided callbacks. This way, if I do end up using it then it is catched by the type checker or I am forced to do sufficient isinstance() checks.

indigo locust
trim tangle
#

!e
Speaking of isinstance checks and LSP...

import yarl
print(yarl.URL("http://127.0.0.1").with_port(True))
rough sluiceBOT
#

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

http://127.0.0.1:True
trim tangle
#

Apparently, if you communicate over this port you can only transmit truthful indormation πŸ˜›

blazing nest
#

I don't use the generics of it

brisk heart
trim tangle
#

If you want stricter typing, you might want to be more specific about the parameters

brisk heart
#

There's some dependency injection stuff going on

#

So while it technically takes in no arguments the function can be defined with multiple

trim tangle
#

I think typing.Callable is the same as typing.Callable[..., Any] but I might be wrong

brisk hedge
brisk heart
brisk hedge
#

If you can represent the transformations in typing that would be fab

brisk heart
brisk hedge
#

using a plain Callable is equally type complete as using Callable [..., Any]

#

Well, it should be

#

Idk whether pyright whines about it

brisk heart
#

Not according to pyright

#

And mypy is even worse with it so ye

brisk hedge
#

Well in that case, if the callbacks really can take in any amount of arguments, they can only be typed as such

trim tangle
brisk heart
#

Something pyright made up

trim tangle
#

Ah

#

Well, I suppose there isn't a lot of benefit from typing if the library is dealing with very dynamic things and is often using Any

blazing nest
#

Sure, it's the same thing, but the --verifytypes is extremely specifically about being explicit about public-facing types

trim tangle
#

Yeah I think you're right

#

Makes sense

brisk heart
#

btw guys idk if this is a pyright specific question but how the heck can I use the python wrapper for pyright without having it reinstall pyright ever time

#

It adds like 10s to my CI

trim tangle
#

Do you really need the python package in the CI? Maybe just run node? I suppose that might be faster

#

I don't know how you're running the CI but you should be able to cache container layers, right?

brisk heart
#

if I use npx it also reinstalls so idk

#

And it'd probs be a pain to get venvs working

brisk heart
trim quartz
#

so I have a hacky decorator that modify a function's signature, e.g. inject a default value

def inject_default(**kwargs):
    def wrapper(func):
        func.__kwdefaults__ = func.__kwdefaults__ or {}
        func.__kwdefaults__.update(kwargs)
        return func
    return wrapper

@inject_default(a = 2)
def func(*, a: int):
    return a + 1

is there any way to reflect that modification to type checker (pylance)?

blazing nest
#

Nope πŸ˜”

tranquil turtle
#
PS1 = ParamSpec('PS1')
PS2 = ParamSpec('PS2')
R = TypeVar('R')

def inject_default(**kwargs: PS1) -> Callable[[Callable[PS2, R]], Callable[Unpack[PS1, PS2], R]]:
    def wrapper(func):
        func.__kwdefaults__ = func.__kwdefaults__ or {}
        func.__kwdefaults__.update(kwargs)
        return func
    return wrapper # type: ignore

@inject_default(a = 2)
def func(*, a: int) -> int:
    return a + 1
``` idk how to use ParamSpec πŸ™ƒ
hearty shell
#

ParamSpec cant help here, the best it can do is maintain the same signature post "decoration"

acoustic thicket
#

dont think any type system in the world would be able to help here tbh

trim quartz
#

I'm looking through pydantic but I still don't understand what kind of magic did it use to display **data as field1: type1 = default1, ...

humble ice
#

how do i correctly type hint an argument that is either a tuple of strings or an empty tuple? ()

humble ice
#

bc length of 0 does fall under: "To specify a variable-length tuple of homogeneous type, use literal ellipsis"

#

?

#

if so, makes sense. was probably overthinking this

oblique urchin
humble ice
trim quartz
#

oh so it's _dataclass_transform_ that does magic
thanks for the answer

fervent sierra
#

Hello, everyone

Could anyone help me check something in pyright, please? I reported a bug which always happens for me but doesn't for the main pyright developer, but I can't figure out if something is wrong strictly on my end. I've tried all sorts of new, untouched environments, to no avail: the error is always there.

This is the issue: https://github.com/microsoft/pyright/issues/3431

And this is the code that I wanted someone to run through pyright 1.1.244 for me:

# pyright: strict, reportUninitializedInstanceVariable=error

from dataclasses import dataclass

@dataclass
class MyClass:
    # error: Instance variable "y" is not initialized in the class body or __init__ method (reportUninitializedInstanceVariable)
    y: int

Alternatively, if anyone has any idea of what could be causing the issue for me (and not for the main dev), I'd highly appreciate any hints =)

GitHub

Describe the bug Using reportUninitializedInstanceVariable reports errors on fields that do not have a default value. To Reproduce # pyright: strict, reportUninitializedInstanceVariable=error from ...

hearty shell
#

Yeah I get the same error, this does look like a bug

fervent sierra
#

Thanks a lot!

fervent sierra
molten mason
#

Can someone explain what this means? And how to approach fixing it?

Expression of type "List[Self@Map]" cannot be assigned to return type "list[Map]"
  TypeVar "_T@list" is invariant
    Type "Self@Map" cannot be assigned to type "Map"

Map is the class I'm currently in

@classmethod
async def random(cls, amount: int) -> list[Map]:
    """Fetch a random amount of Map documents.
    Args:
        amount (int): Number of documents to fetch.

    Returns:
        List[Map]: List of Map documents.
    """
    return (
        await cls.find()
        .aggregate([{"$sample": {"size": amount}}], projection_model=cls)
        .to_list()
    )
fervent sierra
#

If you don't need to be able to modify that list, you might be able to get away by returning just a Sequence[Map], since a Sequence is covariant with the element type

#

Alternatively, maybe you could also return a list[Self] in the random function, in the same way that find seems to be doing it. I'm not sure if the Self type is already stabilized, but if it isn't I think the syntax would be something like:

T = TypeVar("T")
class MyClass:
    async def random(cls: Type[T], amount: int) -> list[T]:
        ...
molten mason
#

Thank you for the clear explanation! I wont be modifying the list so I used Sequence and it worked.

#

No more yelling from my type checker :-)

oblique urchin
devout barn
#

What does this error mean?

from collections.abc import Callable


class Wrap(Callable): # Argument to class must be a base class
                      # - Pyright
    def __call__(self):
        ...

works fine during runtime

oblique urchin
#

Interestingly pyright doesn't complain about inheriting from Union, which I'm pretty sure doesn't work at runtime

clever bridge
#

If I have a function that basically just wraps a method of an instance attribute but provides an argument or two (like functools.partial but on a method of an instance attribute). Is there a good way to typehint that without having to rewrite the entire signature again and again?

#

My best idea is a protocol class

hearty shell
#

Could you provide an example? I am not sure what you mean by method of an instance attribute

pastel egret
#

Perhaps you're looking for the new ParamSpec?

clever bridge
#
class CommonCommand:
    interaction: Interaction

    def shortcut(self, *args, **kwargs):
        return await interaction.respond(type, *args, **kwargs)
clever bridge
clever bridge
hearty shell
#

Do you have control over the Interaction class?

clever bridge
#

Yes but actually no

pastel egret
#

You'd need to make the command generic, probably also the interaction class.

clever bridge
#

I wrote the library

hearty shell
#

Yeah, you would need to make Interaction generic over the P ParamSpec and return of the method respond, then use those in the shortcut method, I think that would work

clever bridge
#

Hmm

hearty shell
#

I am double checking, I am not too sure

clever bridge
#

πŸ‘

#

Honestly I'm probably just going to add the methods to Interaction, only issue is uh, I still don't want to duplicate the signature

#

And if possible would like to avoid making another one of my classes Generic

pastel egret
#

Could potentially also make Interaction here a protocol.

clever bridge
#

Hmm

#

Honestly I don't know enough to know how that would help

#

I'm just going to copypasta honestly

#

It'll get the job done I maintain the lib anyway and aren't planning any breaking changes to this so yeah

#

Unless I can find an easy solution

#

Then I can remove my other copypastas

hearty shell
#

Yeah if all you want is just copy the signature that should be the easiest, not sure how this can be achieved for on class in isolation

#

The signature is not even dependent on instantiation right?

clever bridge
#

I've already done it a couple times and it's always annoying when I change things, but I think I'm done with those types of changes now so I don't think my lib's going to change it

#

Nope?

hearty shell
#

It is just a TypeOf kind of operation you want

clever bridge
#

Sure?

#

It's like, uh

#
class Interaction:
  async def respond(self, type: InteractionType, *, embeds: list[Embed], content: str) -> None:
    ...

class CommonCommand:
  def __init__(self, interaction: Interaction) -> None:
    self.interaction: Interaction = interaction

  async def alias_respond(self, *, embeds: list[Embed], content: str) -> None:
    return await self.interaction.respond(InteractionType.SUPER_LONG_NAME, embeds=embeds, content=content)
#

But with six or ten arguments instead of just embeds and content

hearty shell
#
from typing import *

P = ParamSpec('P')
R = TypeVar('R')
R_co = TypeVar('R_co', covariant=True)



class ProtoInteraction(Protocol[P, R_co]):
    async def respond(self, type: object, *args: P.args, **kwargs: P.kwargs) -> R_co: ...

class Interaction:
    async def respond(self, type: object, *, embeds: list[float], content: str) -> None: ...

class CommonCommand(Generic[P, R]):
    def __init__(self, interaction: ProtoInteraction[P, R]) -> None:
        self.interaction: ProtoInteraction[P, R] = interaction

    async def alias_respond(self, *args: P.args, **kwargs: P.kwargs) -> R: ...


foo = CommonCommand(Interaction())
reveal_type(foo.alias_respond)  # Type of "foo.alias_respond" is "(..., embeds: list[float], content: str) -> Coroutine[Any, Any, None]"
#

I think this does it? Not too sure what (..., embeds: list[float], content: str) means

#

the ... part x)

#

@clever bridge

clever bridge
#

I think it does

#

    class RespondAlias(Protocol):
        def __call__(
            self,
            *,
            content: Missing[str] = quarrel.MISSING,
            embeds: Missing[list[quarrel.Embed]] = quarrel.MISSING,
            # allowed_mentions: Missing[AllowedMentions] = MISSING,
            ephemeral: Missing[bool] = quarrel.MISSING,
            # attachments: Missing[Attachment] = MISSING,
            tts: Missing[bool] = quarrel.MISSING,
            grid: Missing[quarrel.Grid] = quarrel.MISSING,
            choices: Missing[list[Choice]] = quarrel.MISSING,
            # modal: Missing[Modal] = MISSING,
        ) -> None:
            ...

class CommonCommand:
    async def respond_with_message(self, *args: Any, **kwargs: Any) -> None:  # type: ignore
        return await self.interaction.respond(
            quarrel.InteractionCallbackType.CHANNEL_MESSAGE_WITH_SOURCE, *args, **kwargs
        )

    respond_with_message: RespondAlias

This seems to work too

#

Ignore the indentation

#

Anywho, thank you for the help! I need to sleep before I fall over now so night! πŸ™‚

hearty shell
wicked scarab
#

How to typehint "an iterable of strings with a length of 20" or something like that?

soft matrix
#

You can call iter then next will work 20 times?

#

Something that returns 20 strings at a time is possible but what I just described isn't

#

The best way to express that would be to use Annotated

trim tangle
#

does mypy support this?

pure dome
trim tangle
#

why specifically 20, that is?

#

what's the benefit over just Iterable[str]?

soft matrix
wicked scarab
hearty shell
#

You cannot apply length constraints to types in python

#

Best you can do is tag types with a length param

#

Which I don’t think would be very useful here

trim tangle
#

Not everything needs to be specified at compile-time, especially very dynamic things that depend on business rules and user input.

#

You can do that in some languages (like Agda) but the costs are diminishing in most programs

hearty shell
#

I dont think you need dependent types for that, https://github.com/pschanely/CrossHair might give you somewhat the ability to do that, it uses a SMT solver under the hood, so if it anything like liquid haskell you should be able to give length constaints to your hearths desire

#

*hearths desire as long as it can find a solution x)

trim tangle
hearty shell
#

Humm well Idk how the library works, but since it is an api call they would probably need to postulate that to CrossHair and then it should be able to use that as a tag that can be worked on

#

I am not sure though

trim tangle
#

is symbolic execution really that different from dependent types? πŸ€”

hearty shell
#

From my understanding symbolic execution gives you refinement types, which are a sort of dependent types but without the overhead that comes with them

hearty shell
#

Humm, the documentation is rather poor, but it seems it can do it

def gets_iterable() -> List[int]:
    return list(range(30))

def uses_iterable(it: List[int]):
    '''
    pre: len(it) <= 20
    '''
    return None

def main():
    '''
    post: __return__ == None
    '''
    uses_iterable(gets_iterable())
    return None

/tmp/main.py:19: error: PreconditionFailed: Precondition "len(it) <= 20" was not satisfied before calling "uses_iterable" for any inputmain()

livid cypress
#

i have a function that takes a dictionary(TypedDict) as a input and returns a new dictionary(TypedDict) that extends over the previous one by adding some keys in it. my static type checker is throwing out errors when i am annotating the copy of the copy of the dictionary with the second TypedDict. how do i resolve this

#

why cant i send screenshots here?

soft matrix
#

TypedDicts aren't currently valid bounds (atleast in mypy) afaik this isn't expressible in a good way without an intersection type

livid cypress
#

how do i define a intersection type then?

#

to be more explicit, what is an intersection type?

soft matrix
#

they dont currently exist in python

#

!pep 483

rough sluiceBOT
#
**PEP 483 - The Theory of Type Hints**
Status

Final

Created

19-Dec-2014

Type

Informational

soft matrix
#

however mentions the proposed semantics of them

livid cypress
#

cool, so i cant deal with this anyhow?

#

i can only #pyright: ignore the error then?

soft matrix
#

oh actually ignore everything ive said

#

just do ValidatedDoc(valid=True, exceptions=[], **doc)

#

that should work

livid cypress
#

oh

#

it works

#

nice

#

thanks @soft matrix

brazen jolt
#

Interesting that pyright can figure out the class name from type() class construction, but doesn't detect the parameters/functions passed into it as __dict__

#

is this a wontfix problem for pyright, or is it a bug?

blazing nest
hearty shell
#

Probably a wontfix, I am surprised that this even works x) It would be nice if pyright had it though, not for that but for capturing kwargs

blazing nest
#

Although, I guess it may be able to do something with dataclass_transform?

#

Technically that dictionary argument can be interpreted kind of like attrs/pydantic/dataclasses does kwargs?

#

Might be a stretch πŸ€·β€β™‚οΈ

soft matrix
#

isnt it just using the name of the variable?

brazen jolt
#

well, if the string is named anything else except what the variable is named, it shows Type[_] so it does seem to do something with it

soft matrix
#

weird

brazen jolt
trim tangle
#

maybe it's a playground glitch?

#

try again

brazen jolt
#

oh that's probably it

soft matrix
#

must just use the same thing as TypeVars?

brazen jolt
soft matrix
#

i mean the name detecting

#

cause it will error if you do T = TypeVar("U")

brazen jolt
#

yeah, Foo is working too now in my environment

#

so, the name seems to get picked up from the string properly

hearty shell
#
name = "A" + "B"
TestCls = type(name, (), {})
reveal_type(TestCls)  # "Type[AB]"
#

Good job pyright x)

#

I tried giving a typeddict as the namespace param to see if it work but you cant give it because it expects a dict[str, Any]

indigo locust
#

In case anyone here as any input

livid cypress
#

How can i type re.match.groupdict() dictionary? docs say it now support '[]' but i found nothing that can be useful.

trim tangle
#

the return value of groupdict is a dict (dict[str, str | Any] as you can see), the documentation about Match and Pattern isn't very relevant

livid cypress
#

oh then i will directly create a instance of LinkedDoc

trim tangle
#

what is LinkedDoc?

livid cypress
#

a type

#

gimme a min

#

ill be copyinh pasting code

#
            linked_doc = i.split(":")[1].strip()
            reg = re.compile(
                r"^(?P<doc_no>-?\d+)\/(?P<doc_year>\d+)\s*of\s*SRO\s*(?P<sro_code>\d+)$"
            )
            searched = reg.match(linked_doc)
            if not searched:
                raise ValueError(
                    f"could not parse doc field for revoking doc, {repr(linked_doc)} does not conform to the format that the regex {reg.pattern} matches on. If this appears frequently, try adjusting the regex or check if the input data is really a valid format"
                )
            temp_data_dict["revokes_docs"].append(searched.groupdict())  # type: ignore
#

this is the code block

#
class LinkedDoc(TypedDict):
    """Represents a linked document"""

    doc_no: int
    doc_year: int
    sro_code: int
#

the type LinkedDoc a TypedDict

trim tangle
#

ah, well in that case you might want to just do a # type: ignore.

#

type checkers aren't that smart

#

i.e. they can't inspect regular expressions

#

Hmmm wait, the type is not quite right. all attributes of the dict will be strings, not integers @livid cypress

livid cypress
#

yep yep got that

#

i did this

#
...append(
                LinkedDoc(
                    **dict(
                        map(
                            lambda x: (str(x[0]), int(x[1])),
                            searched.groupdict().items(),
                        )
                    )
                )
            )
#

and the error was gone

trim tangle
#

but why?

#

it doesn't make it any more safe and it's pretty confusing

livid cypress
#

what else can i do instead of that?

trim tangle
#

# type: ignore

livid cypress
#

that was what i was doing earlier

#

encountered an error deep down where this code block was a dependancy

#

thought of fixing it in a better way

#

haha

trim tangle
#

Another option would be to make a dataclass

@dataclass(frozen=True)
class LinkedDoc(TypedDict):
    doc_no: int
    doc_year: int
    sro_code: int

then do ```py
.append(LinkedDoc(
doc_no=int(searched["doc_no"]),
doc_year=int(searched["doc_year"]),
sro_code=int(searched["sro_coe"]),
))

#

generally, if you have a fixed set of fields, a dataclass is better

#

TypedDict is more for specifying existing APIs that use dictionaries

livid cypress
blazing nest
livid cypress
#

shouldve used dataclasses haha

#

ignore all this for a moment, what is that re.Match thing though?

#

what does [] mean

trim tangle
#

re.Match is the object that re.match and re.search return

livid cypress
#

i know the slicing use and it being used as generic type acceptor

#

but how do i use it here?

trim tangle
#

!e

import re
print(re.match(r"(foo)(bar)", "foobar"))
rough sluiceBOT
#

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

<re.Match object; span=(0, 6), match='foobar'>
trim tangle
#

you're already using this object in your code

#

but you don't need an explicit type annotation

livid cypress
#

okay

#

alright gotcha

trim tangle
#

Unrelated to typing, but I would remove this docstring ```py
"""Represents a linked document"""

trim tangle
#

!e
because I believe you can match on bytes as well

import re
print(re.match(b"(foo)(bar)", b"foobar"))
rough sluiceBOT
#

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

<re.Match object; span=(0, 6), match=b'foobar'>
trim tangle
#

yep

hearty shell
#

A bytes pattern, interesting

#

I didnt know those were a thing

chilly ruin
#
if opcode == GatewayOpcode.DISPATCH:     β–  Condition will always evaluate to False since the types "Literal[0]" and "Literal[GatewayOpcode.DISPATCH]" have no overlap

where GatewayOpcode is an enum.IntEnum subclass

#

pyright strict, works in repl

#
>>> from enum import IntEnum
>>> class MyEnum(IntEnum):
...  h = 0
... 
>>> 0 == MyEnum.h
True
soft matrix
#

That does sound like a bug although I'm not sure where

#

Yeah it's a bug in pyright and not in typeshed afaict

nimble spade
#

Anyone give me the proper way to typehint that a function returns a list of tuples, pleeeease??

oblique urchin
#

-> list[tuple[some_type, some_other_type]]

nimble spade
oblique urchin
#

upgrade to 3.9 or use from typing import List, Tuple instead

nimble spade
oblique urchin
#

on 3.10 you can just use list and tuple lowercase

nimble spade
oblique urchin
#

either you're not using 3.10 or you're shadowing the list and/or tuple name

nimble spade
#

I'm definitely using python 3.10.4, but I have to import and use the uppercase

#

I'm using list and dict now

nimble spade
#

I use vscode and the jedi lang server, and I can't use matchcase with jedi, only if I change the language server to pylance can I use matchcase

soft matrix
#

add print(__import__('sys').version_info) to your script

nimble spade
#

I don't know what it means

soft matrix
#

It means you're running your program in 3.8

oblique urchin
#

3.8.10 to be precise

soft matrix
#

Try running python3.10 script_name.py

nimble spade
#

lol

#

it say 3.10.4 64 bit on the bottom bar

soft matrix
#

Maybe you aren't activating a venv then

nimble spade
#

get the same sys info response

#

and I can use matchcase, which is 3.10 but only if I change my language server from jedi to pylance

soft matrix
#

The language server doesn't effect how your code runs

nimble spade
#

no, but it effects how the ide plugins take your code

#

and I can't use matchcase from 3.10 without changing my language server because jedi hasn't updated their engine or something like that

#

if I use it with jedi I get red files that I couldn't run if I wanted to

solar sphinx
#

hey all, I'm trying to type a function that receives a response object (doing web scraping), and I'm not sure how to type the input parameter. the parameter is a response object from requests, when I do type(res) I get <class 'requests.models.Response'> does that mean I should add the following annotation in the function signature? def normalize_response(res: requests.models.Response) -> list: or how should I do it exactly?

leaden oak
solar sphinx
#

awesome, I'll do that. thx @leaden oak

indigo locust
#

Wowwwww

#

Pycharm's linter never ceases to disappoint

wraith tulip
#

Pycharm bug?

blazing nest
#

Since you're on 3.10, yeah.

blazing nest
#

Hm, where can I follow the progress on generic typed dictionaries?

oblique urchin
hearty shell
#

Yup

#
from typing import *
from typing_extensions import TypedDict

T = TypeVar('T')

class A(TypedDict, Generic[T]):
    foo: T
    
c: A[int] = {'foo': 42}
e: A[int] = {'foo': '42'}  # "str" is incompatible with "int" (reportGeneralTypeIssues)
blazing nest
#

Then I suppose I am waiting on runtime support on older versions (if possible 😬)

hearty shell
oblique urchin
# hearty shell Does this not mean it is essentially supported at runtime through typing_extnest...
    ...:     a: int
    ...: 

Traceback (most recent call last):
  File "/main_instance_shell/jelle/venv3.9/lib/python3.9/site-packages/IPython/core/interactiveshell.py", line 3397, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-21-eac8c22158fe>", line 1, in <cell line: 1>
    class TD(TypedDict, Generic[T]):
  File "/main_instance_shell/jelle/venv3.9/lib/python3.9/typing.py", line 1947, in __new__
    raise TypeError('cannot inherit from both a TypedDict type '
TypeError: cannot inherit from both a TypedDict type and a non-TypedDict base class
hearty shell
#

Humm, I just tried in 3.9

#

Seemed fine

oblique urchin
#

which version of typing-extensions?

hearty shell
#

4.2.0

#
Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing_extensions import TypedDict
>>>
>>> from typing import Generic
>>>
>>> class A(TypedDict, Generic): ...
...
>>>
oblique urchin
#

interesting, I was on 4.1.1 but it indeed works on 4.2. Not sure what we did to make it work

rare vapor
stray tide
#
def foo():
    async def wrapper() -> int:
        return 1
    return wrapper

How do I type foo?

plain dock
#

!d typing.Callable (bot is case sensitive)

rough sluiceBOT
#

typing.Callable```
Callable type; `Callable[[int], str]` is a function of (int) -> str.

The subscription syntax must always be used with exactly two
values: the argument list and the return type. The argument list
must be a list of types or an ellipsis; the return type must be
a single type...
stray tide
#

Callable[[], Coroutine[]]? or what i'm confused since the docs have parameters and my functions don't

plain dock
#

pylance's type linter set to strict-mode does not yell at this:

from typing import Callable, Awaitable
def func() -> Callable[[], Awaitable[int]]:
    async def wrapper()-> int:
        return 1
    return wrapper
plain dock
stray tide
plain dock
#

maybe it's not the best example

vagrant fox
#

nextcore/gateway/shard.py: error: Name "IdentifyConnectionProperties" is not defined [name-defined]
mypy v0.950

Theres no line number... and that class doesn't exist in that file at all.. however iirc it does exist in https://github.com/Bluenix2/discord-typings which I use.

#

seems to not error on pyright however

rustic gull
#

hello everyone! Is there anyone with knowledge of machine learning?

#

I want to learn about logic implementation with machine learning in python

plain dock
brisk heart
vagrant fox
hearty shell
#

Might be due to the fact that mypy doesnt support final typed dicts? The message wouldnt not be that in that case though so not sure

vagrant fox
rough sluiceBOT
#

discord_typings/gateway.py lines 44 to 53

# The leading dollar sign makes this an invalid attribute in Python so we need
# to use this way of defining typed dicts.
IdentifyConnectionProperties = final(TypedDict(
    'IdentifyConnectionProperties',
    {
        '$os': str,
        '$browser': str,
        '$device': str
    }
))```
oblique urchin
#

that looks like it would really confuse mypy

#

it just sees it as a normal function call

grave fjord
#

func()().send(None) raises StopIteration(1)

oblique urchin
#

Callable[[], Awaitable[int]] would be more practical

grave fjord
#

Depends what it's used for

blazing nest
loud osprey
#

How do I type hint for instance of a class?

#
def repo_path(repo, *path):
    return os.path.join(repo.gitdir, *path)
``` Here, `repo` is an instance of a class `GitRepository`. How do I typehint it?
plain dock
loud osprey
#

even though GitRepository is defined

blazing nest
#

The issue isn't how you have written, it is how you define GitRepository. Can you show where that is?

loud osprey
#

alright. silly mistake, It was defined below this function definition

#

I moved the function below and it does not complain

plain dock
#

it wasn't the problem this time, but in future cases: your pyright error is the same error it gives if you use a class as a type hint inside the same class.
from __future__ import annotations fixes that problem via forward references

cedar sundial
#

For declaring a function argument to only accept say 2 different string values, would you still annotate that argument with Literal['str1', 'str2'] or is there a better way to do so?

hearty shell
#

If the return is of the same type for both strings yes, otherwise an overload

buoyant swift
#

an enum

trim tangle
#

It depends ℒ️
Consider making an Enum

#

Sometimes there are more options, like the Strategy or Adapter pattern.

trim tangle
cedar sundial
# trim tangle Maybe tell more about your function?

I had thought about using an enum first but thought it was easier to just use strings. I'm making a decorator which checks an instance attribute against the argument passed in and either raises an error or passes on with the function

cedar sundial
#

Sure

def pipeline(name: Literal["non_biological", "biological"]):
    if name not in ("non_biological", "biological"):
        raise ValueError(f"{name} is not one of ('non_biological', 'biological')")

    def decorator(func):
        def wrapper(self, *args, **kwargs):
            if name != self.pipeline:
                raise FunctionNotCompatibleError(func.__name__)
            ret = func(self, *args, **kwargs)
            return ret

        return wrapper

    return decorator
fervent sierra
fervent sierra
cedar sundial
hearty shell
#

Why not use good old inheritance?

cedar sundial
#

Something I'm working on. With that thought though, I have a class with most of the shared functions and I am subclassing that with just a pipeline class. The idea here was to use that decorator to distinguish between the two different pipeline methods. Would it be better to subclass them into the two different pipelines and then have a final pipeline class which subclasses both of those since we want just 1 class.
β€β€β€Ž β€Ž Biological
/
Shared Pipeline
\ /
Non-biological

hearty shell
#

I mean, if you need to have all the functionality in a single class then I am not sure what the benefit is of having a Biological and Non.. if you are only going to use the Pipeline class

#

One thing I would suggest though it also make the Pipeline class generic over the two options

#

That would give you two types with the same class, which is kind of what you are doing via the decorators

#
from __future__ import annotations
from typing import *

T = TypeVar('T', Literal['biological'], Literal['non_biological'])

class Pipeline(Generic[T]):
    def __init__(self, pipeline: T):
        self.pipeline = pipeline

    def bio_method(self: Pipeline[Literal['biological']]): ...
    def nonbio_method(self: Pipeline[Literal['non_biological']]): ...

bio = Pipeline('biological')
nonbio = Pipeline('non_biological')

bio.bio_method()
bio.nonbio_method()  # Erros

nonbio.bio_method()  # Erros
nonbio.nonbio_method()
#

That way if you have a function that accepts just on type of pipeline, you can mark as doing so

cedar sundial
#

Interesting. I've not played around with generics yet but this sounds like a decent alternative to the decorator. Thank you

hearty shell
#

You might want to combine both as this only gives you a type warning, while the decorator only gives you a runtime warning

#

That is, if you want the runtime checking

fiery goblet
#

you can go with Protocol typing for this, if the Pipeline is requiring the classes that are not using inheritance to implement functions it can use

#

like this

#

your protocol would just define the interaction/method calls it would make to the two objects "bio" and "non_bio" from the pipeline

#

although with this, you'd be passing off most of the execution to the "bio" and "non_bio" class, unless they have generic way of processing in the pipeline

fiery goblet
#

Idk how mypy and pyright play with protocol typing tho

fiery goblet
#

@verbal spoke just had a good idea and suggested this. As it is cleaner imo.
Only thing is the your "pipeline" class will require you to pass in the "Bio" or "NonBio" each time

verbal spoke
rose root
#
class Foo:
    def __init__(self) -> None:
        self.allowed: set[str] = {}  # Cannot assign member "allowed" for type "Foo"
``` Is this supposed to be happening?
#

Ah, doing ```py
class Foo:
def init(self) -> None:
self.allowed: set[str] = set()

trim tangle
#

yep, {} is a dictionary πŸ™‚

grave fjord
#

I get burnt by this often

grave fjord
fiery goblet
#

wouldn't you want

#

!docs typing.AsyncGenerator

rough sluiceBOT
#

class typing.AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra])```
An async generator can be annotated by the generic type
`AsyncGenerator[YieldType, SendType]`. For example:

```py
async def echo_round() -> AsyncGenerator[int, float]:
    sent = yield 0
    while sent >= 0.0:
        rounded = await round(sent)
        sent = yield rounded
```...
grave fjord
fiery goblet
#

ah

blazing nest
#

I've been thinking about this and I am not sure whether that should change.

#

An AsyncIterator can rightfully have it defined as Awaitable, but for an AsyncGenerator we should be able to know that it is a Coroutine

reef nova
#

Hello! I tried using a type alias as an annotation and flake8 flagged it as invalid syntax. I had it typed (and working) like so:

async def say(ctx: cctx | sctx | Channel,
              description: str,
              components: ActionRow | Button | SelectMenu | list[ActionRow] |
                  list[Button] | list[SelectMenu] | None = None,
              hidden: bool = False) -> Message:
    """Post a message in an Embed."""```
However, that long components type union is repeated all over the place and I wanted to save myself the copy-pasting, so I tried doing this:

```py
Components = ActionRow | Button | SelectMenu | list[ActionRow] | list[Button] | list[SelectMenu] | None

async def say(ctx: cctx | sctx | Channel,
              description: str,
              components: Components = None,
              hidden: bool = False) -> Message:
    """Post a message in an Embed."""```
Flake8 doesn't like this, though, and marks `components: Components` as an invalid syntax (E999). I also tried

```py
Components: TypeAlias = ActionRow | Button | SelectMenu | list[ActionRow] | list[Button] | list[SelectMenu] | None```
and

```py
Components = TypeVar("Components", ActionRow, Button, SelectMenu, list[ActionRow], list[Button], list[SelectMenu], None```
but flake8 flags them the same way. Am I doing something wrong, or is my linter mistaken?
oblique urchin
reef nova
#

Clearly I'm picking up some habits from doing Java on IntelliJ.

#

Thanks, Jelle!

blazing nest
#

Note that you can enable auto-save if you wish to πŸ˜…

reef nova
#

O_O WHAT that's AMAZING I'm Googling this RIGHT NOW

grave fjord
#

So I think it's a mypy bug that it gets rejected

hearty shell
#

Isnt that just as much of a bug as this though?

import types

def foo() -> types.FunctionType:
    def bar(): ...
    return bar  # error: Incompatible return value type (got "Callable[[], Any]", expected "FunctionType")
#

I think most types from the types modules arent "supported"

#

Humm a few actually worked

from types import *

class A:
    def foo(): ...

def foo():
    yield
    
a1: NoneType = None  # Error
a2: FunctionType = foo  # Error
a3: GeneratorType = foo()  # Ok
a4: CodeType = foo.__code__  # Ok
a5: MethodType = A().foo  # Error
a6: BuiltinFunctionType = len  # Error
a7: WrapperDescriptorType = object.__str__  # Error
a8: MethodWrapperType = object().__str__  # Error
a10: NotImplementedType = NotImplemented  # Ok
a9: MethodDescriptorType = str.join  # Error  # Ok
a11: ClassMethodDescriptorType = dict.__dict__['fromkeys']
a12: GetSetDescriptorType = A.__dict__['__dict__']  # Ok
a13: MemberDescriptorType = type.__dict__['__itemsize__']  # Error
mossy ridge
#

hey guys, not sure this is the right channel for that question,
but anyone might know how can i convert a google.cloud.pubsub_v1.types.PullResponse into a python dict or json?

soft matrix
#

This isn't really the right channel but since it's probably a protobuf use google.protobuf.json_format.MessageToJson/Dict

mossy ridge
#

hmm ok, thanks ill look into that πŸ™‚

fervent sierra
# hearty shell I think most types from the types modules arent "supported"

As far as I understand, there's a lot of types in the typing module that can be considered "internal compiler stuff", and are by design not recognized by the type checkers (e.g.: here's the author of pydev talking about MethodType: https://github.com/microsoft/pyright/issues/2524#issuecomment-958184499)
If they are not mentioned in any PEP, they are probably not for use by normal code.

GitHub

Is your feature request related to a problem? Please describe. I would like to be able to type-safely access the bound self object from a bound method. Describe the solution you'd like ...

hearty shell
#

Yeah most of them I dont see how any value would be added by supporting them in real code

#

Other then for consistency, one is not wrong to expect those to work Imo, but then you have stuff like types.UnionType, and types.GenericAlias which start breaking things

#

unrelated
What is the point of Any | None?, I see in typeshed but in mypy Any just absorbs None

soft matrix
#

some people think Any | None is not the same as Any

#

i dont really see the distinction

void panther
#

exposes the None to the user and signifies it is a special value

hearty shell
#

Yeah I guess it does have some value for documentation

#

but in mypy Any just absorbs None
It is interesting that in Pyright, it does not absorb it instantly like mypy, but if you do a is None check then it goes away

#
def foo() -> Any | None: ...
a = foo()

reveal_type(a)  # Type of "a" is "Any | None"

if a is None:
    reveal_type(a)  # Type of "a" is "Any | None"
else:
    reveal_type(a)  # Type of "a" is "Any"
    if a is None:
        reveal_type(a)  # Type of "a" is "Any"
terse sky
#

I definitely think Any should absorb None, in the python type system

#

that's a very weird choice by pyright imho

#

Any is not a top type; it's literally anything, so None of course falls into that

#

some other languages use Any for their top type, and if you also have nullability then technically Any is not actually the top type, but rather Any | None is the top type

#

but in python that still wouldn't apply because the language doesn't really treat None as special in that regard.
None is still an object, so object | None still doesn't make sense, it's the same as object

brisk heart
#

I personally like the feature. It gives some clear default for arbitrary objects

#

without it I'd have to raise n catch exceptions which just makes it annoying

terse sky
#

I mean it's a nice feature, just not consistent with python

#

In e.g. Kotlin, Any is almost the top type: the actual top type is Any?. That works because null is a special built in thing.
If you wanted to pretend python is like that in your static type system, I could live with it, but only in the context of object. Not Any.

trim tangle
#

Hmm well... an operation P is allowed on A | B iff P is allowed on A and P is allowed on B.
Suppose that P doesn't work with None. Then P also doesn't work on Something | None, including Any. Therefore, Any | None is not the same as Any

I guess that's the rationale 🀷

#

if you want Any, just annotate as Any

#

A U B is surely the same as A if B is a subset of A. But Any doesn't really follow set theory rules because Any is both a subtype and supertype of any other type.

rare vapor
#

using TypeVarTuple, is it possible to correctly type builtins.zip now?

trim tangle
#

nope that's not possible

#

same for asyncio.gather

rare vapor
#

thanks

#

I read the PEP, but couldn't tell if I was just missing something

hearty shell
#

Ohhh yeah you are right, from the perspective of using the type Any | None it is different from Any, only when it comes to "what is acceptable" that it is the same

trim tangle
hearty shell
soft matrix
trim tangle
hearty shell
#

No it should not, I said I agree with you :P

brisk heart
#

it'd be ideal if it collapsed to Any by default but still allowed Any | None when stated explicitly

hearty shell
solid pecan
#

Is there something like a typescript compiler for typed python that goes through and verifies everything and then spits out a plain py script?

hearty shell
#

What would be the use in that? In Python type hints can have runtime effects unlike typescript

solid pecan
#

Can it? I didn’t know. The hinting seems weak compared to TS

hearty shell
#

Annotating at module level adds those hints to the __annotations__ global dictionary, in functions to the function.__annotations__ and same for classes. Many libraries make use of those hints to make autoconvertion of data types and probably for other stuff as well

#

Just in the stdlib there is the @functools.singledispatch deco that does runtime overloading based on annotations

trim tangle
#

I think the annotations are here to stay for a long time

#

I you want a backend language with a strong type system, well, pick a language with a strong type system πŸ™‚

hearty shell
#

It does make you wonder, what would have happened had python gone the typescript route πŸ€”

solid pecan
#

I have been using types in python since they came out, but never to much effect. I recently learned typescript and was amazed

trim tangle
#

I was blown away by typescript as well

#

my favourite so far is turning snake_case to camelCase and back on the type level

hearty shell
#

so less attention, less features?

solid pecan
hearty shell
#

Yeah it ignores until you turn on the strict option, but that is what I am saying, there is not much friction and yet most dont use it

#

So imagine if you had to install a whole different program just to compile python to get the static typing

terse sky
#

@trim tangle err that's the thing, you can't apply these rules to Any

#

Any | None doesn't really make any sense because if a function returns Any and that function returns None, then that's already ok

hearty shell
#

I think the problem is not when you try to fit that type into a hole, but specifically when you ask what members the union of the two types has, and in the case of Any | None, when you take the Union of the members you are only left with the members of None

carmine sigil
#
from typing import Generic, TypeVar, get_args

T = TypeVar("T")
V = TypeVar("U")

class MyClass(Generic[T, V]):
  def __new__(cls):
    print(get_args(cls))

MyClass[int, str]() 
```Output ```py
()
```I know this seems a bit weird but my use case makes use of something similar to this, I don't understand why the `__new__` function's `cls` arg which is the class itself never has the type arguments, it always returns `()` empty tuple , is there a way such that its possible to have the type arguments of the class that is called in the `__new__` function ?
trim tangle
#

Why do you want this though?

carmine sigil
# trim tangle Why do you want this though?

i was planning to make something like a function that could use generics, I would use a decorator to wrap, something like:

@generics(T, V)
def myfn(w: T, x: V, types = ()): #types is the tuple of generic arguments, I know this doesnt have any use but just curious to play with it
    pass
``` then i can do:
```py
myfn[int, str](2, "ok")
soft matrix
#

You can actually get them from orig_class

#

Oh wait not in new sorry

brisk heart
#

fastapi had a new release an hour ago and they added this along with other features

soft matrix
#

this isnt that bad a take imo

blazing nest
soft matrix
#

i think making sure that people are aware what optional actually means is a good thing

blazing nest
#

I wouldn't recommend they use Union[..., None] though

brisk heart
blazing nest
#

Here's my opinions:

  • T | None > Optional[T] > Union[T, None]
  • T | U | None > Union[T, U, None] | Optional[Union[T, U]]
soft matrix
#

idk unless the ? null op pep gets accepted i dont think theres much to be said as the 3.10 syntax is what youre meant to use

blazing nest
#

That's isn't to say that you shouldn't teach about Union[T, None] == Optional[T]

brisk heart
#

personally I use type aliases so often that I rarely even get to use Optional[Union[T, U]]

#

wonder when that problem last came up for me

blazing nest
#

Yeah but when I need to represent that typing I rather just but it in a Union together

brisk heart
#

oh yeah for sure

soft matrix
#

i also agree with your list, but i seriously think being more explicit here with members of your team is a good thing

blazing nest
#

It's not often, but the few times I needed to type it that's what I went with

blazing nest
brisk heart
#

well the actual proper solution is upgrading to 3.10 of course but I can say with confidence that I'll never use Union[T, None] in any project of mine that has to support 3.8

soft matrix
brisk heart
#

you know I feel like they recommend Union[T, None] over Optional[T] because of how many beginners get confused by it. I know there have been dozens of warnings littered around the docs just for that

soft matrix
#

yeah its cause the naming is very ambiguous especially in python

blazing nest
stark shuttle
#

Eh, T | None is IMO better than any explicit name - we just mean "or it can be none", and this is a direct and concise way to say so.

trim tangle
# brisk heart this is the worst take I've ever heard <https://fastapi.tiangolo.com/python-type...

I have personally seen a pretty experienced colleague get confused as to what Optional means (at least from past code, they were using weird aliases like Optional[Union[str, None]]).

But I don't think it's a good idea to just not teach Optional (unless you're targeting 3.10+, which might be reasonable in the near-ish future) because it will appear in other libraries as well. It's not that hard to remember just: "Optional[T] means exactly Union[T, None]"
In fact, the repr of Union[T, None] is Optional[T]

#

!e

from typing import Union

print(Union[str, None])
rough sluiceBOT
#

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

typing.Optional[str]
trim tangle
grave fjord
trim tangle
#

I'll try it out

lapis adder
hearty shell
#

If I tell you I have a function that takes an argument of type int and an optional str, I think if you have no prior knowledge it is not hard to see how one would be surprised that in order to call the function you would have to do fun(42, None)

trim tangle
acoustic thicket
#

i think Nullable would've been a good name

#

maybe Noneable, though that doesnt quite have the same ring to it

grave fjord
#

I think just having Union on it's own would have been fine

trim tangle
#

that's true, and that might be a reasonable style guide

#

but Optional is here to stay

#

well, until people switch to 3.10

lapis adder
#

what about just | None

soft matrix
#

That's the 3.10 approach

lapis adder
#

ah

grave fjord
#

| None works on 3.7 (but not for pydantic)

trim tangle
#

with __annotations__

#

yep

#

and also not for type aliases

grave fjord
#

They could probably make it work with a thing that parses the strings before evaling them

lean socket
#

hi all

I have a function that return a, b, c, d when I want to get a i can do function()[0] is there a way i can do function()[a] or something along these lines ?
not sure it it is the right channel her thought

brazen jolt
tranquil turtle
lean socket
blazing nest
#

I want to backport the slots kwarg of dataclasses, how do I use dataclass_transforms() to tell the type checker that slots will be set?

#

I see that slots is mentioned in the PEP, but my decorator's sole purpose is adding slots so I won't have that parameter: ```python
def _add_slots(...): ... # Backport of _add_slots from dataclasses' internals

@dataclass_transform()
def backport_slots(cls: Type[T]) -> Type[T]:
return _add_slots(cls)

#

Do I need to enforce using the decorator with parenthesis such that I can have slots: bool = True?

digital bolt
#

Is there a need for mypy when python 3.6 introduce type hinting or are the two completely different things?

blazing nest
#

MyPy is what checks your types

trim tangle
#

you can do x: int = "foo" and it will work.

#

the purpose of mypy, pyright and other tools is to look at these annotations and find mistakes

digital bolt
#

Ohhh I see, thank you

outer yarrow
#

What would be the way to validate the strucure or schema of a json/dict type?

Could I maybe use type hinting for this purpose?

blazing nest
#

If you use a library that interprets the typehints, then yes.

trim tangle
#

check out:

#

!pypi dataclass_factory

rough sluiceBOT
trim tangle
#

!pypi pydantic

rough sluiceBOT
#

Data validation and settings management using python 3.6 type hinting

oblique urchin
blazing nest
#

Not even if I force the decorator to be called with no argument (@my_deco())?

oblique urchin
#

Not sure why that matters

blazing nest
# oblique urchin Not sure why that matters

Because then I could do:

def backport_slots(*, slots: bool= True) -> Callable[[ClsT], ClsT]:
    def inner(cls: ClsT) -> ClsT:
        ...
    return inner

I haven't really gotten as far as to fully test this yet

oblique urchin
blazing nest
#

Why so? Isn't it statically determinable?

oblique urchin
#

I don't think PEP 681 asks that they look at the argument defaults, though you're right that they could

#

that's why there's a few params to dataclass_transform like order_by_default=

#

sorry order_default

blazing nest
#

Wow what a shame, so it forces the defaults of dataclasses? ```python
from typing import Callable, TypeVar

from typing_extensions import dataclass_transform

@dataclass_transform()
def test(init: bool = False) -> Callable[[type], type]:
def inner(cls: type) -> type:
return cls

return inner

@test()
class ABC:
a: int

_ = ABC() # Argument missing for parameter "a"
xyz = ABC(123)

blazing nest
blazing nest
oblique urchin
#

mypy doesn't support dataclass_transform at all

blazing nest
#

Oh lol

#

I ran it through Mypy's playground and it passed how I wanted to... but I didn't realize it did nothing hemlock

gray dust
#

Should I aim for zero errors on mypy? God, this is tough!

trim tangle
#

or maybe you have a different situation?

gray dust
#

Oh, I started with basic type hints on everything months before using mypy. Now there's just lots of fiddly incompatible types and return value errors

trim tangle
#

I see, I think it's not a good idea to use type annotations without a type checker. If an annotation is incorrect, it only confuses the reader (and also autocompletion and type checkers)

#

but yeah, ideally mypy should be quiet

gray dust
#

Yeah I definitely learned my lesson for the next project 😦
I've got it from 90 something down to 31 errors. Will keep going

trim tangle
#

Maybe don't think of it as pleasing mypy. These are actually inaccurate annotations, right?

#

what editor are you using, by the way?

gray dust
#

Yes, it is all correct suggestions from mypy. Just fussy and safe. Like my logic is generally ok accepting the return as either str or None, but the annotation is just "-> str"

gray dust
trim tangle
# gray dust VSCode

I would suggest using the Pylance extension. It's much more interactive than mypy, and I like its logic better

gray dust
#

Yeah I'm using Pylance. Don't see where its giving me annotation warnings though?

trim tangle
gray dust
#

OooO! Thanks!

#

Oh god. Red worms everywhere

trim tangle
#

you might want to turn off the mypy extension if you have it

plain dock
#

you can set it to 'strict' as well, but i don't recommend it. the strict-mode type checker hates just about everything, including some things that you can't fix. the python files for numpy, for example, are seas of red in strict mode

gray dust
#

Even in "basic" it is complaining about stuff in the requests lib in my venv

trim tangle
#

what's the error?

gray dust
#

Loads!

#

It's up to date too @ 2.27.1

brazen jolt
#

you can create pyrightconfig.json file and ignore the .venv directory

gray dust
#

Ah nice. I am reading that pylance uses pyright under the hood somewhere

brazen jolt
#

yeah, pylance is basically the name of the extension while pyright is the language server

#

pyright can do some other things, but it is mainly a type checker

gray dust
#

Great, thank you

blazing nest
# gray dust Loads!

Huh, do you have no .gitignore? I think Pyright reads that one, as I don't get this issue.

brazen jolt
#

pyright doesn't respect gitignore, but pylance might

gray dust
#

Ah interesting. I do have .venv in .gitignore, but its the parent dir, outside of the vscode project dir. That might explain it (i'm using Git outside vscode)

brazen jolt
#

I'm not sure if pylance actually picks up on gitignore, since I haven't used vscode for a while now, but it's quite possible, try adding a gitignore and see what happens, or just move to the parent folder in your case

gray dust
#
    def __init__(self, comitclient: Comit, entity: str, currency: str, reconcile: str = 'fresh') -> None:

        self.reconcile_mode = reconcile

        base_balance_url = f'/accounting/balances/{entity}/{currency}'
        if self.reconcile_mode == 'fresh':
            balance_url = f'{base_balance_url}/reconcile'
            mock_file = 'treasury_balances_reconcile.html'
        elif self.reconcile_mode == 'last_saved':
            balance_url = f'{base_balance_url}'
            mock_file = 'treasury_balances_last_saved.html'

I default the reconcile arg to fresh so I know that if statement will always at least match. But now I'm getting pylance "possibly unbound" warnings for the balance_url var. I don't think I need an else statement here, but I guess its not truly safe

What's the best practice here?

brazen jolt
#

well, what if the user changes the reconcile to "abc"?

gray dust
#

That user is me! I would never do something so stupid

brazen jolt
#

well, pyright doesn't know that

hearty shell
#

Instead of taking reconcile as str, make it accept Literal['fresh', 'last_saved']

gray dust
brazen jolt
#

you can tell pyright that the variable is of literal type of 2 different strings

brazen jolt
#

literal is a special thing and may cause issues sometimes

#
def my_func(x: Literal["abcd"]):
    ...

x = "abc"
x += "d"
my_func(x)  # pyright wouldn't like this
#

issue is that pyright won't actually run your code for you, and so it can't always know what a string will be in the end

#

so Literal only really work if you use it directly

#

my_func("abcd")

hearty shell
brazen jolt
#

well, perhaps but it was just an example and many type-checkers wouldn't know this

hearty shell
#

But yeah it mostly would not

brazen jolt
#

if you did something more complex, it certainly wouldn't work

#

and even with this, you really shouldn't rely on that logic, even with pyright

#

only use literals when the parameter will truly be passed in directly as that literal value

#

also, literals are limited to only bools, strings, numbers probably also bytes but don't quote me on that

#

oh and enums too

#

but yeah, if you can't rely on the variable being passed as a literal thing, don't use them

#

instead, do something like this: ```py
def my_func(x: str = "one"):
if x == "one"
foo = 1
elif x == "two":
foo = 2
else:
raise Exception
print(foo)

gray dust
#

Ah Jesus. I kept resisting the else because I didn't need to set something. Forgot about just raising an Exception πŸ€¦β€β™‚οΈ

brazen jolt
#

yeah, it's a nice way to stop the execution there, I'd recommend a ValueError to convey that that the passed value was something unexpected

gray dust
#

Right. That keeps pylance happy. God, I've got a lot of cleanup to do

#

Thanks all!

fierce ridge
#

can you make a TypeGuard for a Literal?

brazen jolt
#

I don't see why you couldn't

#

obviously, enums would probably be the best option here

fierce ridge
#
from typing import Literal, TypeGuard

def is_abcd(x: str) -> TypeGuard[Literal['abcd']]:
    return x == 'abcd'

x: Literal['abcd']
if is_abcd(result := input()):
    x = result

mypy allowed this

#

nice

#

so you could do assert is_abcd(x) and run with python -O if you need

#

instead of cast which is always hideously ugly

brazen jolt
#

true, but I'm not sure this is better than a single cast, or an else statement with exception

#

and again, enums would certainly be the best and simplest here

fierce ridge
#

especially since you can do class MyEnum(str, Enum) and get literal strings as enum members

lethal solar
#

Hi, I have a problem with mypy, but it seems like a bug :

Here, this typing works well : ```py
from typing import Literal

def foo() -> Literal[0]:
return 0

bar: Literal[0, 1] = foo()

BUT, here there is an error with incompatible types : ```py
from typing import Literal

def foo() -> list[Literal[0]]:
  return [0]

bar: list[Literal[0, 1]] = foo()

Anyone know how to fix this ?

test.py:6: error: Incompatible types in assignment (expression has type "List[Literal[0]]", variable has type "List[Literal[0, 1]]")
hearty shell
#

It is because of list invariance

brazen jolt
#

yeah, you can't set a list of some type to be a list of a subtype

#

this is because if you could, you could append elements that are not of that type anymore

#

i.e.: ```py
def foo(animal_list: list[Animal]):
animal_list.append(Dog())

my_list: list[Cat] = [Cat(), Cat()]
foo(my_list)

#

passing my_list, being a list of just cats to the foo function which expects a list of animals, no matter if those animals are cats, or dogs, or anything else quickly becomes an issue because lists are mutable

#

that means you can edit the object after it was passed into a function, by for example adding a dog into that list, and since in that function, it's seen as a list of animals, that's allowed, but after that, you're now left with a list typed as a list of cats, with a dog in it

lethal solar
#

hm ok I think I understand
but so what should I do in that case ?

brazen jolt
#

you could use an immutable sequence

obsidian coral
#

Both frames need to be the same but I couldn't do it anyway. what is the problem ?

brazen jolt
#
def foo() -> list[Dog]:
    return [Dog(), Dog()]

out: typing.Sequence[Animal] = foo()
#

Sequence is a protocol that's a subtype of list, however it doesn't allow mutability

#

you could also copy the list

brazen jolt
lethal solar
#

but I want my list "bar" to be edited (with 0 and 1)
so by copying the list on definition will avoid the problem ?

brazen jolt
#

yes, if you copy the list, it's simply a new list containing elements of some type, but that new list can be typed as a list containing elements of some supertype, since you can't alter the old list anymore, it's a new instance at that point

hearty shell
brazen jolt
#

*oh yeah sorry, it's a supertype

lethal solar
#

hm by using .copy() it keep the old type hinting for the object
I have to do something like [x for x in foo()], that's not really good

but I guess I will just make the foo function return a list with Literal[0, 1], because the object it return will be able to have 1 in the futur (event if it is only 0 at creation)

brazen jolt
#

you'd probably need to cast it manually with copying

#

using list comprehention is indeed not a good solution

#
from typing import cast
x: list[int] = [1, 2, 3]
y: list[float] = cast(list[float], x.copy())
lethal solar
#

oh yes I can use cast
thx

brazen jolt
#

or you could make your function return list[Any]

#
x: list[Any] = [1, 2, 3]
y: list[float] = x
#

that way, this wouldn't actually cause any issues

lethal solar
#

I think I will make my function return list[Literal[0, 1]]
because the object it return will never get other things than 0 and 1

brazen jolt
#

that's also an option yeah

lethal solar
#

even if the object it return contain only 0
I got it, the typing describes what the object (the list) can contain, and not what it contains when the function returns it

brazen jolt
#

well, with immutable types, this wouldn't be an issue, but because lists are mutable, they need to be invariant otherwise issues would arise quickly

#

but if you were using something immutable, like a tuple, you could easily do exactly what you want

#
def foo() -> tuple[Literal[0], ...]:
    return (0, )

x: tuple[Literal[0, 1], ...] = foo()  # fine
lethal solar
#

yes, because even if x can contain 0 and 1, the object returned will not be able to change, so there is no risk that something else than 0 will be present in the tuple

brazen jolt
#

this may be a better solution for you, if you don't need mutability

rustic gull
#

How would you type annotate a class from within it
For example, when trying to return the subclass that would inherit your class

class Base(abc.ABC):
    @abc.abstractmethod
    def new(self, x) -> Self:
        x = self.something_with_x(x)
        return Self(x)

class Vector(Base):
    pass

shell = Vector()
position = shell.new(5)  # Here, position is an instance of Vector
#

The example might be a bit wonky

#

I've heard in 3.11 there will be typing.Self

obtuse lance
#

i have some repetitive piece of code could you give me tips to make it smaller

def get_lang():
    mydivs = soup.select_one('.galleries_info')
    txt_div = mydivs.get_text
    the_list = []
    the_final_list = []
    soup2 = BeautifulSoup(str(txt_div), 'html.parser')
    for i in soup2.find_all('a', class_="tag btn btn-primary"):
        the_main = (i.get('href')).split('/')
        if the_main[1] == 'language':                  #{the changing part}
            the_list.append((the_main[2].split('-')))

    for i in the_list:
        the_perfect_list.append(' '.join(i))

    return the_final_list


def get_tags():
    mydivs = soup.select_one('.galleries_info')
    txt_div = mydivs.get_text
    the_list = []
    the_perfect_list = []
    soup2 = BeautifulSoup(str(txt_div), 'html.parser')
    for i in soup2.find_all('a', class_="tag btn btn-primary"):
        the_main = (i.get('href')).split('/')
        if the_main[1] == 'tag':                  #{the changing part}
            the_list.append((the_main[2].split('-')))

    for i in the_list:
        the_perfect_list.append(' '.join(i))

    return the_perfect_list
trim tangle
#

!pypi typing_extensions

rough sluiceBOT
rustic gull
rustic gull
potent kayak
#

How would i go about typehinting an 2D array of 6 lists containing 6 ints

brisk hedge
#

if 6 is constant, the best you can do is a nested tuple

trim tangle
#

You can't specify the length of a list, no

brisk hedge
#

if not, there isn't a way

potent kayak
#

List[List[int, int, int, int, int, int]]

trim tangle
#

That is not legal

potent kayak
#

This gives me TypeError: Too many parameters for typing.List; actual 6, expected 1

#

Yeah

trim tangle
#

Why do you need to specify the length?

#

Just do list[list[int]]

potent kayak
#

hmm okay

fervent sierra
# potent kayak How would i go about typehinting an 2D array of 6 lists containing 6 ints

As people have suggested, Tuples can define their lengths,~~ but maybe python's native array can already handle a TypeVarTuple to statically-type its shape?~~

Nevermind, https://github.com/python/typeshed/blob/eadb35e6200dd34173ec226431f43456ccfae04b/stdlib/array.pyi#L18 it's not there yet

GitHub

Collection of library stubs for Python, with static types - typeshed/array.pyi at eadb35e6200dd34173ec226431f43456ccfae04b Β· python/typeshed

potent kayak
#

Hmm well i think it could be used in some cases

brazen jolt
#

is there some indication on what version of pyright is running on the pyright playground? @trim tangle

trim tangle
#

it's pretty ancient

brazen jolt
#

is this open to be used in some other apps?

#

also, you could update the version, lol

trim tangle
#

yes, I could πŸ™‚ if I weren't lazy

#

I'll get to it some day

brazen jolt
#

understandable

trim tangle
#

(I haven't paid a single cent so far)

brazen jolt
#

is it a github deployed page?

brazen jolt
trim tangle
#

nope

brazen jolt
trim tangle
#

well it's complicated

#

the actual pyright diagnostics are run as a cloud function written in Node.js, and the rest is what's in the repo

trim tangle
brazen jolt
#

lol, that probably won't happen

trim tangle
#

well, you have a lot of time until I'll be able to send an invoice

#

because of some crazy asshole

#

anyway...

#

yeah I was planning to renovate the playground for a while but honestly I'm a bit burn out by work and stuff right now

#

the best way would probably be the "serverless container" thing, which lets you run a whole container in a serverless way, like a lambda but better

brazen jolt
#

well, if the version won't get auto-updated, it'd be nice if it was shown on the page directly instead of having to inspect the response from that post request

trim tangle
#

that is true

#

but isn't a little adventure always exciting? πŸ™‚

brazen jolt
#

can't argue with that

brazen jolt
trim tangle
#

nah I think it's extra complexity

#

I did that because I wanted to scale the expensive stuff without buying an expensive VPS. But I didn't know about Serverless Containers back then

brazen jolt
# trim tangle nah I think it's extra complexity

how would it be more complex? you don't really need to worry about almost anything that happens in the background and since you just seem to be using a subprocess to run pyright with temporary files, it seems like an easier option to just list pyright in pyproject.toml and let poetry take care of installation

cunning raven
#

Is this good?

Optional[List[str]]``` or not
brazen jolt
cunning raven
#

What do you guys recommend me to do.

brazen jolt
#

the annotation itself is valid if that's what you're asking about

cunning raven
cunning raven
#

Maybe there's pretty much a readable option?

#

i don't know.

brazen jolt
#

Optional[List[str]] is probably the best option in terms of readability

oblique urchin
#

list[str] | None in 3.10+

brazen jolt
#

you can also do Union[List[str], None], or on newer python versions: List[str] | None

cunning raven
#

no, i use 3.8

brazen jolt
#

then I think optional is the best way to go

cunning raven
#

Thanks. πŸ‘

brazen jolt
trim tangle
#

Maybe you need typing.Sequence or Collection or Iterable?

#

Maybe you could show the function?

cunning raven
#

Pycharm does really show it in a very bad.

#

Like giving extra spaces or everything joined

lethal delta
#

Is there an off-the-shelf library that includes getting all functions/callables declared in a specific file? Especially if filtered by type hints?
I know inspect exists, but I'd like to use something that wraps that if possible.

cunning raven
#

and the three "`"

didn't work.

brazen jolt
#

you could do the same with inspect.isfunction if you wanted only functions per-se, since callable just returns anything that can be called

#

but that's probably something that doesn't need an entire library around it

lethal delta
#

I think I'll probably need to get inspect involved at some point for what I want to do.

lethal delta
brazen jolt
#

why not just inspect.isfunction though? that will automatically exclude classes

#

or do you want to catch methods of each class?

brazen jolt
#

there's not much else to type-hint other than variables and functions, classes don't need type-hinting, they're their own types, and in most cases, variables also don't need type-hinting since they're often inferred from the value they're set to a = "abc" => a is a string

#

also, the more you use python, the lesser the difference between variables and something like a function/class becomes

#
def foo():
    return 5

x = foo

x()  # 5
#

in this case, x could be typed as Callable[[], int]

#

yeah, python is very dynamic in it's nature, classes are also treated like this, and even modules

cunning raven
#

!e

def foo():
    return 5

x = foo

print(x is foo)
print(id(x) == id(foo))
rough sluiceBOT
#

@cunning raven :white_check_mark: Your eval job has completed with return code 0.

001 | True
002 | True
cunning raven
#

They from the same memory location.

brazen jolt
#

you can even construct a class manually: ```py
MyClass = type("MyClass", (), {"x": lambda self: 1})
MyClass().x() # 1

cunning raven
#

i don't like that.

brazen jolt
#

if you don't need to duplicate an object, you shouldn't be doing it

#

you can always use copy.deepcopy though

cunning raven
#

πŸ‘

cunning raven
brazen jolt
#

yeah it wouldn't

#

but it doesn't make a lot of sense to copy functions like this, why would you ever need that

brazen jolt
#

well, you should generally use an Optional

#
from typing import Optional

def func(x: Optional[int] = None):
    ...
#

but there is an option to disable the need for this in pyright

#

yes

#

pyright can infer that from it being set to = None

#

this was actually pyright's default behavior for a long time

#

it only started to require the Optional type-hint recently

#

yes

#

I mean, some people

#

explicitly setting the type-hint as Optional is a pretty good practice though

#

that only depends on how your function works internally

#

if this variable is holding something like an amount, then perhaps it could make sense

#

but 0 may be passed in intentionally in some cases, and maybe the default behavior should be something else

#

this is the pyright's setting you can toggle to disable the Optional requirement

#

always use None

#

NoReturn is for an infinite cycle or something like that

#

it's a function that never stops executing

#

so it never reaches the point of returning a value

trim tangle
oblique urchin
#

(will be typing.Never in 3.11)

trim tangle
#

what's the difference between them?

brazen jolt
#

will NoReturn get deprecated in it's favor?

oblique urchin
#

type checkers are supposed to treat them the same

trim tangle
#

oh so it's just a rename

#

maybe I could mention it at the end

oblique urchin
trim tangle
# trim tangle maybe I could mention it at the end

like: "You might be familiar with never in TypeScript. NoReturn is the same thing. In fact, in Python 3.11 NoReturn will be renamed to Never to reduce the confusion. If you're not on 3.11 yet, you can use Never from typing_extensions."

torpid yarrow
torpid yarrow
spice locust
#

what does the Never type hint mean? when i hover over a particular variable in vscode it shows (parameter) to: Never

#

the code is ```py
x = x if isinstance(x, (list, typing.Sequence)) else [x]

oblique urchin
trim tangle
minor hedge
#

is there something like a typevar for types? something like this ```py
class A:
pass
class B(A):
pass

this part doesn't seem right

TA = TypeVar('TA', bound=A)
cls: Type[TA] = B

x: TA = cls()```

trim tangle
minor hedge
#

I'm trying to do something in a class ```py

class A:
pass
class B(A):
pass

TA = TypeVar('TA', bound=A)

class Thing:
def init(self, cls: type[TA]):
self.cls = cls

def get(self, data) -> TA:
    return self.cls(data)```
tranquil turtle
#

class Thing(Generic[TA]):

trim tangle
tranquil turtle
#

why Callable[[], TA] is better than type[TA]?

trim tangle
trim tangle
tranquil turtle
#

it isnt more flexible if you want to get a class, not an instance factory

trim tangle
#

Well, the example shows that they need a factory

tranquil turtle
#
class Thing(Generic[TA]):
    def __init__(self, cls: Callable[[], TA]):
        self.cls = cls
        print(cls.mro()) # error
        

#

yes, youre right, in this example Callable[[], TA] is better
but in general i prefer type[TA]

minor hedge
#

huh so callable or type both work fine?

soft matrix
#

Yes but it's imprecise

#

But I also don't think it's possible to type this

soft matrix
#

I think if Map is introduced you can do (tuple[*Ts]) -> list[Map[Union, Ts]] or something for typing foo

#

You need to explicitly allow None

#

value: float | None = None

#

No

#

That's going to evaluate to just float

#

At runtime atleast

#

Yep

#

But that should be the same as float | None because of the way that type checkers treat float (it's a subtype of int)

#

It is you just need to turn on

#

Fun

#

Whichever you prefer

brazen jolt
#

you'll need overloads then

#
from typing import overload, Literal

@overload
def foo(x: Literal[1], y: int) -> None:
    ...

@overload
def foo(x: Literal[2], y: str) -> None:
    ...

def foo(x: Literal[1, 2], y: object) -> None:
    # implementation
soft matrix
#

Yeah overloads won't help here

#

You need conditional types

#

Well i guess they help with the signature

brazen jolt
#

assuming winreg.SetValueEx has overloads defined, you should then be able to get away with something like the above with the implementation like: ```py
if x == 1:
x = cast(Literal[1], x)
winreg.SetValueEx(...)
elif x == 2:
x = cast(Literal[2], x)
winreg.SetValueEx(...)

#

yeah, the implementation needs to then use conditional types

#

which can be very annoying with literals

#

you could also type-ignore that call internally, and just have overloads defined on your method so people use it correctly

brazen jolt
#

I doubt it though

cunning raven
hearty shell
#

overloads allow you to type a function to have a different signature depending on the input arguments, it is not supposed to have any runtime effect though

#
@overload
def foo(a: Literal[True]) -> Literal[1]: ...

@overload
def foo(a: Literal[False]) -> Literal[0]: ...

def foo(a: bool) -> int:
    return int(a)

reveal_type(foo(True))  # Literal[1]
reveal_type(foo(False))  # Literal[0]
hearty shell
# cunning raven i've seen it in discord.py resource code

But it can have runtime effects, so it depends how a library uses it, at runtime it just returns the function unchanged with no side effects (prior to 3.11) but if used in a class with some magic it can have runtime effects if a library makes it so via metaclass magic, maybe that is the case with discordpy

#

Actually my bad, it doesnt return the function, it just returns a dummy function that then gets redefined by the implementation

soft matrix
hearty shell
#

Ah alright, I said maybe because I wouldnt put it past them doing that xD

cunning raven
#

@hearty shell thank you so much.

urban agate
#

I've got an async context manager and factory method classmethod as such:

class User:
    def __init__(self, security_cookie: str, proxies: dict = None):
        self.__roblosecurity = security_cookie
        self.__proxies = proxies

    async def __aenter__(self, *args, **kwargs) -> None:
        """
        Context manager version of User.create
        """
        pass

    async def __aexit__(self, *args, **kwargs) -> None:
        await self.close()

    @classmethod
    async def create(cls, *args, **kwargs) -> Self:
        """
        Factory method creation of User object
        Initializes the user class, checking the cookie's validity in the process
        """
        self = User(*args, **kwargs)
        return self

How do I go about adding parameter hints in __aenter__ for the context manager and also in create to match __init__ parameters so that it appears in IDE suggestions?
I'm aware I can just change the parameters of __aenter__ and create themselves, but I feel like there's probably a more pythonic way to just have them just pull from what's required by __init__ but I don't know how. Any ideas? Tag on response please.

#

See how it says *args **kwargs due to it being the params for classmethod create, but I want it to show me the params for User.__init__ somehow.

#

I'm using VSCode, for reference.

hearty shell
# urban agate I've got an async context manager and factory method classmethod as such: ```py ...

I guess you can do

from typing import *

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

def method_args_of(
    init: Callable[Concatenate[Any, P], None]
) -> Callable[[Callable[[Any], T]], Callable[Concatenate[Any, P], T]]:
    ...

class User:
    def __init__(self, security_cookie: str, proxies: dict | None = None) -> None:
        self.__roblosecurity = security_cookie
        self.__proxies = proxies

    @classmethod
    @method_args_of(__init__)
    async def create(cls, *args, **kwargs) -> Self: ...

reveal_type(User.create)  # Type of "User.create" is "(security_cookie: str, proxies: dict[Unknown, Unknown] | None = ...) -> Coroutine[Any, Any, User]"

I dont know if I would call this "more pythonic" though

tranquil turtle
#

Your method_args_of return None, you should return lambda x: x instead

hearty shell
#

runtime details

#

But yeah youre right x)

solid mesa
#

thoughts on this?

status_flag: int[range(0, 4)] = 1
#

dont ask me why, i couldnt tell you

hearty shell
#

thoughts as in? If this should be supported?

solid mesa
#

is it good? is it stupid? will it work?

#

idk why i did it, i just did it to define a int variable that has a range of 0-4

#

linter isnt giving me an error

hearty shell
#

It might just not be on or in a mode where it just ignores incorrect type signatures

#

But this is definitely not supported

solid mesa
#

this is the return Generator[Literal[2, 0], None, None]

hearty shell
#

It is also a runtime exception as int does not support []

solid mesa
#

it doesnt?

#

its not giving me any errors and im using pylance

#

status_flag: int[0, 4] = 1

hearty shell
#

!e int["foo"]

rough sluiceBOT
#

@hearty shell :x: Your eval job has completed with return code 1.

001 | Traceback (most recent call last):
002 |   File "<string>", line 1, in <module>
003 | TypeError: 'type' object is not subscriptable