#type-hinting

1 messages · Page 21 of 1

soft matrix
#

type(type(instance)) -> type not Instance

simple field
#

not sure what you mean

oblique urchin
#

I believe type[type[Arg]] is illegal. type[] can only holds class names, unions of class names, TypeVars, or Any according to PEP 484

soft matrix
#

!e ```py
class Foo: ...
print(type(Foo())) # => type[Foo]
print(type(type(Foo()))) # => type[type[Foo]]

rough sluiceBOT
#

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

001 | <class '__main__.Foo'>
002 | <class 'type'>
soft matrix
#

pyright doesnt seem to have any problems with nested type[...]

simple field
simple field
oblique urchin
#

no. that suggests it returns the metaclass of your class

#

there should be type[] either both in the argument and return type or neither

#

because you're both taking and returning a type

simple field
#

so then what exactly is the meaning of bound in TypeVar?

oblique urchin
#

it means the TypeVar must be solved to a subtype of the bound

simple field
#

so it's a naming issue? as in, i should change the name of T to something clearer? otherwise, isn't the actual type hinting correct?

oblique urchin
#

no. you're saying that you're taking a T and returning the type of T

#

but in fact you're both taking and returning a type

cunning plover
#
from typing import NewType

PolicyARN = NewType("PolicyARN", str)

some_arn = PolicyARN("bla")
str_arn = "bla2"

def some_func_with_arn(arn: PolicyARN) -> None:
  pass

def some_func_with_str(arn: str) -> None:
  pass

some_func_with_arn(some_arn) # Mypy verifies OK
some_func_with_str(some_arn) # Mypy verifies OK :see no evil:
some_func_with_arn(str_arn) # Mypy screams Error
some_func_with_str(str_arn) # Mypy is ok

NewType works like this in mypy

Can i have it working like

some_func_with_arn(some_arn) # Mypy verifies OK
some_func_with_str(some_arn) # Mypy verifies Error
some_func_with_arn(str_arn) # Mypy screams Error
some_func_with_str(str_arn) # Mypy is ok

? forbidding automated casting back to parent type

simple field
# oblique urchin no. you're saying that you're taking a T and returning the type of T

please correct me if im wrong, im just going off of what it says here: https://docs.python.org/3/library/typing.html#the-type-of-class-objects

my understanding so far is: if T itself is bound to Type[SomeClass], that means that a name with annotation T must be set to SomeClass, or a subtype of SomeClass, right? So then, why would a name of annotation Type[T] have to be set to a metaclass of T, rather than T itself or one of its subtypes?

trim tangle
#

because NewType creates a "subtype"

cunning plover
# trim tangle because NewType creates a "subtype"

any alternative to achieve such result?

some_func_with_arn(some_arn) # Mypy verifies OK
some_func_with_str(some_arn) # Mypy verifies Error
some_func_with_arn(str_arn) # Mypy screams Error
some_func_with_str(str_arn) # Mypy is ok

trying to find may be there is some plugin or smth that forbids implicit casting to parent back

#

thinking of may be possible to write such mypy plugin 😄

#

maybe some other linter exists that forbids it

cunning plover
# trim tangle I don't think you can
from typing import TypeVar, Callable, NewType, TypeAlias

T = TypeVar('T') 

class PolicyARN:
    def __init__(self, name: str):
        self.name = name
    def __str__(self) -> str:
        return self.name

# PolicyARN = NewType("PolicyARN", str)

some_arn = PolicyARN("bla")
str_arn = "bla2"

def some_func_with_arn(arn: PolicyARN) -> None:
  pass

def some_func_with_str(arn: str) -> None:
  pass

some_func_with_arn(some_arn) # Mypy verifies OK
some_func_with_str(some_arn) # Mypy screams Error
some_func_with_arn(str_arn) # Mypy screams Error
some_func_with_str(str_arn) # Mypy is OK

I did it 😄

#

yeah... additional casting costs. I don't care 🙈

#

there are magic method for all simple types. (__int__, __float__ and etc) 😄 exactly what i wish

#

yeah... not super performant 🙈 but lets say performance was not a goal anyway.

cunning plover
#

at the end seeing new bugs appearing from this, easier to use NewType. because it is more friendlier to code that is not typed 😐

simple field
#

well

#

pycharm seems to understand this correctly:

T = TypeVar('T', bound=type[SomeClass])

def patched_class(cls: T) -> T:
  class PatchedClass(cls):
    ...
  return PatchedClass
#

but neither mypy or pyright like the class PatchedClass(cls): line

#

mypy says:

main.py:12: error: Variable "cls" is not valid as a type  [valid-type]
main.py:12: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
main.py:12: error: Invalid base class "cls"  [misc]
main.py:15: error: Incompatible return value type (got "type[PatchedClass]", expected "T")  [return-value]

where line 12 is class PatchedClass(cls): and line 15 is return PatchedClass

eager vessel
#

Is it possible to remove kwargs from a function definition using ParamSpec? 🤔
E.g. something like

def remove_kwargs(func: Callable[P, R]) -> Callable[[*P.args], R]:
    return func
trim tangle
#
Ts = TypeVarTuple("Ts")
def remove_kwargs(func: Callable[[*Ts], R]) -> Callable[[*Ts], R]:
    return func
eager vessel
#

I guess I need to write a mypy plugin

#

Ultimately I want to strip all Annotated[..., Inject] from function definition, but for my limited usecase leaving only first argument of a function would have worked:

@inject
async def load_users(
    keys: Sequence[int],
    session: Annotated[AsyncSession, Inject],
) -> Sequnece[User | None]:
    pass
#

@trim tangle Do you think a plugin is the way to go there?

trim tangle
eager vessel
fossil sorrel
#

How can I declare a CALLBACK: Callable[..., Any] = None, as None?

#

like how can I point that callback to nothing?

#

Expression of type "None" cannot be assigned to parameter of type "(...) -> Any"
Type "None" cannot be assigned to type "(...) -> Any"

fossil sorrel
#

Union callable and none?

tranquil turtle
#

Yes

fossil sorrel
#

Argument of type "((...) -> Any) | None" cannot be assigned to parameter "Callback" of type "(...) -> Any" in function "upload_fileobj"
Type "((...) -> Any) | None" cannot be assigned to type "(...) -> Any"
Type "None" cannot be assigned to type "(...) -> Any"

#

uhhhhhhhhhhhhhhhhhh

#

it doesnt accept none, like i have to point it to something void

trim tangle
#

sounds like the typings for boto3 are fucked

tranquil turtle
#

Oh, i misunderstood you. I dont think it is possible without calling function in teo different ways in if

fossil sorrel
#

probably, service and resource was fucked too

trim tangle
#

is the library even typed?

fossil sorrel
#

no it is unofficial types

#

meaning it may be fucked, not %100 sure

tranquil turtle
fossil sorrel
#

here ise the stub file in unofficial typings

#
def upload_fileobj(
        self,
        Fileobj: FileobjTypeDef,
        Bucket: str,
        Key: str,
        ExtraArgs: Dict[str, Any] = ...,
        Callback: Callable[..., Any] = ...,
        Config: TransferConfig = ...,
    ) -> None:
        """
        Upload a file-like object to S3.

        [Show boto3 documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.upload_fileobj)
        [Show boto3-stubs documentation](https://youtype.github.io/boto3_stubs_docs/mypy_boto3_s3/client/#upload_fileobj)
        """ 
trim tangle
#

Yeah this is wrong

tranquil turtle
trim tangle
#

yep

fossil sorrel
#

exactly

trim tangle
#

I would file an issue with the stub repo

tranquil turtle
#

All optional args are wrong

fossil sorrel
#

will do

#

thanks boys

#

i cant even find my way where the heck is source code in github lmao

soft matrix
fossil sorrel
#

thank you thank you

rare scarab
#

This is why I always prefer wrapper libraries.

#

Never directly interact with s3 aws

rustic gull
#

if you wrap a typed function with an untyped decorator type checkers assumes its args Any, kwargs Any -> Any (I think right?).. is there a way to say "im not going to change the function signature with this decorator"

brazen jolt
#

you could do this:

from typing import TypeVar
from collections.abc import Callable

F = TypeVar("F", bound=Callable)
def my_deco(f: F) -> F:
  ...
oblique urchin
#

!pep 612

rough sluiceBOT
#
**PEP 612 - Parameter Specification Variables**
Status

Accepted

Python-Version

3.10

Created

18-Dec-2019

Type

Standards Track

brazen jolt
#

or this: ```py
from typing import TypeVar, ParamSpec

T = TypeVar("T")
P = ParamSpec("P")

def my_deco(f: Callable[P, T]) -> Callable[P, T]:
def wrapper(*a: P.args, **kw: P.kwargs) -> T:
...
return wrapper

oblique urchin
#

^ for more advanced variations

brazen jolt
#

yeah, that's actually the linked pep

soft matrix
#

generally though id recommend the first example until hkt is a thing because it will preserve types better

#

and its easier to do

brazen jolt
soft matrix
#

because to preserve the type of the callable used you need to do F[P, T] or something

#

otherwise it gets erased on the return type to just a generic callable when it could have been a functiontype or methodtype etc

brazen jolt
#

oh, yeah that's true I suppose, though I never really needed to preserve FunctionType/MethodType, but yeah, that makes sense

soft matrix
#

this doesnt look right

#

much better 😎

rare scarab
#

Paramspec is more useful if you actually want to change the params

oblique urchin
#

it also avoids a cast() in the implementation for decorators that don't change the signature

rare scarab
#

Does pycharm support decorators that change the signature yet?

cunning plover
#
from typing import NewType, TypeVar, Type

ExcType = NewType("ExcType", Type[Exception])

class PanicException(Exception):
    pass

abc: ExcType = PanicException

Trying to make a type that allows any subclassed exception. help 😄 not sure how yet

#
class Notificator(metaclass=ABCMeta):
    @abstractmethod
    def panic(self, msg: str, exc: Optional[Exception] = None, error_cls: types.ExcType = PanicException) -> None:
        """
        Critical problems.
        """

target, to use there

trim tangle
#

That's not possible

cunning plover
#

okay, then i need to figure out Generic usage for Notificator, then it should be possible

trim tangle
#

Well, it will allow using BaseException
But I don't see problems with that

cunning plover
rare scarab
#

Use bound= on the typevar

#

Your typevar has no meaning in your function

#

You gotta use it all least twice

#

And typevars don't behave when used with a default arg. You'll need an overload.

cunning plover
rare scarab
tranquil ledge
#

Suppose I have a decorator that adds a regular parameter to the function it’s decorating, between the args and kwargs. Is there a way to indicate that the parameter has been added to the function when working in the context of type stubs, where (AFAIK) one can’t actually use custom decorators and has to collapse the return type of the decorator into the function’s return type? I have relevant code but can’t post it atm, so I’m curious about general mechanisms for this.

#

I’ve managed to nail down a callable protocol that describes the decorator return type such that if the extra parameter is or isn’t specified, my IDE indicates alternate return values for the function. Beyond that though, I have no idea how to apply that kind of typing to every function that the decorator wraps, or if that’s even a relevant approach.

celest iron
#

this is very difficult and usually doesn't work, esp since there's no Concatenate for kwargs like things (afaik). I ran into this a bit ago and while decorators can do really fancy things, if u want to fully type ur code, u ought to be careful with how fancy u get.

#

i think there's a way to use @typing.overload, but it's pretty restrictive

paper salmon
#

im not sure what you mean by collapsing the decorator return type, but it sounds like a generic callable protocol with paramspec (3.10) is what you want

rare scarab
#

Every type feature is available in pyi files

tranquil ledge
#

Hmm. Technically this decorator gives every function it decorates two overloads and a new arg. I guess I’m asking, is there an obvious clean way to indicate these functions now have overloads (in type stub files) without going through the work of showing the overloads for every function that’s been decorated? If that makes sense.

tranquil ledge
paper salmon
#

oh didnt know that paramspec didnt allow appending positional args, only prepending

tranquil ledge
#

^^ Yeah, that was an issue.

#

The more I think about it, the less possible it seems. Might just be easier to bite the bullet and type out the overload definitions.

rare scarab
#

Are you unable to use the decorator in the stub?

tranquil ledge
tranquil ledge
rare scarab
#

That's a fair assumption, and probably correct

oblique urchin
tranquil ledge
#

Huh. If so, sick. I’ll give it a go tomorrow and see what happens.

#

The decorator, for the record.

# Some of these imports are for other things in the same file.
from threading import Thread
from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Protocol, TypeVar, Union, overload


if TYPE_CHECKING:
    from typing_extensions import TypeAlias


T = TypeVar("T")
T_co = TypeVar("T_co", covariant=True)


class ThreadableCallable(Protocol[T_co]):
    @overload
    def __call__(self, *args: Any, threaded: Literal[False] = ..., **kwargs: Any) -> T_co:
        ...

    @overload
    def __call__(self, *args: Any, threaded: Literal[True] = ..., **kwargs: Any) -> Thread:
        ...

    def __call__(self, *args: Any, threaded: bool = False, **kwargs: Any) -> Union[Thread, T_co]:
        ...


def threadable(func: Callable[..., T]) -> ThreadableCallable[T]:
    """Allows the function to be ran as a thread using the 'threaded' argument"""

    @overload
    def wrapped(*args: Any, threaded: Literal[False] = ..., **kwargs: Any) -> T:
        ...

    @overload
    def wrapped(*args: Any, threaded: Literal[True] = ..., **kwargs: Any) -> Thread:
        ...

    def wrapped(*args: Any, threaded: bool = False, **kwargs: Any) -> Union[Thread, T]:
        if threaded:
            thread = Thread(target=func, args=args, kwargs=kwargs)
            thread.start()
            return thread
        return func(*args, **kwargs)

    
    wrapped.__module__ = func.__module__
    wrapped.__name__ = func.__name__
    wrapped.__qualname__ = func.__qualname__
    wrapped.__doc__ = func.__doc__
    wrapped._threadable = True  # type: ignore
    return wrapped
#

Probably quite a bit wrong with it, but this was my attempt at typing it.

#

(Not quite sure how functools.wraps interacts with overload and I didn’t look too deep into it at the time.)

rare scarab
#

Overload doesn't exist, so wraps doesn't do anything

low escarp
#

I'm not sure if I am being dumb, but I am trying to type hint something which is a ctypes.POINTER.
I tried the naive

data: ctypes.POINTER(DataStructure)

(data type being pointed to for the most part here doesn't matter, but I am generally using other ctypes.Structures)
I have also tried googling and followed this: https://github.com/python/mypy/issues/7540#issuecomment-845741357 to no avail:

data: ctypes.pointer[DataStructure]

In both cases vscode just thinks that data is Any, so I get no code completion...
I'd like to have it so that data is correctly understood to be a pointer, and thus has the contents attribute, and the type of centents is DataStructure.

#

Also, what is weird is that if I do

data = ctypes.POINTER(DataStructure)

It works fine, but the issue is that I am just defining the types as properties to a ctypes.Structure, so I can't set it equal like that...

brazen jolt
#

I don't think ctypes.POINTER is generic

#

notice that pyright uses a _Pointer type, not the ctypes.POINTER class directly

#

this is probbaly comming from a stub

low escarp
#

yeah, I did notice that...

#

typeshed apparently has a stub for it

#

but I wasn't sure how to use that info...

#

typing it with _Pointer also didn't seem to work...

#

lol, ok, I got it...

#

x: ctypes._Pointer[ctypes.c_int] works

#

thanks for helping to nudge me in the right direction

brazen jolt
#

yeah, but it might fail on runtime

#
if TYPE_CHECKING:
    from ctypes import _Pointer
#

I'd do this

#

with future annotations, the above would work too

low escarp
#

hmm, let me try...

#

hmm yeah, it doesn't like it...

brazen jolt
#
from __future__ import annotations

from typing import TYPE_CHECKING

from ctypes import c_int

if TYPE_CHECKING:
    from ctypes import _Pointer


x: _Pointer[c_int] = ...
#

this should work on runtime

#

alternatively, if you really don't want __future__.annotations, you could also put an else under that if TYPE_CHECKING, and make the _Pointer variable hold something that supports class getattr

#

it's still gonna be lying to the type checker though

low escarp
#

why do I need to import annotations from future?

brazen jolt
#

it's a special import, that affects how python treats type-hints, it basically converts every type hint into a string

#

so it's the same as if you did: ```py
x: "_Pointer[c_int]" = ...

#

which would work without that import too

low escarp
#

interesting...

#

ok, let me tinker thanks!

soft matrix
#

list is invariant so that should be an error yes

#

try changing searches to a Sequence

#

read the link i sent

#

oh youve snuck an int at the end of your union

#

yeah that also makes sense still

#

you cant put subclasses into a list and expect it to work

#

why would you cast a parameter like that?

#

yes

#

well open a pr to fix it

#

or for now cast it yeah

#

unless they actually mutate the list

#

what are you doing?

#

oh no you need to cast to the exact same type

red shard
#

Hello

fierce ridge
#

is there a way to "downgrade" a dict[str, str] to a Mapping[str, str] without cast?

#

here's my example that mypy doesn't like:

from collections.abc import Mapping
from python_dotenv import dotenv_values

ENV: Mapping[str, str | None] = dotenv_values()  # returns dict[str, str | None]
#

oh never mind.. i forgot the None on the left hand side 😅

#

it'd be nice if mypy was clearer about which part of the type was incompatible

jade viper
#

Hey guys, how do I type hint a str with exactly three chars?

fierce ridge
#

@jade viper you can't unfortunately

#

tuples are special in that they can know their own size in some sense. strings however are opaque to the type checker

#

you can use newtype wrappers to emulate it, but it's going to be hard to work with and it's probably not worth the trouble

#

there are some programming languages with something called "dependent types" where types can encode arbitrary information about the object, but they're fairly complicated and mostly used for proof assistants and other research work. the only one that's practical for regular programming is Idris 2, and it's very very different from python

jade viper
#

:/

#

I really do only need it to be a hint

#

Not pass type checks

#

str[3] seems like a obvious type hint but my linter only recognizes str

simple field
#

maybe Annotated and a linter plugin if that's a possibility?

#

something like

from typing import TypeVar, Annotated, Generic

I = TypeVar('I', bound=int)

class Length(Generic[I]):
    pass

# then
example: Annotated[str, Length[3]] = ...
soft matrix
#

theres a pypi project for reuseable annotateds so you could use that as a basis

simple field
#

i see a Len there

soft matrix
#

yeah

trim tangle
#

You can't put everything in type hints

#

If you want to be damn sure you have 3 characters you can use tuple[str, str, str]

oblique urchin
trim tangle
#

yeah good question

oblique urchin
#

I fully expected you to start typing out Literal["a", "b", ...]

trim tangle
#

Why are you starting with lowercase letters

oblique urchin
#

got to start somewhere

trim tangle
#

Please think of the \x00!

jade viper
soft matrix
#

you type hinted an enum?

trim tangle
#

"Ж\x00&" is probably not something that you can trade for US dollars

#

You cannot encode every possible constraint in the type system, as I said. Runtime checks are perfectly valid, especially if you can build a "boundary" that encapsulates some data that's definitely valid.

class CurrencyCode:
    def __init__(self, raw: str, /) -> None:
        if len(raw) != 3:
            raise ValueError("Expected currency code length to be 3")
        if not all(char in string.ascii_uppercase for char in raw):
            raise ValueError("Only expected uppercase Latin letters in currency code")
        self._value = raw

    def value(self) -> str:
        return self._value
jade viper
trim tangle
#

Well then, why do you need to encode the length in the type?

low escarp
#

Is it possible to get functools.singledispatch to work nicely with intellisense? I know this isn't strictly type hinting, but it's kinda close...
For example, I have a test class like this:

class Test():
    def __init__(self, x: int):
        self.x = x

    @singledispatch
    def function(self, y: int) -> int:
        """ This is a docstring... """
        return self.x + y

    @staticmethod
    @function.register
    def _(this: int, y: int) -> int:
        return this + y

This all works perfectly, but the issue is that I'd quite like to be able to type

t = Test()
t.function(

And be able to see the possible arguments (kind of like the overloads of something like a ctypes.protocol where it shows mutliple options...)

#

what I see:

#

what would be nice:

#

lmao, just realised I should look at how they do it internally and there is an overload decorator...

low escarp
#

Ok, actually looking at that and some other links such as this on github: https://github.com/python/mypy/issues/8356
I still can't figure it out.
I think I am a bit stuck because I need one of the methods to be a staticmethod since I am basically wanting to have the same method name for both a static method and a normal method...

golden niche
#
# this is inside class
def method1(self):
    return 'hello world'

def method2(self, x_method):
    result = x_method()
    return result

what is the type hint for method inside that class?

golden niche
#

I want x_method to have type hint of method in that class

trim tangle
#

that sounds strange, can you explain more?

golden niche
#

like

self.method2(method1())
#

it will only take 1 method

tranquil turtle
#

!d collections.abc.Callable

rough sluiceBOT
trim tangle
#

You mean self.method2(self.method1)?

golden niche
trim tangle
#

Why does it have to be a method and not just any function?

#
from collections.abc import Callable
from typing import TypeVar

T = TypeVar("T")

class Foo:    
    def method2(self, x_method: Callable[[], T]) -> T:
        result = x_method()
        return result
golden niche
#

I think any function also work I think

trim tangle
golden niche
# trim tangle could you give a more realistic example maybe?
class ScannerWidget:
    def __init__(self, x: int, y: int, h: int = 100, w: int = 100, pad: int = 20) -> None:
        self.win1 = Toplevel()
        self.win2 = Toplevel()
        self.win3 = Toplevel()
        self.win4 = Toplevel()

        self.win1.geometry('%dx%d+%d+%d' % (w-pad, pad, x+pad, y))
        self.win2.geometry('%dx%d+%d+%d' % (w-(2*pad), pad, x+pad, y+h-pad))
        self.win3.geometry('%dx%d+%d+%d' % (pad, h-pad, x, y+pad))
        self.win4.geometry('%dx%d+%d+%d' % (pad, h-(2*pad), x+w-pad, y+pad))

        self._win_prep(self.win1)
        self._win_prep(self.win2)
        self._win_prep(self.win3)
        self._win_prep(self.win4)

        self.left_top = Coordinate2D(x, y)

    def _move(self, win: Toplevel, x: int, y: int) -> None:
        win.geometry('+%d+%d' % (win.winfo_x() + x, win.winfo_y()) + y)

    def _do4win(self, method):
        pass
def _win_prep(self, win) -> None:
        if platform == "darwin":
            win.overrideredirect(1)
            win.overrideredirect(0)
        else:
            win.overrideredirect(1)

        label = Label(win)
        label.pack(fill="both", expand=True)

        label.bind('<Configure>', self._label_resize)
        label.bind("<ButtonPress-1>", self._drag_start)
        label.bind("<ButtonRelease-1>", self._drag_stop)
        label.bind("<B1-Motion>", self._drag_motion)
    
    def _label_resize(self, event):
        width = event.width
        height = event.height
# more code...

here as you can see I have lot's of repeated win1, win2, win3, win4. so I want to make method that take method

tranquil turtle
#

tkinter?

golden niche
#

ah... wait some of it is not method, but more below is method

#

yes tkinter

trim tangle
#

In your example you call the function with 0 arguments, so that's something the argument should satisfy

golden niche
#

I see... ty 👍

buoyant ridge
#

the class could even derive from str, with its validation in __new__

#

obviously what you initialise the class with won't be type checked before runtime but like, it's beyond the scope of the type checker here imo

solid light
#

Is it possible to dynamically create a TypedDict (or akin) based on the value of a list at runtime? E.g.py wanted_info = [ "id", "user.username", "user.id", ]would create the {id: string, user: {username: string, id: string}} type (every bottom-level key always has a string value)

soft matrix
#

It wouldn't be useful at runtime would it?

buoyant ridge
#

what they said

# non-recursive ver
my_dict = {k: str for k in wanted_info}
wanted_info_type = TypedDict("wanted_info", my_dict) # static type checker cannot read a dynamically created dict entry
x: wanted_info_type = {"id": "hi", "user.username": "hi", "user.id": "hi"} # fails due to above
#

(this is just a flattened example). code runs fine, but you won't get type supports for this

#

you probably want to use dataclasses (won't support recursion?) or pydantic to solve this kind of problem in a pythonic way

solid light
#

Hmm, okay. I know it's possible in ts so was hoping you could in python too, but guess not (easily, anyway)

buoyant ridge
#

i wonder whether typescripts advantages to python are largely just due to more work put into or or a fundamental difference between a typed superset and static type checking?

brisk hedge
#

typescript is designed type system first and attempts to fit as many existing use cases of javascript as possible

fossil sorrel
#
targetPrice: Optional[int] = Field(default=0)
#

why in the tarnation pylance see this field as either a bool, integer, or none

#

i understand int or none, but where the fk bool comes from

pastel egret
#

Booleans are a subclass of integers.

fossil sorrel
#

so how would i write the signature of this ```py
def something(targetPrice: int) -> int

pastel egret
#

Originally for historical reasons, it does come in handy sometimes though.

#

Like that? It's actually valid to pass True in. Try True + 5 in the REPL :)

fossil sorrel
#

wadufuuuuug

#
  Type "bool | int | None" cannot be assigned to type "int"
    Type "None" cannot be assigned to type "int
```i am gonna ignore these errors then lmfao
#

pyright at strictest is most fun and most stubborn thing i have ever seen

brisk hedge
#

well that's a different problem

fossil sorrel
#

yeah wait this is none problem

#

this is my dumbness

pastel egret
#

Yeah, you need to check for none first.

fossil sorrel
#

nonetheless

#

it was fking cool to hear bool is subclass of int

#

thnks a lot

pastel egret
raven prairie
#

How can I assert that a variable is bound?

#
        if 'tile_data' not in locals():
            raise Exception("No tile data found.")

this doesn't work, tile_data is still Unbound | NDArray[uint8]

soft matrix
#

you cant

#

the best way is to set it to None and then check if its none

raven prairie
#

I'm assuming there's a typing reason for that?

soft matrix
#

it will actually work with pyright

#

you could probably open a feature request if you want to float the boat but i highly doubt it would be accepted when setting it to None and just checking after the fact if its None already works

trim tangle
#

Yeah, conditionally defining locals is generally considered a bit weird

jade viper
#

Hey guys

#

What would be the right way to type hint the translate_symbol_with_cache function?

from functools import lru_cache
from typing import Optional


def translate_symbol(field: str, source_name: str, destination_name: str = 'GS_VALUE', source_value: Optional[str] = None, destination_value: Optional[str] = None, primary_only: bool = False, return_none_on_not_found: bool = False, break_on_not_found: bool = False) -> Optional[str]:
    ...


translate_symbol_with_cache = lru_cache(maxsize=None)(translate_symbol)

#

My linter doesn't get anything from it

#

Just lrucachewrapper

soft matrix
#

it doesnt preserve types unfortunately currently

chrome hinge
# jade viper What would be the right way to type hint the `translate_symbol_with_cache` funct...

I think you can easily-ish make a wrapper over lru_cache that preserves types:

P = ParamSpec("P")
R = TypeVar("R")
def lru_cache_(maxsize: int | None = 128) -> Callable[[Callable[P, R]], Callable[P, R]]:
    return functools.lru_cache(maxsize)  # type:ignore

def f(x: int, y:str)-> dict:
    return {}

fcached = lru_cache_(123)(f)
reveal_type(fcached) # Type of "fcached" is "(x: int, y: str) -> dict[Unknown, Unknown]"Pylance
pastel egret
#

It's really hard, since you need to handle function -> method descriptor behaviour. Someone tried to do it on typeshed, it's like a page of types and still doesn't work...

lunar dune
#

One was actually merged, but was then reverted a few months later as it caused an unacceptable number of regressions for mypy users

#

I'm personally unclear about whether all of these problems are due to the fact that the specification is very unclear about how ParamSpec should work with the "function -> method descriptor behaviour". Some of the issues may be due to incomplete ParamSpec support at mypy

#

A lot of ParamSpec fixes have been made to mypy recently, so it's possible we could try again after mypy 1.6 is released and see what the fallout is

#

FWIW I think https://github.com/python/typeshed/pull/7771 has been the most promising attempt so far to fix lru_cache typing at typeshed. You can see from the primer diff that it would have fixed a bunch of issues in user code; it just would have caused a few regressions as well

deep saddle
#

This code fails in Pylance because argument y does not fit the type of parameter x. Why? ```py
def f(x: list[tuple[str]]):
print(x)

y = [("hello",)]
f(y)

Argument of type "list[tuple[Literal['hello']]]" cannot be assigned to parameter "x" of type "list[tuple[str]]" in function "f"
"list[tuple[Literal['hello']]]" is incompatible with "list[tuple[str]]"
Type parameter "_T@list" is invariant, but "tuple[Literal['hello']]" is not the same as "tuple[str]"```

#

Interestingly, removing the y variable and directly passing the value: f([("hello",)]) works completely fine

#

maybe this is just a bug with pylance?

#

this also passes ```py
def f(x: list[tuple[str]]):
print(x)

y = [("hello",)]
y: list[tuple[str]]
f(y)```

#

i'm struggling to think of a scenario in which "list[tuple[Literal['hello']]]" is incompatible with "list[tuple[str]]" makes any sense

brisk hedge
#

list[some subclass of T] is not compatible with list[T], because lists allow mutating their elements
that causes problems where you could add an invalid element to a list. like if you pass list[MyInt subclass of int] to a function taking a list[int], and the function append()s a bool to it, then your original list now doesn't only have MyInt objects
you can avoid the error by explicitly annotating y: list[tuple[str]]

#

the part in the error (T is invariant) explains the distinction a bit

acoustic thicket
acoustic thicket
trim tangle
tranquil turtle
#

Pyright for some reason tells me that staticmethod/classmethod are generic. Is it true?

#

What about property?

soft matrix
#

property isnt at all but the other 2 arent at runtime unless somethings changed

tranquil turtle
#

Are these 2 generic in typeshed?

#

Can we pls pin link to typeshed here?

#

Pin this VVV

trim tangle
#

Done 😎

viscid spire
#
class classmethod(Generic[_T, _P, _R_co]):
    @property
    def __func__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ...
    @property
    def __isabstractmethod__(self) -> bool: ...
    def __init__(self, __f: Callable[Concatenate[type[_T], _P], _R_co]) -> None: ...
    @overload
    def __get__(self, __instance: _T, __owner: type[_T] | None = None) -> Callable[_P, _R_co]: ...
    @overload
    def __get__(self, __instance: None, __owner: type[_T]) -> Callable[_P, _R_co]: ...
    if sys.version_info >= (3, 10):
        __name__: str
        __qualname__: str
        @property
        def __wrapped__(self) -> Callable[Concatenate[type[_T], _P], _R_co]: ...
#

this is in my builtins.pyi locally

tranquil turtle
#

You pinned wrong message

trim tangle
#

nah that's the right message

viscid spire
#

it's the right message

tranquil turtle
#

Ok -_-

trim tangle
#

I pinned this wdym

viscid spire
#

I think we are having a politician moment

trim tangle
cunning plover
#

is there a python linter (mypy configuration?) that allows to enforce correct private/public vars/methods access?
as work around i could use probably Interface/protocols for this enforcement, but can i do it without Interfaces?

#

This is truly interesting and right reason to use always Interfaces over Abstract classes then 🤔 i heard there are some other reasons to prefer interface over abstract classes, but not knowing full list yet

#

Yup. Interface helps. hooray to interfaces. Abstract classes look like work too though, so not really matters as long as i make sure to input abstract class as a type of variable, in order to forbid access to any unappropriate private stuff

deep saddle
#

thanks @acoustic thicket @brisk hedge, now I understand why I get the error - is there a more convenient way of resolving it without having to explicitly specify the generic type?

#

in my actual code I would have to do
y: list[tuple[str, str, bool] | SomeClass] | None
which is pretty awkward

soft matrix
viscid spire
deep saddle
#

that's better but I'd still have to import the alias or copy/paste it across 3 files which is kinda awkward still

#

I found out if I edit the function and type the parameter as: typing.Sequence[tuple[str, str, bool] | SomeClass] | None
then it accepts the Literal and seems to otherwise work fine

#

is this an okay solution or does it have other negative side effects?

acoustic thicket
#

yeah since Sequence is read-only it works

simple field
#

class A:
  pass

class A2(A):
  pass

class B:
  def __init__(self, a: A):
    self.a = a

class B2(B):
  def __init__(self, a2: A2):
    super().__init__(a2)

whats the least awkward way to indicate that a subclass attribute's type has changed? none of the checkers seem to infer that B2().a is an A2 rather than an A.

i tried:

class B2(B):
  def __init__(self, a2: A2)
    self.a: A2
    super().__init__(a2)

which didn't change anything

and

class B2(B):
  a: A2

which works but isnt great since now it implies a is a class attribute.

fierce ridge
simple field
# fierce ridge > which works but isnt great since now it implies a is a class attribute. `a: A...

then what does that mean for class attributes with a value set? e.g.

class A:
  value: str = 'value'

my understanding was that ClassVar only added strictness.

for example mypy complains about the instance attribute assignment here:

class A:
  value: ClassVar[str] = 'value'

A.value = 'new value'
A().value = 'new value'

but is fine with both the assignments here:

class A:
  value: str = 'value'

A.value = 'new value'
A().value = 'new value'
#

and what i'm seeing is pycharm seems to think this:

class A:
  a: str

implies the existence of A.a in autocomplete

fierce ridge
#

pycharm in particular is wonky unfortunately

#

it's unfortunate that this doesn't work

class B2(B):
  def __init__(self, a2: A2)
    self.a: A2
    super().__init__(a2)
simple field
#

if that's how ClassVar is supposed to be used, then it would make more sense to have InstanceVar instead. making a: T = ... and a: T by itself mean two different things in a class body is pretty awful honestly.

#

but in any case, the a: A2 in the class body solves my problem at least, so thanks @fierce ridge 🙂

fierce ridge
#

but yes i agree

#

it made sense in 2016 back when this was all kind of new and they/guido didn't want to make things too strict lest they seem unpythonic

simple field
#

a: T = ... is a lot more intuitive and consistent with the rest of the language. wouldn't really expect anyone to know about this ClassVar special case off the bat.

fierce ridge
#

alternatively you could say that you can't assign to a: T at class level, being an instance variable declaration, which follows typical idiomatic usage

#

most class-level attribute declarations are intended to be instance attributes in my experience

tidal venture
#

How to set a type hint that refers to an instance from a certain type/class? Callable is too generic.

tidal venture
tranquil turtle
#

Yes, i know

#

X is an instance of X
type[X] is a class itself

viscid spire
tidal venture
tranquil turtle
tidal venture
tranquil turtle
#

No

viscid spire
#

no

#
x: int = 123
tranquil turtle
#
y: type[int] = bool
viscid spire
#

(because bool is a subclass of int, if that's a sticking point)

tidal venture
#

Ok now i understand, thanks guys!

tranquil turtle
#

Bruh

#

No

viscid spire
#

you don't understand actually

tidal venture
#

sorry that was the reference to the class

#

sec

viscid spire
#

unless you are just naming your variables in a confusing way

tidal venture
tidal venture
tranquil turtle
#

You also have to pass argument of this type to this method

viscid spire
#

!e

class A: ...
class B(A): ...

x: A = A()
print(isinstance(x, A))
T1: type[A] = type(x) # value is `A`
print(issubclass(T1, A))

y: A = B()
print(isinstance(y, A))
T2: type[A] = type(y) # value is `B`
print(issubclass(T2, A))
rough sluiceBOT
#

@viscid spire :white_check_mark: Your 3.11 eval job has completed with return code 0.

001 | True
002 | True
003 | True
004 | True
tidal venture
viscid spire
tidal venture
acoustic thicket
#

: should be read as "has type"

buoyant ridge
#

Python's TypeVar accepts either multiple constrained types, or a bound type, where the typevar represents any subclass of the bound type

#

wouldn't it just be easier to represent that using a single constrained type

#

like i assume uh

#
from typing import TypeVar
T = TypeVar('T', str, bytes)
class StrSub(str):
  pass

def f(x: T):
  print(x)

f(5) # doesn't like this
f(StrSub()) # fine
#

constained types also include subclasses

#

so it seems like bound is literally just a single type constraint

#

I think I've answered my question if someone can confirm:

  • T = TypeVar('T', str) is literally just str (?). If this is true my question could be answered what's the difference between what's the difference between str and S = TypeVar('S', bound=str), and the difference is that in f(x: str, y: str) x and y can be different subclasses of str, but in f(x: T, y: T) if x is a subclass, y cannot be str, it must be x's subclass (or a subclass of x's subclass?)
chrome hinge
#

T = TypeVar('T', str) is literally just str (?).
No, but it will only be different if subclasses of str are involved. Not sure I've ever seen one.

#

(actually... is a StrEnum variant a str subclass? might be important, then)

#

what's the difference
Consider:

def f(x: str) -> str:
    return x
x = MyStrSubclass("yay")
reveal_type(f(x)) # str

T = TypeVar('T', str)
def f2(x: T) -> T:
    return x
reveal_type(f2(x)) # MyStrSubclass
buoyant ridge
#

RIGHT

#

wait, so then why can't you have a single constraint? that seems useful in this context

#

that example errors bc of single constraint, which i assume was intentional to show the theory, but then what's the difference between your T and the bound T T = TypeVar('T', bound=str)?

chrome hinge
#

Oh, I didn't realise you mean the constraint, I thought you were talking about TypeVar("T", bound=str), whoops.

#

Yeah, I believe TypeVar("T", str) is literally just str and hence not allowed due to being useless and confusing.

buoyant ridge
#

right, i think i understand then

trim tangle
# buoyant ridge I think I've answered my question if someone can confirm: - ``T = TypeVar('T', s...

A declaration like T = TypeVar("T", Foo, Bar, Baz) means that T resolved to exactly Foo, Bar, Baz.
For example:

import random
from typing import TypeVar

T = TypeVar("T", bound=int | str)
X = TypeVar("X", int, str)

def f(x: T) -> list[T]:
    return [x, x, x]

def g(x: X) -> list[X]:
    return [x, x, x]

if random.random() > 0.5:
    wat = "yes"
else:
    wat = 42

reveal_type(f(1))  # list[int]
reveal_type(f("foo"))  # list[str]
reveal_type(f(True))  # list[bool]
reveal_type(f(wat))  # list[str | int]

reveal_type(g(1))  # list[int]
reveal_type(g("foo"))  # list[str]
reveal_type(g(True))  # list[int]
reveal_type(g(wat))  # Error!

pyright playground link

buoyant ridge
#

ah, so is T bound to Foo resolved to being the same thing as T is constrained to any subclass of Foo?

trim tangle
#

Well, to any "subtype" more broadly. For example int is a subtype of int | str, and so is Literal["yes", 42]

buoyant ridge
#

oh right yeah

#

ah okay! that helps me a lot, showing the conversion between them makes it rly intuitive

raven prairie
#

I'm getting "Any" back from creating a weakref proxy to an object. Is there a type that I can use instead that I can distinguish weakref proxies from the genuine article?

#

I want a dictionary to contain the "owned" version of objects, but for all outside interactions to be done through proxy objects or weak references.

#

Weak references can be made typesafe it looks like

raven prairie
#

however, i want to avoid accidental owning references to objects.

raven prairie
# trim tangle can you show the code maybe?
@dataclass
class Actor:
    id: int
    atype: str
    pos_x: float
    pos_y: float
    bounds: pygame.Rect
    dir: Dir
    frame: int

    def get_ref(self):
        """Obtain a proxy to the actor."""
        return weakref.proxy(self)
trim tangle
#

Ah

raven prairie
#

weakref.proxy returns Any

trim tangle
#

yeah I don't think there's a good way to type weakref.proxy

#

I guess you could pretend that you're returning Actor form the method

raven prairie
#

I'd prefer for the proxy object type to have a unique type.

#

ActorRef was what I had in my head.

soft matrix
soft matrix
#
class ActorRef(Protocol):
    id: int
    atype: str
    pos_x: float
    pos_y: float
    bounds: pygame.Rect
    dir: Dir
    frame: int

@dataclass
class Actor:
    id: int
    atype: str
    pos_x: float
    pos_y: float
    bounds: pygame.Rect
    dir: Dir
    frame: int

    def get_ref(self) -> ActorRef:
        """Obtain a proxy to the actor."""
        return weakref.proxy(self)
trim tangle
soft matrix
#

ok maybe just dont make a protocol then 🤷

raven prairie
#

Hehe. Sorry, I have C++-brain.

#

though fwiw, I have found that reasoning about ownership, and even const correctness, leads to better code, even in a scripting language.

golden niche
#

how to get this type hint?

#

I can't find where is MSSBase

trim tangle
golden niche
trim tangle
golden niche
#

but I can't use it...

#

it's just not there

trim tangle
golden niche
trim tangle
#

Well then, import it from mss.base, not mss

golden niche
cedar sundial
#

Is it possible to overload a function with the same parameter type but depending on the value?

def extract(self, filename: str) -> pd.DataFrame | pd.ExcelFile: ...

I would like to make sure the typing is correct based on the filename extension, unless there is a different way from overloading?

soft matrix
#

Not currently like that no because constant string operations don't exist

#

But you can overload if you put suffix separately to the file name using a typing.Literal

cedar sundial
#

Ah. I think I'll just assert instead rather than change that everywhere

fierce ridge
#

for example pandas uses this for their inplace=True kwarg which changes the return type to None

rough sluiceBOT
#

pandas-stubs/core/series.pyi lines 407 to 420

@overload
def drop_duplicates(
    self, keep: NaPosition | Literal[False] = ..., inplace: Literal[False] = ...
) -> Series[S1]: ...
@overload
def drop_duplicates(
    self, keep: NaPosition | Literal[False], inplace: Literal[True]
) -> None: ...
@overload
def drop_duplicates(self, *, inplace: Literal[True]) -> None: ...
@overload
def drop_duplicates(
    self, keep: NaPosition | Literal[False] = ..., inplace: bool = ...
) -> Series[S1] | None: ...```
fierce ridge
#

but no you can't do something like return ExcelFile if it's *.xlsx

rustic gull
#

is that correct to typehint varriable as dict[str]?

fierce ridge
#

e.g. {"a": 1, "b": None} might be dict[str, int | None]

fierce ridge
#

what am i doing wrong here? mypy says signatures 1 and 2 overlap with incompatible return types, which i guess they do.

but i see the exact same pattern in the official pandas stubs and those apparently work fine: https://github.com/pandas-dev/pandas-stubs/blob/v1.4.3.220822/pandas-stubs/core/series.pyi#L718-L753

@overload
def index_antijoin(df1: pd.DataFrame, df2: pd.DataFrame, inplace: Literal[True] = ...) -> None: ...

@overload
def index_antijoin(df1: pd.DataFrame, df2: pd.DataFrame, inplace: Literal[False] = ...) -> pd.DataFrame: ...

@overload
def index_antijoin(df1: pd.DataFrame, df2: pd.DataFrame, inplace: bool = False) -> pd.DataFrame | None: ...

def index_antijoin(df1: pd.DataFrame, df2: pd.DataFrame, inplace: bool = False) -> pd.DataFrame | None:
    r"""Obtain rows from df1 for which the index is not in df2.

    Equivalent to:

        df1.loc[~df1.index.isin(df2.index)]
    """
    return df1.drop(index=df2.index.intersection(df1.index), inplace=inplace)
#

i also tried adding the * to make inplace keyword-only as a wild guess, but that of course didn't help

oblique urchin
#

so that if you don't pass that arg, exactly one overload matches

fierce ridge
#

oh wow, i missed that in the Series type stub

#

i see

#

i'll have to try to wrap my head around that

#

not sure i fully understand but thank you because it works now 😆

#
_Indexable = TypeVar("_Indexable", bound=pd.DataFrame | "pd.Series[Any]")



@overload
def index_antijoin(
    df1: _Indexable, df2: pd.DataFrame | "pd.Series[Any]", *, inplace: Literal[False]
) -> _Indexable: ...


@overload
def index_antijoin(
    df1: _Indexable, df2: pd.DataFrame | "pd.Series[Any]", *, inplace: Literal[True] = ...
) -> None: ...


@overload
def index_antijoin(
    df1: _Indexable, df2: pd.DataFrame | "pd.Series[Any]", *, inplace: bool = False
) -> _Indexable | None: ...


def index_antijoin(
    df1: _Indexable, df2: pd.DataFrame | "pd.Series[Any]", *, inplace: bool = False
) -> _Indexable | None:
    r"""Obtain rows from df1 for which the index is not in df2.

    Equivalent to:

        df1.loc[~df1.index.isin(df2.index)]
    """
    return df1.drop(index=df2.index.intersection(df1.index), inplace=inplace)

interestingly now i get a return type problem, although i could just # type:ignore that one

#

pandas returns DataFrame | Series[Any] | None but i'm returning _Indexable | None which i guess is a tighter restriction

#

not sure if there's a way to assert that my version is actually correct, instead of casting or ignoring

oblique urchin
#

I think your overloads are reversed? since your default is False but not passing the arg will match the True overload

fierce ridge
#

i also removed the = False from the 3rd one

#

i didn't have a clear sense of what ... really meant in a type annotation until now

oblique urchin
#

the default itself is ignored, what matters is that there is a default

fierce ridge
#

right, but in my mind i had it reversed, so that the overloads with ... indicated the use of non-default values

soft matrix
#

how should auto_variance interact with the suffix naming of type params?

oblique urchin
#

at least, that's my opinion. The language won't enforce anything obviously

soft matrix
#

yet™️ 😉

#

(im not actually gonna change that, im just thinking for if they become public api then naming probably matters)

raven prairie
#

Why is float = float // int

#

Shouldn't that be int?

oblique urchin
rustic gull
#

python/cpython#103445

mighty lindenBOT
rustic gull
#

wild

oblique urchin
#

the linked discuss thread is rather bad too

olive lotus
#

Hi. I have a function that takes in this type

dict[str, dict[str, str | set[str]]]

Issue is that I want to add something to the set, but python thinks I am adding it to the string rather

people[person_id]["movies"].add(movie_id)
Cannot access member "add" for type "str" Member "add" is unknown

So I came up with using isinstance, but that still failes. The full code I came up with is below

        for row in reader:
            person_id = row.get("person_id")
            movie_id = row.get("movie_id")
            if person_id and movie_id:
                if isinstance(people_dict[person_id]["movies"], set) and isinstance(
                    movies_dict[movie_id]["stars"], set 
                ):
                    people_dict[person_id]["movies"].add(movie_id) # still gives that message here
                    movies_dict[movie_id]["stars"].add(person_id) # and here
oblique urchin
#

so you need to first put people_dict[person_id] in a variable and then check isinstance() on that_dict["movies"]

oblique urchin
#

also this kind of sounds like you want to use a TypedDict

#

since the "stars" key should always be a set[str] and some other keys should always be a str

olive lotus
#

Never heard of a typed dict before.

oblique urchin
#

!d typing.TypedDict

rough sluiceBOT
#

class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict).

`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
olive lotus
#

Checking it out. That's nice that theres something better

trim tangle
#

So Eric Traut is going to keep working on pyright, right?

#

I saw a commit from him 4h ago

wicked scarab
#

wait, he's quitting?

acoustic thicket
trim tangle
feral wharf
#

Link?

wicked scarab
#

https://github.com/erictraut "former Microsoft Technical Fellow"

tranquil turtle
#

😭

mortal cipher
#

well that's interesting news...

brisk hedge
#

Former starting this spring, for context. The bio has been there since at least May.

frozen wraith
#

Who can answer me ? about my questions

carmine phoenix
#

can you have a TypedDict with optional keys? meaning that the key itself wont be present instead of its value being None...

frigid jolt
rustic gull
carmine phoenix
carmine phoenix
#

I'll put it under if TYPE_CHECKING

#

do you guys define TypedDict's under if TYPE_CHECKING?

frigid jolt
#

I don't, are you getting that error because you're using a version older than 3.11?

#

you need to import it from typing_extensions if that's the case

#

and typing_extensions itself must be >=4.0.0

carmine phoenix
#

btw is there a way to type hint a list or tuple that takes ints, and the length is 1, 2, or 3. exactly like range()

rustic gull
#

technically you could tuple[int] | tuple[int, int] | ...

trim tangle
#

Yeah, you can only do it with tuples

rare scarab
#

unless you can hide what teams you're on

#

If he's no longer working on pyright, that's just the way things are in corporate projects. It's a double edged sword.

soft matrix
brisk hedge
#

I do

wicked scarab
rare scarab
#

or closed as "not planned"

grave fjord
#

"Pattern will never be matched for subject type "Never""

rare scarab
#

that site never worked for me on my work network

#

It's not even a ssl or firewall issue. Just "The connection has timed out"

grave fjord
#

sorry here's the code

# pyright: strict

from typing import assert_never

def f(x: int | str ) -> str:
    match x:
        case int():
            return "got int"
        case str() as x_str:
            return x_str
        case _:
            assert_never(x)
#

demo.py:11:14 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)

#

that's on the case _: line

rare scarab
#

In this case, you might need to add a # type: ignore

brisk hedge
#

pyright considers the case impossible to reach since both int and str are covered

soft matrix
#

just looks like a bug

brisk hedge
#

not sure why that's an error

#

might be a bad choice of diagnostic

rare scarab
#

under normal operation, _ will never match

brisk hedge
#

oh wait strict mode

#

i see

soft matrix
#

_ should be allowed as Never, this really does look like a regression still

rare scarab
#

it would be the same if it was without the match. ```py
def f(x: int | str) -> str:
if not isinstance(x, int | str):
raise TypeError("x")
...

soft matrix
#

oh unless its a reportUnnecessaryIsInstance kind of check

#

yeah

brisk hedge
acoustic thicket
soft matrix
#

i thought eric was slightly more ok with exhaustive code checks with match

rare scarab
#

maybe it's explicitly checking for a raise statement?

#

or assert?

#

not sure. Never messed around with assert_never

tranquil turtle
#

noticed bug in pyright: ```py
o: type = int
type(o).flags + 0 # works fine at runtime, but pyright think that lest thing is a property

#

can someone pls confirm it and create github issue (if it is not created yet)?

rare scarab
#

What does doing that even solve?

fierce ridge
#

it fails in mypy too without type[int]

#

and tbh i don't see how it could work. the type checker can't know what __flags__ is, only that accessing it should be valid on a type[Any]

olive lotus
#

What would be the right way to type hint this?

return []

Just doing list acts as a catch all which is not what I want

tranquil turtle
tranquil turtle
#

That depends actually on other return statements

olive lotus
tranquil turtle
#

How can you annotate only one return statement? That doesn't make sense

olive lotus
#

So there are different return paths based on what happens.

So

if a == 2:
   return []

if a == 3:
   return [("hello", "hello")]

return None
tranquil turtle
#

What is the problem?

#

Your function returns list or None.
list[?] | None
What your list can contain? Tuple of str and str. Does the empty list consist only of tuple[str, str]? Yes, because you can't prove that it doesn't.
So there is only one good way to annotate this function

#

Also i think that list[T] | list[Any] is the same as list[T]

olive lotus
#

Ok I think I am getting it. I was thinking there was a special way to tell python that there would be 3 different return types, an empty list, a list with tuples or None

#

Because an empty list is different from a list with tuples.

tranquil turtle
#

There is no way to express length of container in type system

olive lotus
#

Ah. That makes it clear.

#

Thanks for explaining that

fierce ridge
#

or maybe it has to do with how it handles descriptors

#

ah, maybe it's confused because type is the type of type so it can't tell if it's the type or the instance thereof?

viscid spire
#

(Actually reading up on it, should substitute NoReturn in 3.11+ as well)

livid plover
#

Is there a way to have a wrapper function that injects an argument but the base function definition still defines the argument as required (ie just not optional)
I have a situation where I am injecting an argument but because I am not actually providing that argument, the syntax is invalid and the return type gets ignored (shows up as Any).

An example of what I mean,

@inject_a
def foo(a: int) -> int:
  return a

The problem here, is that if I call foo(), it will be displayed as Any. The "solution" seems to be making a an Optional[int] = None. But I don't like this solution because it implies that a is not actually required from within the function logic. When it is, it just gets handled in the case you don't provide it. (Obviously the actual case would be with a class of some type and not an int).
I wonder if there is a way to convey the intent. Concatenate seems to want to solve this purpose but it doesn't actually solve this issue. An example use case would be https://discord.com/channels/267624335836053506/1143885302503579708

#

When I think about it though, I can see a reason why this is imposible or really hard to fix. The most ideal situation is that the function signatrue would look like:

(method) def foo(
    a: int | None = None
) -> int

But the actual definition would be:

@inject_a
def foo(a:int) -> int:
  return a

This would be the most ideal implementation but I don't know that something like this exists or not

livid plover
#

sooooo ... I half found a solution. If all i wanted was to make help work;

from inspect import Parameter, signature

def inject_a(function: Callable[Concatenate[P], R]) -> Callable[Concatenate[P], R]:
    @wraps(function)
    def wrapped(*args: P.args, **kwargs: P.kwargs) -> R:
        a = kwargs.get("a", None)
        if a is None:
            a = 10
        kwargs["a"] = a

        return function(*args, **kwargs)

    sig = signature(function)
    new_sig = list(sig.parameters.values())[:-1] + [
        Parameter("a", Parameter.KEYWORD_ONLY, default=None, annotation=Optional[int])
    ]
    sig = sig.replace(parameters=new_sig)
    wrapped.__signature__ = sig
    return wrapped

This will result in help producing:

Help on function foo in module __main__:

foo(*, a: Optional[int] = None)

And that is closer to what I would like. However, the signature on my "ide" is still

(function) def foo(
    *,
    a: int
) -> int

Not exactly ideal. However, if I remember correctly, this is a limitation of using vscode.

brisk hedge
#

Concatenate will allow you to inject an argument into the first parameter, if that helps.

#

Something like

from typing import *
from functools import wraps

P = ParamSpec("P")
R = TypeVar("R")
A = TypeVar("A")
def inject_a(fn: Callable[Concatenate[A, P], R]) -> Callable[P, R]:
    injected_a: A = ... # type: ignore (value of A obtained however you wish)
    @wraps(fn)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
        return fn(injected_a, *args, **kwargs)

    return wrapper
    
@inject_a
def function(a: int, b: int, c: str, d: float, **kwargs: bool) -> float:
    print("hello", a)
    return a / 2

reveal_type(function) # Type of "function" is "(b: int, c: str, d: float, **kwargs: bool) -> float"
#

(@livid plover)

#

That's—I believe—the standard way to inject values to a signature

livid plover
young zealot
#

I have been thinking of making a class which can store a list of callbacks and when you __call__ the class it will forward the call to all the callbacks. Is it possible to type hint this so that the callbacks are all the same and the __call__ of the class matches the callbacks?

jade viper
#

For the love of god is there a way to keep my linter with the original linting from the function when using a lru_cache decorator?

rare scarab
#

It's still technically correct

soft matrix
#

the answer hasnt changed since you last asked

jade viper
rare scarab
#

don't use lru_cache

#

make your own wrapper with your "fixed" types

#
TFunc = TypeVar("TFunc", bound=Callable[..., Any])

def lru_cache(func: TFunc) -> TFunc:
  return functools.lru_cache(func)
jade viper
#

That worked perfectly.

#

Thank you so much!

#

If it's this easy, why hasn't functools been fixed yet? lol

rare scarab
#

because most people don't see it as a problem.

jade viper
#

Wait but does this accept parameters for the decorator?

rough sluiceBOT
#

stdlib/functools.pyi lines 66 to 73

if sys.version_info >= (3, 8):
    @overload
    def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ...
    @overload
    def lru_cache(maxsize: Callable[..., _T], typed: bool = False) -> _lru_cache_wrapper[_T]: ...

else:
    def lru_cache(maxsize: int | None = 128, typed: bool = False) -> Callable[[Callable[..., _T]], _lru_cache_wrapper[_T]]: ...```
jade viper
#

Just copy/paste this?

rare scarab
#

that's the stdlib type definition.

#

If you copy it, you'll have the same types as before

jade viper
rare scarab
#
@overload
def lru_cache(*, maxsize: int | None = ..., typed: bool = ...) -> Callable[[TFunc], TFunc]: ...
@overload
def lru_cache(func: TFunc, /) -> TFunc: ...

def lru_cache(*args: Any, **kwargs: Any) -> Any:
  return functools.lru_cache(*args, **kwargs) # type: ignore
#

The reason stdlib returns _lru_cache_wrapper is because you can call f.cache_info(), f.cache_clear() and f.cache_parameters() on it

rough sluiceBOT
#

stdlib/functools.pyi lines 54 to 64

@final
class _lru_cache_wrapper(Generic[_T]):
    __wrapped__: Callable[..., _T]
    def __call__(self, *args: Hashable, **kwargs: Hashable) -> _T: ...
    def cache_info(self) -> _CacheInfo: ...
    def cache_clear(self) -> None: ...
    if sys.version_info >= (3, 9):
        def cache_parameters(self) -> _CacheParameters: ...

    def __copy__(self) -> _lru_cache_wrapper[_T]: ...
    def __deepcopy__(self, __memo: Any) -> _lru_cache_wrapper[_T]: ...```
jade viper
rare scarab
#

it accepts a function or arguments, but not both

jade viper
#

But is the function not an argument in this case?

rare scarab
#

the function is passed when used as a decorator

#

In fact, my type may be wrong because you may want to do @lru_cache(10) instead of @lru_cache(maxsize=10)

prisma talon
#

Hello I have 2 things that are annoying me.
Im on python 3.10.5

I am using the csv library that is built in (I think the abbreviation is stdlib?)

with open("data/Students.csv") as csv_file:
    csv_reader = csv.DictReader(csv_file, delimiter=",")
    for row in csv_reader:
        row["X Coord"]

The "X Coord" is underlined with
Expected type 'int | slice', got 'str' instead

Also how do I except a exact list of strings as an argument to a function?

def __init__(self, house: Location, school: School, level: typing.Literal["E", "M", "H"]):

When passing a string to level I get
Expected type 'Literal["E", "M", "H"]', got 'str' instead

rustic gull
prisma talon
rustic gull
#

however the first question no idea why its showing this error, even in the docs thats how they access the values from row

prisma talon
#

I should specify for the first one. It is not giving an error just a warning.

rustic gull
oblique urchin
prisma talon
#

Pycharm is the IDE not sure how to view the type checker

oblique urchin
#

PyCharm has its own type checker, which is famously not very good

prisma talon
#

Ok, I will deal with it I guess. (No need to use a different type checker even if it is possible. This is just a small project.)

prisma talon
# rustic gull should be enough

Could it be that there is no guarantee that row[1] is equal to one of those values? It is read from the CSV. I know that those are the only possible values for that column but python/typechecker would not

oblique urchin
rustic gull
prisma talon
#

Ah, makes sense. What is the recommended way to handle this? Instead of literal just use string and then throw an error if it is not in the list.

oblique urchin
#

yes, or keep the literal type and do the check before you call that function

prisma talon
#

Thanks!

round bolt
#

What is mypy yelling at me for here:

OriginalFunc = Callable[Param, RetType]
DecoratedFunc = Callable[Concatenate[str, Param], RetType]
Missing type parameters for generic type "OriginalFunc"  [type-arg]
Missing type parameters for generic type "DecoratedFunc"  [type-arg]
Missing type parameters for generic type "OriginalFunc"  [type-arg]
error: Missing type parameters for generic type "DecoratedFunc"  [type-arg]
rare scarab
#

You need to provide Param and RetType to your type alias usages

fierce ridge
#

would TypeAlias help there?

soft matrix
undone saffron
soft matrix
#

@trim tangle would you be able to update the pyright version for pyright playground please?

carmine phoenix
carmine phoenix
# trim tangle There is a mypy extension

I have it installed, but you need to have a separate tab open and match the errors with the line numbers... not as convenient as having the warnings be displayed directly in the code

trim tangle
#

oh, the extension is that underfeatured?

buoyant ridge
#

what do u guys think of PEP 727? It does a good job of listing its counterarguments imo... verbosity is definitely a concern. but it seems like it could be a good way to formalise documentation patterns

trim tangle
#

!pep 727

rough sluiceBOT
trim tangle
#

Wow, this is... extremely verbose

buoyant ridge
#

yeah, even for like, simple type annotations, it's a lot

#

if you want to document all your parameters it feels like half of your type code will be the docs

trim tangle
gleaming crescent
# rough sluice

Interesting approach but it seems rather, not so good. That would make the params pretty long..?

trim tangle
#

The proposed syntax is, with all due respect, a bit mad

void panther
#

When I though of something similar, editor support felt crucial to make it less in the way of everything, but that can't cover normal text editors

buoyant ridge
#

oh interesting ideas. and yeah, you're def justified about the verbosity

trim tangle
#

You can already do almost this with Annotated[str, "the name of a user in the system"]

#

I don't see why a new library symbol is even required

void panther
#

well it can't be mixed up with other things in Annotated

buoyant ridge
#

i was thinking that as well actually, unless there is any other use case for a string as metadata, which i guess there could be

void panther
#

I think everything in Annotated should have some unique wrapper obj around it because if there are multiple uses for the metadata everything will just get mixed up

buoyant ridge
#

i see your point. doesn't help for the verbosity, though. also, in a way, this proposal is kind of for introducing a new standard already

trim tangle
#

Nevertheless, this verbosity would not affect end users as they would not see the internal code using typing.doc()

And the cost of dealing with the additional verbosity would only be carried by those library maintainers that decide to opt-in into this feature.

I disagree. I often read library code, especially for libraries without an online reference (like FastAPI which the author of this PEP made, and starlette on which it is based)

#

Any authors that decide not to adopt it, are free to continue using docstrings with any particular format they decide, no docstrings at all, etc.
I thought the point of this PEP was to standardize argument docstrings, not add another convention?

void panther
#

I think the usability really just depends on everything fitting on one line, after that it's a lot of data in the signature. But still harder to ignore when reading if it's not in the docstring

trim tangle
#

If the annotation is large and complicated, I'm probably going to ignore it

buoyant ridge
#

a lot of good points, ty for your input :)

trim tangle
#

Well, I don't have a good alternative tbh

#

I'm mostly just heckling from the back row

void panther
#

I think it could be nice but really would just have to see actual code using it to decide on that, but don't really think it has that big of a chance of going through when normal docstrings are somewhat standardised for params, google style is fairly low effort to parse visually and group all the docs together

#

one thing it'd make possible without additional work would be documentation for globals and other free standing vars instead of the current way that's not really standard

buoyant ridge
#

yeah, agree. i wonder why standardizing an existing format was rejected

#

oh, true, true

pastel egret
#

It's also accessible at runtime without having to write/import a complex parser.

void panther
#

I suppose, but the work for those is already done and I'd hope parsing data from docstrings isn't done that often to be much a bottleneck

tranquil turtle
modest spindle
#

looking at a function definition and seeing symbol vomit that takes up 4 lines is awful. this only makes that worse.

surely just having a rigid format for the docstring and a flag that you're using the new rigid format gets you to the same place whilst keeping definitions looking like normal python?

#

Editors don’t have a way to support all the possible micro languages in the docstrings and show nice user interfaces when developers use libraries with those different formats.

pick/create one, make it the official standard, then this is a non-issue.

#

It wouldn’t solve any of the other problems, like getting editor support (syntax checks) for library authors, the distance and duplication of information between a parameter definition and its documentation in the docstring

if there was an actual standard, editors could/would totally be able to support it. duplication > syntax vomit. this is a problem solved in most other languages, why is python deciding to be a special fairy and do it a completely different way?

soft matrix
#

ideally though there does need to be a way to get them at runtime

brisk hedge
tranquil turtle
#

& doc(...) is also pretty good

azure mural
#

Guys

#

Now hear me out

#
def hello(to: str("The name to say hello to")) -> None: …
chrome hinge
# azure mural Now hear me out

no, hear me out

TheNameToSayHelloTo = NewType("TheNameToSayHelloTo", str)
def hello(to: TheNameToSayHelloTo) -> None: …
rare scarab
azure mural
rare scarab
#

better wrap it in quotes to prevent execution

brisk hedge
#

type.__matmul__ for Annotated when BAB_BE_TROLL

tranquil turtle
#
def hello(num: int @ 'The number of people to say hello to') -> None: …
wicked scarab
#

that's the nicest-looking one imo

rustic gull
#
def hello(the_number_of_people_to_say_hello_to: int) -> None: ...
trim tangle
#

adding @ support for all type objects plus all the type-like things sounds like a bad idea to me

#

Especially when it's not really necessary

viscid spire
#

you don't think it's bad?

trim tangle
rare scarab
trim tangle
#

damn that's evil

rare scarab
#
class DocType:
  ...

  def __rmatmul__(self, typ):
    return Annotated[typ, self] # doesn't actually work for typing

def doc(doc: str) -> DocType:
  return DocType(doc)

foo: str @ doc("this is a foo")
#

It would basically make it an opt-in feature

undone saffron
#

I'd rather we not overload operators for documentation. I don't even see a need for this, there are existing IDEs that can parse inline variable documentation already, so clearly it's already possible, why is this not just the responsibility of documentation tooling?

#

It doesn't seem to belong in the type system.

void panther
#

The intent is mostly to give easy runtime access

undone saffron
#

That's literally not in the rationale or motivation section, so I don't know why you think that's the intent.

#

and there's Already inspect.cleandoc, as well as sphinx's methods if you use sphinx to get runtime info

oblique urchin
undone saffron
#

programtically doesnt necessitate "At runtime" and in context, it's talking about editors and consistency

#

so if that's actually the motivation, there should probably be a clarification that it is referring to runtime access as well

oblique urchin
#

I would count a unit test that inspects the metadata as "runtime"

undone saffron
#

I would not. Tests are not runtime code. And saying this makes it possible is misleading, because this is already possible with existing documentation tools.

#

and in the full context, it's talking about in editors and the impact on maintainers of the seperation of the documentation from the variable:

Because the docstring is in a different place in the code than the actual parameters and it requires duplication of information (the parameter name) the information about a parameter is easily in a place in the code quite far away from the declaration of the actual parameter. This means it’s easy to refactor a function, remove a parameter, and forget to remove its docs. The same happens when adding a new parameter: it’s easy to forget to add the docstring for it.

Editors can’t check or ensure the consistency of the parameters and their docs.

It’s not possible to programatically test these docstrings, for example to ensure documentation consistency for the parameters across several similar functions, at least not without implementing a custom syntax parser.

#

Nothing here indicates at runtime, and this reads as "makes it easier to write an editor plugin"

fleet sorrel
#

!typehinting

rough sluiceBOT
#
Type hints

A type hint indicates what type a variable is expected to be.

def add(a: int, b: int) -> int:
    return a + b

The type hints indicate that for our add function the parameters a and b should be integers, and the function should return an integer when called.

It's important to note these are just hints and are not enforced at runtime.

add("hello ", "world")

The above code won't error even though it doesn't follow the function's type hints; the two strings will be concatenated as normal.

Third party tools like mypy can validate your code to ensure it is type hinted correctly. This can help you identify potentially buggy code, for example it would error on the second example as our add function is not intended to concatenate strings.

mypy's documentation contains useful information on type hinting, and for more information check out this documentation page.

viscid spire
fleet sorrel
#

sorry

simple field
#

re: parameter documentation, how about bare strings below the argument?

def example(
  arg_1: int,
  """documentation for arg_1"""
  arg_2: int,
  """documentation for arg_2"""
) -> int:
  ...
tranquil turtle
#

Why below? This is not consistent.
First you read module docstring, and them module itself
Class docstring - class body
Function docstring - function body
Variable docstring - variable declaration

viscid spire
#
def example(
  arg_1: int, # documentation for arg_1
  arg_2: int, # documentation for arg_2
) -> int:
  ...

lemon_clown

#

And it's in python already!

simple field
#

idk how many checkers actually pick those up though

#

or if it was ever made into an official thing

soft matrix
tranquil turtle
simple field
#

how so?

tranquil turtle
#
def example(
  arg_1: int,
  """foo bar"""
  arg_2: int,
) -> int:
  ...
simple field
#

i can see the ambiguity for someone who'd be encountering these for the first time, yeah

oblique urchin
#

Not just the first time. It's also just hard to remember

soft matrix
#

ive personally never had any problems with it

simple field
#

oops hit enter a bit too early. id say in comparison to the other proposed solutions it balances tradeoffs well.

soft matrix
#

also normally the doc string makes it obvious enough as it contains a reference to the name

simple field
tranquil turtle
#

To me doc-declaration order seems more logical, than declaration-doc order, and i always thought that rejected pep 224 proposed doc-decl, and not decl-doc

tranquil turtle
simple field
soft matrix
pale otter
#

It makes sense to sweep all the documentation for a function and its attributes into a single docstring

soft matrix
#

eg```py
def parse_trade_url(url: StrOrURL, /) -> TradeURLInfo | None:
"""Parses a trade URL for useful information.

Parameters
-----------
url
    The trade URL to search.
#

or even ```py
async def edit(self, *, name: str | None = None, tagline: str | None = None, avatar: Media | None = None) -> None:
"""Edits the chat group.

    Parameters
    ----------
    name
        The new name of the chat group.
    tagline
        The new tagline of the chat group.
    avatar
        The new avatar of the chat group.
    """
tranquil turtle
#

No, this is ok
I thought you are talking about x: int """x: do X-related stuff""" where you prefix docstring with arg name

soft matrix
#

oh right understood

simple field
#

hmm, how about requiring some indentation as well?

def example(
  a: int,
    """
    does the extra indentation make it easier to
    tell/remember which one this belongs to?
    """
  b: int,
): ...
tranquil turtle
#

Indentation is not important in python (except for top-level things), so it will be inconsistent

#

Also, function and class docstring are placed at the same level as the thing they are describing

simple field
simple field
tranquil turtle
tranquil turtle
simple field
simple field
#

yeah it's technically possible to do something like this

def example(
            a: test,
        b: test,
):
    pass

but why would anyone do this? what information is it supposed to convey? and how would it make using indentation to relate docstrings to attributes inconsistent? the rule would specifically be about an indented string following an argument, not arguments themselves.

oblique urchin
simple field
oblique urchin
#

It's probably possible, but I don't think the tokenizer currently even tracks indentation within parentheses

trim tangle
brisk hedge
#

I'm still highly partial to rst docstrings 🤷‍♀️

reef yacht
#

rst is one of those things where it's just super annoying and hard to remember compared to markdown, but it's WAY more powerful and can be reasoned about programmatically much easier

cedar sundial
#

What does a * mean in types?

Expression of type "DataFrame*" cannot be assigned to declared type "T@Imputer"
    Type "DataFrame*" cannot be assigned to type "T@Imputer"
soft matrix
#

its inferred sort of

tranquil turtle
#

Looks like a pointer type

cedar sundial
#

How do I get rid of the star then? Idk whether that is causing this error or not 😆

soft matrix
#

the star isnt causing the error

#

well probably

#

can you share the code

#

and the full error

cedar sundial
#
T = TypeVar("T", pd.DataFrame, NDArray[np.float64])

if not isinstance(X, pd.DataFrame):
    X = pd.DataFrame(X)

#
error: Expression of type "DataFrame*" cannot be assigned to declared type "T@Imputer"
    Type "DataFrame*" cannot be assigned to type "T@Imputer" (reportGeneralTypeIssues)
soft matrix
#

is there a reason youre using constraints here?

tranquil turtle
#

That makes sense. You function is generic, type of X is T. You are assigning non-T to X

soft matrix
#

cause you should probably be using a union

cedar sundial
#

Ahh, I don't understand these generics very well. I just looked at the docs and didn't want a bound, so used that instead

#

Wouldn't a union also be a constraint?

tranquil turtle
#

I think you dont need TypeVars there at all

soft matrix
#

they do have a class that they arent showing

#

unless the functions called Imputer

brisk hedge
tranquil turtle
#

You can try annotating X as DataFrame

cedar sundial
#

Oh, its just

class Imputer(Generic[T]):
    def __init__(self, method: Methods, features: list[int] = []): ...
    def transform(self, X: T) -> pd.DataFrame: ...
cedar sundial
dull lance
#

How can I properly type annotate the following decorator function so that the type checker can identify both x and func as members of obj?

from __future__ import annotations

from typing import Protocol, TypeVar

class Decorated(Protocol):
    def func(self) -> None: ...

T = TypeVar('T')
def decorate(cls: type[T]):
    class _Decorated(Decorated, cls):
        def func(self) -> None:
            return None

    assert issubclass(_Decorated, cls)

    return _Decorated

@decorate
class MyClass:
    def __init__(self, x: int) -> None:
        self.x = x

obj = MyClass(1)
obj.x
obj.func()
#

Or is this simply impossible without explicit type intersections?

soft matrix
#

its not possible

#

mypy and pyright can infer intersections to some degree but they cant really escape the scope they are created in

dull lance
#

Oh well, thanks for the info

young zealot
#

I have a func with three parameters that can either all be specified or all be None, is there a way with overloads to specify that? I could switch to one param with a tuple[a,b,c] | None setup but I would prefer to have them individual params

viscid spire
#

overload with no params and overload with all params non-optional

viscid spire
young zealot
young zealot
viscid spire
#

It has a fib function

young zealot
#

weird, link works for me. This is what it should have:

from typing import overload

@overload
def a(a: int, b: str, c: float): ...
@overload
def a(a: None, b: None, c: None): ...
def a(a: int | None, b: str | None, c: float | None):
    pass```
viscid spire
#

(The function only has one parameter)

viscid spire
young zealot
#

yeah I realized I forgot that thanks!

oblique urchin
viscid spire
young zealot
#

Thanks

slender topaz
#

following django tutorial:

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    def __str__(self):
        return self.choice_text

pylint is complaining that __str__ does not in fact return a string. I don't want to make a habit of ignoring pylint, so should I str() it or can I shut it up somehow?

jade viper
#

Why on Earth am I getting this?

brazen jolt
#

how is References defined? is it an enum?

jade viper
#

Just a regular object I think

#

Lemme check

brazen jolt
#

it just holds a string

jade viper
#

Yeah, it's legacy company code :/

brazen jolt
#

the function expects References type though, i.e. an instance of that class

#

can you change references to an enum?

#

if not, you can either do str or a Literal of all of those options as your type-hint for that func

jade viper
#

That would make it so you'd have to call it like References.GS_VALUE.value, right?

brazen jolt
#

no

#

in fact that would not work

#

because .value would hold the actualy string

#

to satisfy the annotation, you'd want to pass in one of the enum variants

jade viper
#

I mean

#

To actually access the string

brazen jolt
#

but yes, inside of the function, you'd do .value to get the string

jade viper
#

Yeah, that's a no go

jade viper
#

This snippet is so spread across the project that it'd probably break something

brazen jolt
#

you can always change that, that would be the ideal solution

#

just find all references of the References class being used in the code

jade viper
#

It would be, but if anything breaks I'm probably fired lol

jade viper
brazen jolt
#

LSPs can do all of the finding for you, you might even be able to use some tools like sed to replace the string, but yeah, I get that you don't want to do that with this kind of project

#

how do other functions in the codebase type-hint accepting one of these?

#

just look at those and adhere to the established style then

jade viper
#

I'm the only one that type hints lol

brazen jolt
#

oh

#

just do str

jade viper
#

Isn't there a way to customize Enum so that I can get the value simply by calling the enum name?

brazen jolt
#

if you really want to be fancy, do: py class References(object): A = "A" B = "B" TYPE: ClassVar[TypeAlias] = Literal["A", "B"] and use References.TYPE in the annotation

#

ideally, the constants there should also be annotated with ClassVar[str]

jade viper
#

Ooh let me try

viscid spire
brazen jolt
jade viper
#

Where do I import TypeAlias from?

viscid spire
#

typing module

jade viper
viscid spire
#

What version you using

jade viper
#

3.8

brazen jolt
#

you can still do py class Foo(str, Enum): ... though StrEnum is like 3.10+ or something like that

viscid spire
brazen jolt
#

that could indeed work pretty well

viscid spire
#

Although funny cause typealias only really lasted 2 versions

jade viper
#

The thing is, I need Foo.X to be "A" and not <Foo.X: 'A'>

brazen jolt
#

print(Foo.X) will be "A"

#

however repr(Foo.X) will be that

jade viper
#

We use it as function calls mostly

brazen jolt
#

you can do Foo.X.startswith

#

for example

#

or any other str methods

#

or pass it to a func that expects str

#

however repr will be <Foo.X: 'A'>

#

you can probably override the repr if you have to, but know that it could make it annoying to distinguish from a regular string in logs / when debugging

jade viper
#

Not sure if Django will strify it

viscid spire
#

It is already a string

brazen jolt
#

the thing is, the type is literally an instance of the string class

#

it's just not literally a str, it's an instance of a subclass that inherits from str

jade viper
#

I mean whether it would use __repr__ or __str__

brazen jolt
#

try and see lemon_glass

jade viper
#

I will

#

Thanks for the help:)

#

Have a good night

rustic gull
#
from collections.abc import Iterator
...
    def __iter__(self) -> "Iterator[dict[str, t.Any]]":
        return iter(self.__dict__.items())
``` is this proper way to annotate `__iter__` dunder method?
soft matrix
#

yeah

fierce ridge
rustic gull
viscid spire
rustic gull
viscid spire
#

I c

frigid jolt
rustic gull
frigid jolt
rustic gull
#

I cant cause some of my code depends on annotations

#

Making them strings would break it

frigid jolt
# frigid jolt `from __future__ import annotations`

mh? this would not require them as strings, means that you are able to do the following

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from asyncio import AbstractEventLoop


class Example:
    loop: AbstractEventLoop
rustic gull
#

Which Like i said would ruin my code

soft matrix
#

!d typing.get_type_hints

rough sluiceBOT
#

typing.get_type_hints(obj, globalns=None, localns=None, include_extras=False)```
Return a dictionary containing type hints for a function, method, module or class object.

This is often the same as `obj.__annotations__`. In addition, forward references encoded as string literals are handled by evaluating them in `globals` and `locals` namespaces. For a class `C`, return a dictionary constructed by merging all the `__annotations__` along `C.__mro__` in reverse order.

The function recursively replaces all `Annotated[T, ...]` with `T`, unless `include_extras` is set to `True` (see [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) for more information). For example:
soft matrix
#

You should probably be using this instead of annotations anyway

rustic gull
#

Well i'm not the one who coded the lib im using Shrugdge

deep saddle
#

Hello, why am I getting this type checking error? (both underlines have the same error)

Here's my (relevant) code: ```py
from typing import Any, Callable, Concatenate, Coroutine, ParamSpec, TypeVar, overload

from discord import Interaction
from discord.app_commands import Command, Group
from discord.ext.commands import Cog

CommandGroupT = TypeVar("CommandGroupT", bound=Group | Cog)
P = ParamSpec("P")
T = TypeVar("T")
Coro = Coroutine[Any, Any, T]
CommandCallback = (
Callable[Concatenate[CommandGroupT, Interaction, P], Coro[T]]
| Callable[Concatenate[Interaction, P], Coro[T]]
)

def log_slash_command(command: Command[CommandGroupT, P, T]) -> Command[CommandGroupT, P, T]:
callback = command._callback # typed as a CommandCallback[CommandGroupT, P, T]

@overload
def wrapped(self: CommandGroupT, interaction: Interaction, *args: P.args, **kwargs: P.kwargs) -> Coro[T]:
    ...

@overload
def wrapped(interaction: Interaction, *args: P.args, **kwargs: P.kwargs) -> Coro[T]:
    ...

@ TODO: @wraps(callback)
def wrapped(*args, **kwargs):
    if len(args) == 0:
        raise NotASlashCommand
    elif len(args) == 1 or isinstance(args[0], Interaction):
        interaction, *command_args = args
        self = None
        assert isinstance(interaction, Interaction)
    else:
        self, interaction, *command_args = args
        assert isinstance(self, Group | Cog)
        assert isinstance(interaction, Interaction)

    print(f"{interaction.user.mention} used `/{command.name}` with arguments {command_args!r}")

    if self is not None:
        return callback(self, interaction, *command_args, **kwargs)
    else:
        return callback(interaction, *command_args, **kwargs)

command._callback = wrapped
return command
#

This code produces the same issue, removing the discord related stuff ```py
P = ParamSpec("P")
T = TypeVar("T")

Callback = Callable[Concatenate[int, int, P], T] | Callable[Concatenate[float, P], T]

def wrapper(f: Callback[P, T]) -> Callback[P, T]:
@overload
def wrapped(a: int, b: int, *args: P.args, **kwargs: P.kwargs) -> T:
...

@overload
def wrapped(a: float, *args: P.args, **kwargs: P.kwargs) -> T:
    ...

def wrapped(*args, **kwargs):
    if isinstance(args[0], float):
        a, *rest = args
        assert isinstance(a, float)
        return f(a, *rest, **kwargs)
    else:
        a, b, *rest = args
        assert isinstance(a, int)
        assert isinstance(b, int)
        return f(a, b, *rest, **kwargs)

return wrapped```
#

I don't really understand what Arguments for ParamSpec "P@wrapper" means

#

clearly f(a, b, *rest, **kwargs) includes all of the arguments

fathom river
#

Why are you unpacking the args if you're just going to add them as positional afterwards?

deep saddle
#

removing the if-statement is equally unsuccessful

#

(that was my original code, I switched it to this: py if self is not None: return callback(self, interaction, *command_args, **kwargs) else: return callback(interaction, *command_args, **kwargs) in an attempt to fix that error)

fathom river
#

I don't think the way you're trying to accomplish this is implemented yet

deep saddle
#

bummer

fathom river
#

Perhaps you could try subclassing Command instead? The thing is (besides ParamSpec argument unpacking not being supported) that P from the wrapped signature isn't the same P as the one in the generic command argument in the log_slash_command signature. The outer one does not include Interaction, but the inner one does.

fierce ridge
#

higher-order functions and callbacks are still really hard with python typing

deep saddle
# fathom river Perhaps you could try subclassing Command instead? The thing is (besides ParamSp...

Perhaps you could try subclassing Command instead
You mean such that I would return a new instance of a different class, rather than the original (modified) instance? This is not an option for several reasons.

the outer one does not include Interaction, but the inner one does
P does not include the Interaction. Specifically, the Command class defines callback as Callable[Concatenate[Interaction, P], ...] (like CommandCallback, above)

deep saddle
#

*rest is P.args

#

a, b would be a completely separate int, int

#

see how Callback = Callable[Concatenate[int, int, P], T]?

#

and that doesn't explain why return f(*args, **kwargs) doesn't work

#

Here's my current code (using discord.py) if anyone would like to take another look https://paste.pythondiscord.com/MAVA.
constraints:

  1. the command argument to log_command must be typed as app_commands.Command[Cog | Group, x, y]
  2. the return value of log_command must be the same instance that was passed as an arguments

otherwise everything else is fair game

undone saffron
#

discord.py has a way to register an "After" command handler

#

could look into what's wrong, but sounds like more effort than using that here

rustic gull
#

is type[T] a new way of doing typing.Type[T]? or its something else

soft matrix
#

Yes

#

As described by pep 585 they are equivalent

rustic gull
#

!pep 585

rough sluiceBOT
scarlet nacelle
#

What libs let you overload functions with genericalias typehints and unions like tuple[int|float]|float

rustic gull
# rough sluice

frozenset # typing.FrozenSet
there is a thing called frozenset in python builtin xd?

rustic gull
rough sluiceBOT
#

@functools.singledispatch```
Transform a function into a [single-dispatch](https://docs.python.org/3/glossary.html#term-single-dispatch) [generic function](https://docs.python.org/3/glossary.html#term-generic-function).

To define a generic function, decorate it with the `@singledispatch` decorator. When defining a function using `@singledispatch`, note that the dispatch happens on the type of the first argument:

```py
>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)
```...
gritty crater
#

I've delayed fully learning TypeVars and generics for a very long time now, though I'd like to get my definitions straight first.

Correct me if I'm wrong on any of this, but to my knowledge generics are just variables in place of actual references to existing types like int, float or another user-defined class. These generic variables can be a collection of multiple existing types, like str or bytes, a subtype of something or just a reference to itself, where it can be anything.

acoustic thicket
#

sounds about right

limber path
#

help

#

I define 2 classes

#

class 1 has a function that returns object of type class 2

#

but class 2 is defined after class 1
so I cant type hint the object of class 2
since it's not defined yet in class 1 definition

#

what is the solution

restive rapids
#

you can use string literal typehints

#

or from future import annotations

limber path
#

can you send an example please

#

and an explanation of what are these

restive rapids
#
In [1]: from __future__ import annotations

In [2]: class A:
   ...:     def meth(self) -> B:
   ...:         return B()
   ...:

In [3]: class B:
   ...:     def meth(self) -> A:
   ...:         return A()
   ...:

In [4]: A().meth()
Out[4]: <__main__.B at 0x12eb712b650>

In [5]: B().meth()
Out[5]: <__main__.A at 0x12eb7008510>

In [6]: A.meth.__annotations__
Out[6]: {'return': 'B'}

In [7]: B.meth.__annotations__
Out[7]: {'return': 'A'}
In [1]: class A:
   ...:     def meth(self) -> "B":
   ...:         return B()
   ...:

In [2]: class B:
   ...:     def meth(self) -> "A":
   ...:         return A()
   ...:

In [3]: A().meth()
Out[3]: <__main__.B at 0x151d1bb8fd0>

In [4]: B().meth()
Out[4]: <__main__.A at 0x151d1da6ed0>

In [5]: A.meth.__annotations__
Out[5]: {'return': 'B'}

In [6]: B.meth.__annotations__
Out[6]: {'return': 'A'}
limber path
#

what future is?

restive rapids
#

a builtin module

#

!d future

rough sluiceBOT
#

Source code: Lib/__future__.py

__future__ is a real module, and serves three purposes:

• To avoid confusing existing tools that analyze import statements and expect to find the modules they’re importing.

• To ensure that future statements run under releases prior to 2.1 at least yield runtime exceptions (the import of __future__ will fail, because there was no module of that name prior to 2.1).

• To document when incompatible changes were introduced, and when they will be — or were — made mandatory. This is a form of executable documentation, and can be inspected programmatically via importing __future__ and examining its contents...

limber path
#

is it recommanded?

restive rapids
#

it can be useful in a case like yours

limber path
#

ok

#

I am reading one sec

#

ok I will try this out thank you!

scarlet nacelle
trim tangle
scarlet nacelle
#

Bruh

trim tangle
#

though in this case it doesn't work with tuples

scarlet nacelle
#

Why cant generics like list[str] be overloaded

trim tangle
scarlet nacelle
#

Y

#

You can iterate over it and check its contents

#

Just make sure it has __iter__, and isnt str, bytes or bytearray tho

trim tangle
# scarlet nacelle Y
class Foo:
    def __init__(self) -> None:
        self._str_lists: list[list[str]] = []
        self._int_lists: list[list[int]] = []

    @singledispatchmethod
    def add(self, arg):
        raise NotImplementedError

    @singledispatchmethod
    def add(self, arg: list[int]):
        self._int_lists.append(arg)

    @singledispatchmethod
    def add(self, arg: list[str]):
        self._str_lists.append(arg)

foo = Foo()
list1: list[int] = []
list2: list[str | bytes] = ["something"]
list3: list[dict[str, int]] = []

What should happend on these lines?: ```py
foo.add(list1)
foo.add(list2)
foo.add(list3)

scarlet nacelle
#

First and third are not implemented error

#

Second should run last foo

trim tangle
scarlet nacelle
#

Thats where they would go unless there are more specific fitting type hints

trim tangle
scarlet nacelle
#

!paste

rough sluiceBOT
#
Pasting large amounts of code

If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/

After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.

trim tangle
#

Imagine if you had

class Foo:
    ...
    def join(self) -> str:
        return "".join(itertools.chain.from_iterable(self._str_lists))

If you later add a bytes object to list2, you are in big trouble

#

Python is a dynamically typed language, and type information is pretty much lost at runtime.

scarlet nacelle
#

Hm ok

trim tangle
#

Your particular application might be fine with doing some of these assumptions. That's why there are libraries like typeguard, pydantic, beartype etc.

raven prairie
#

It seems like array.array has room for a generic type, but when I attempt to use it I get a compile error about array not being subscriptable.

soft matrix
#

what version are you on?

#

it only appears to have been made subscriptable at runtime on 3.12+

raven prairie
#

3.11.5. So array[int] won't work until then? Got it.

raven prairie
#

Okay, I've got mypy hooked up to pyxel but I am finding that I am getting some errors that are straight up wrong

#
ichor\game.py:18: error: "Image" has no attribute "load"  [attr-defined]
ichor\game.py:19: error: "Image" has no attribute "load"  [attr-defined]
ichor\game.py:20: error: "Image" has no attribute "load"  [attr-defined]

And this is the pyi I'm dealing with... https://paste.ee/p/Pp9cB

#

It seems like there's multiple definitions for class Image, a foward declaration that simply has ... and a normal one that comes later.

soft matrix
#

weird, wheres this pyi from?

tranquil turtle
#

why is codecs.CodecInfo.decode annotated as def decode(input: bytes, errors: str = 'strict') -> tuple[str, int]:, if at runtime input is not bytes but memoryview ?

#

also, forcing users to shadow builtin name is bad

soft matrix
#

memoryview was a subclass of bytes pre 3.12 typing changes

#

or maybe 3.11

tranquil turtle
#

im on 3.11, and pyright screams at me

tranquil turtle
soft matrix
#

well the change was made retroactively

soft matrix
#

now if you want the old behaviour you need to use collections.abc.Buffer

tranquil turtle
soft matrix
#

open an issue

tranquil turtle
#

done

rustic gull
#

Hey

tranquil turtle
rustic gull
#

Oh sorry

#

Didn’t really know

soft matrix
#

in a PEP 649 world it's still possible to have stringised annotations right?

#

if you manually annotate something as : "SomeString"

tranquil turtle
#

You can use whatever you want
If you used string, you will get string

soft matrix
#

yeah ok thought as much

trim tangle
#

just like in life

#

this type hinting thing just got dark

frigid jolt
#

lmao

simple field
#

i want a type alias that takes exactly two parameters i.e. X, Y and evaluates to Annotated[X, list[Y]]. is that doable?

#

generic type alias* to be clearer

#

i tried:

from typing import TypeVar, Annotated

X = TypeVar('X')
Y = TypeVar('Y')
XY = Annotated[X, list[Y]]


a: XY[int, str] = 12

which mypy didnt like:

error: Bad number of arguments for type alias, expected: 1, given: 2  [type-arg]
soft matrix
#

nope, annotated is only generic over its first parameter

#

well its not possible doing it that way

#

well ok lmao it does work with 3.12 typealias syntax

simple field
# soft matrix well ok lmao it does work with 3.12 typealias syntax

ohhh ok cool, like one of these? TIL, thanks

type Combo[X, Y] = Annotated[X, list[Y]]

tried using this in mypy playground and it keeps saying error: invalid syntax; you likely need to run mypy using Python 3.12 or newer even though 3.12 is chosen from the top, both for mypy 1.5.1 and master branch. is there a specific branch ill need to clone or a new version to wait for?

also do you know if there's gonna be any kind of backport for this in typing-extensions maybe?