#type-hinting

1 messages · Page 15 of 1

pastel egret
#

You can annotate it with cls: type[Self], but there's no reason to. Type checkers already know what it is.

rare scarab
#

I wish type checkers knew what args were in overrides.

tranquil turtle
#

you mean def foo[T: A | B](num: T) -> T: ... ?

oblique urchin
slender timber
#

if a function has overloads why do we still need to annotate the actual implementation function?

#

seems redundant and wrong

dreamy cove
#

am i holding this wrong? this is how all the dataclass docs say to do it

@dataclass
class Foo():
    foo: Optional[str] = field(default=None)
Expression of type "Field" cannot be assigned to declared type "str | None"
  Type "Field" cannot be assigned to type "str | None"
    "Field" is incompatible with "str"
    Type cannot be assigned to type "None" PylancereportGeneralTypeIssues
dreamy cove
pastel egret
merry current
#

Yes! I knew there was a new way too but not what it looked like

#

Thnx

acoustic thicket
#

whats the difference?

trim tangle
#

I think you meant (num: T) -> T

oblique urchin
oblique urchin
#

while the overload version would return A

rare scarab
rough sluiceBOT
#

Simple overloading of methods and functions through an @overload decorator

slender timber
rare scarab
#

there's so many libraries that do that.

fading temple
#

how i typehint these type of functions?

from typing import Callable


def f(cadena: str) -> Callable[[str], "?"]:
    print(cadena, end="")
    return f

f("h")("o")("l")("a")```
#

what "?" should be?

soft matrix
#

use a recursive type alias

#
from collections.abc import Callable
from typing import TypeAlias

FReturn: TypeAlias = Callable[[str], "FReturn"]

def f(cadena: str) -> FReturn:
    print(cadena, end="")
    return f

f("h")("o")("l")("a")
fading temple
soft matrix
#

cause its deprecated

fading temple
#

since when?

oblique urchin
#

3.9

fading temple
#

okay thanks

compact mountain
tranquil turtle
rare scarab
#

which is basically ```py
Self = TypeVar("Self", bound="Foo")

class Foo(Protocol):
def call(self: Self) -> Self: ...

soft matrix
#

thats just more code for no reason really

rare scarab
#

yeah... Self works

slender timber
#

i have created type stubs for cefpython, should i distribute them via typeshed or make a package on pypi instead?

rough kettle
#

Is there any way to express "the arguments for this function get passed to another function therefore they have the same signature"?

I have the following function that wraps an async command, gets the result of the coroutine and returns that:

from functools import wraps
import asyncio
from typing import ParamSpec, TypeVar, Coroutine, Callable
import typer

P = ParamSpec("P")
R = TypeVar("R")
Y = TypeVar("Y")
S = TypeVar("S")
class AsyncTyper(typer.Typer):
    def async_command(self, *args, **kwargs):
        def decorator(async_func: Callable[P, Coroutine[Y, S, R]]) -> Callable[P, Coroutine[Y, S, R]]:
            @wraps(async_func)
            def sync_func(*_args: P.args, **_kwargs: P.kwargs) -> R:
                return asyncio.run(async_func(*_args, **_kwargs))

            self.command(*args, **kwargs)(sync_func)
            return async_func

        return decorator

AsyncTyper.async_command takes the same arguments as AsyncTyper.command, I'd like to express that somehow

eager vessel
rough kettle
#

How so?

#

I know you can annotate args and kwargs using ParamSpec but I don't know how to "connect" the two signatures together

eager vessel
rough kettle
#

They are not

eager vessel
#

Well, your async_command shouldn't accept any parameters really? 🤔

#

I assume you'd use it like this?

@instance.async_command()
async def command(a, b, c) -> ...:
    pass
#

It doesn't make sense for async_command method to accept anything, or do you want to take same args as Typer.command?

oblique urchin
#

that would provide a standardized way to test the stubs and distribute them, so hopefully less work on your part

slender timber
#

alright then

#

typeshed it is

#

damn, line-length is 130

#

that barely fits on my screen

rough kettle
eager vessel
#

But maybe I'm wrong

slender timber
slender timber
#

oh cool

#

also do i need to explicitly mark a typealias?

oblique urchin
#

yes

slender timber
#

mm okay

#

can i use @type_heck_only for TypedDict that aren't (probably?) exposed by the library?

#

oops

#

wtf discord

oblique urchin
slender timber
#

hmm okay, i already underscored them

oblique urchin
#

that should be enough

slender timber
#

should i import new stuff from typing_extensions?

#

ah ok i need to

oblique urchin
#

yes

slender timber
#

but if the new syntax is supported, why not the imports themselves?

#

I am already doing stuff like this

_JSValue: TypeAlias = (
    bool
    | bytes
    | float
    | int
    | str
    | Callable[..., _JSValue]
    | list[_JSValue]
    | tuple[_JSValue, ...]
    | dict[_JSValue, _JSValue]
    | None
)
oblique urchin
#

For syntax, the type checkers basically have logic like "| is allowed if either you're in a stub or you're on >=3.10"

#

but for imports if you do from typing import TypeAlias on 3.7, they look in typing.pyi and see TypeAlias doesn't exist there

slender timber
#

kinda confusing

#

and do i need from __future__ import annotations?

#

i am using annotatations before declaring them

oblique urchin
slender timber
#

alright

rough sluiceBOT
#

stdlib/builtins.pyi line 416

class str(Sequence[str]):```
oblique urchin
#

even this works

slender timber
#

isn't there any char type in _typeshed?

oblique urchin
#

there's been talk of that but it's fairly complicated to implement. str is indeed a sequence of str; if you index it you get another str

median ledge
#

So, I know you can use @overloadwith classmethod and such.
However, a method decorated with my decorator shows an error:
Argument of type "class_or_instance_method[(), int]" cannot be assigned to parameter "func" of type "_F@overload" in function "overload"
I think it's because of the return type of my decorator, but idk what's wrong.

here's my decorator:

class class_or_instance_method(Generic[P, R_co]):
    def __init__(self, func: Callable[Concatenate[Any, P], R_co]):
        self._func = func

    def __get__(self, instance: Any | None, owner: type) -> Callable[P, R_co]:
        if instance is None:
            cls_or_self = owner
        else:
            cls_or_self = instance

        def new_func(*args: P.args, **kwargs: P.kwargs) -> R_co:
            self._func
            return self._func(cls_or_self, *args, **kwargs)

        return new_func
hearty shell
frigid jolt
#

Any | None None is included in Any, isn't it?

oblique urchin
#

But returning Any | None is different from returning Any in practice

#

If a function returns Any | None, a type checker will flag if you access an attribute on the object, because None doesn't have that attribute

median ledge
# hearty shell Can you show how this is being used?

minimal example

class Model(metaclass=ModelMeta):
    @overload
    @class_or_instance_method
    def a(cls_or_self:ModelMeta)->int:
        ...

    @overload
    @class_or_instance_method
    def a(cls_or_self: Model)->int:
        ...

    @class_or_instance_method
    def a(cls_or_self: Model | ModelMeta)->int:
        return 1
soft matrix
#

have you tried reordering the decorator?

median ledge
hasty jungle
#

in #software-architecture , @cunning plover explained a very cool construction with NewType (#software-architecture message) ; it inspired me a question that I ask here

let's say I'd like a

PositiveInteger = NewType("PositiveInteger", int)

but I would like an automatic runtime check x > 0. Something I can thing about follows the flavor of a factory method:

def asPositiveInteger(x: int) -> PositiveInteger:
  assert x > 0, "Int {} is no positive integer".format(x)
  return PositiveInteger(x)

but that wouldn't force the user to actually go through asPositiveInteger, as it could directly do PositiveInteger(-1)

is there a way to somehow combine the two constructions, in such a way that a validation step is performed when "transferring"? I'm especially interested in assertions kind of checks. there is nothing in that direction in this doc https://docs.python.org/3/library/typing.html, as it seems

something I can think about, is to have PositiveInteger somehow "opaque", in such a way the user cannot (with a static linter happy) call it, because it might not be callable. but that seems a bit weak and beyond that, I'm not sure it's possible to make a type being opaque

frigid jolt
#

define PositiveInteger inside asPositiveInteger

hasty jungle
#

hmmmm, not sure to follow ; then I cannot type it anymore (nor use the type in a signature of another method)

mystic harness
wraith linden
#

Hey. Does anyone know of a tool to convert code written using the new 3.12 type parameter syntax to the old syntax?

soft matrix
#

pyupgrade probably will at some point

#

but i dont think that will come till its at least merged into main

solid light
#
from typing import Literal

COINS = ["heads", "tails"]

def flips_to_reach(min_required_first: int, min_required_second: int, required_first: Literal[COINS[0], COINS[1]]):```How do I dynamically typehint something as "an item of the list"? Tried this but it didn't work
trim tangle
#

or define it explicitly

#
from enum import Enum

class Coin(Enum):
    heads = "heads"
    tails = "tails"

def flips_to_reach(min_required_first: int, min_required_second: int, required_first: Coin):
#

to get all Coins you can actually iterate over the Coin class

trim tangle
#

then you can do the checks in its __init__

solid light
trim tangle
#

Alternatively, there's this hack @hasty jungle :

# positive_integer.py

_Opaque = NewType("_Opaque", int)
PositiveInteger = NewType("PositiveInteger", _Opaque)

def positive_integer(x: int) -> PositiveInteger:
    assert x > 0, f"Int {x!r} is no positive integer"
    return PositiveInteger(_Opaque(x))
trim tangle
#

!e

from enum import Enum

class Coin(Enum):
    heads = "heads"
    tails = "tails"

print(list(Coin))
rough sluiceBOT
#

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

[<Coin.heads: 'heads'>, <Coin.tails: 'tails'>]
solid light
#

Looks like there's a __members__ too

#

!e ```py
from enum import Enum

class Coin(Enum):
heads = "heads"
tails = "tails"

print(Coin.members)```

rough sluiceBOT
#

@solid light :white_check_mark: Your 3.11 eval job has completed with return code 0.

{'heads': <Coin.heads: 'heads'>, 'tails': <Coin.tails: 'tails'>}
solid light
#

Eh, would need to get .keys() I guess

#

And then convert to list

#

So yeah, might as well just do list(Coin), thanks

trim tangle
#

yeah, something like [coin.value for coin in Coin] if you really need the strings

oblique urchin
solid light
#

Or if not, then why?

oblique urchin
#

I don't think there's a "should" in there. StrEnum means the enum members are subclasses of str, but a plain Enum can still have members whose values are strs

#

It's just different options

solid light
#

Ah, fair

#
class Coin(Enum):
    heads = "heads"
    tails = "tails"

def flips_to_reach(min_required_first: int, min_required_second: int, required_first: Coin):
    ...

flips_to_reach(3, 3, "foo")
```how comes I don't get a linting error here (since "foo" isn't a valid coin)?
oblique urchin
#

main.py:10: error: Argument 3 to "flips_to_reach" has incompatible type "str"; expected "Coin" [arg-type]

solid light
#

Hmm

#

I guess my VSC doesn't have linting for some reason....

#

Any idea how to fix that?

solid light
#

Figured it out

(cmd+shift+p -> python: select linter -> mypy)

wraith linden
#

Is it possible to keep track of the development of pep 695 in cpython? Is it being worked on currently?

oblique urchin
#

feedback welcome

wraith linden
azure mural
solid light
#

I had to cmd+shift+p -> python: select linter -> mypy

#

It was set to disabled

hasty jungle
#

the _Opaque thing is cool! thx

slender timber
#

can i use the new [T] syntax in stub files?

hallow flint
#

probably only if you’re comfortable only supporting 3.12 and newer

#

mypy uses python’s ast module to parse, so generally you need to run mypy with and targeting a version of python containing the syntax you want to use

#

pyright has its own parser, so there’s no technical limitation there, but it might still complain if you’re targeting an older python

soft matrix
#

Could typed ast not support it?

oblique urchin
#

I think Sebastian tried to backport positional-only args syntax to it and failed

fading temple
#

how can i typehint a variable that can be a class or any of his child classes?

oblique urchin
#

type[BaseClass]

fading temple
#

thanks!

fading temple
#

what can i do if importing modules for typehinting causes a circular import?

slender timber
hallow flint
drifting ibex
#

Hi, I'm seeing some different behavior between mypy and pyright with this code on Python 3.11:

from typing import Protocol, Self, TypeVar

class SupportsAdd(Protocol):
    def __add__(self, other: Self, /) -> Self:
        ...

T = TypeVar('T', bound=SupportsAdd)

def add(a: tuple[T, ...], b: tuple[T, ...]) -> tuple[T, ...]:
    return tuple(i + j for i, j in zip(a, b))

s1 = (1, 1)
s2 = (2, 'a')
reveal_type(s1)
reveal_type(s2)
reveal_type(add(s1, s1))
reveal_type(add(s1, s2))

The intent of this code was to have an add function that could add homogeneous tuples. pyright doesn't give an error because it infers a Union type where I don't want it to. mypy gives me an error like I want, but I'm not sure if it's the "correct" error.

pyright:

pyright 1.1.305
/Users/gsgx/code/test2.py
  /Users/gsgx/code/test2.py:21:13 - information: Type of "s1" is "tuple[Literal[1], Literal[1]]"
  /Users/gsgx/code/test2.py:22:13 - information: Type of "s2" is "tuple[Literal[2], Literal['a']]"
  /Users/gsgx/code/test2.py:23:13 - information: Type of "add(s1, s1)" is "tuple[int, ...]"
  /Users/gsgx/code/test2.py:24:13 - information: Type of "add(s1, s2)" is "tuple[int | str, ...]"
0 errors, 0 warnings, 4 informations
Completed in 0.67sec

mypy:

test2.py:21: note: Revealed type is "Tuple[builtins.int, builtins.int]"
test2.py:22: note: Revealed type is "Tuple[builtins.int, builtins.str]"
test2.py:23: note: Revealed type is "builtins.tuple[builtins.int, ...]"
test2.py:24: error: Value of type variable "T" of "add" cannot be "object"  [type-var]
test2.py:24: note: Revealed type is "builtins.tuple[builtins.object, ...]"
Found 1 error in 1 file (checked 1 source file)
  1. I understand how pyright is inferring the type int | str, but I don't understand why mypy is inferring the type of T to be object.
  2. Which behavior is correct in this case?
hallow flint
#

great question!

#

i think pyright's behaviour is buggy, since if it's inferring T to be int | str i think that doesn't line up with the bound you have on the type var

#

but if you get rid of the bound, i don't think either behaviour is incorrect

#

mypy gets object from doing what it calls a join: basically, object is the common base class of int and str.
mypy tends to do joins a little more often than it should. and errors from joins can be a less intuitive than errors from unions. but in this case it's not incorrect.

#

hmm mypy and pyright both seem happy with:

from typing import Protocol, Self, TypeVar
class SupportsAdd(Protocol):
    def __add__(self, other: Self, /) -> Self: ...
def takes_add(x: SupportsAdd, y: SupportsAdd) -> None: ...
def f(a: int | str, b: int | str) -> None: takes_add(a, b)

so maybe i'm wrong about the bound thing. it's late for me, i should go sleep

slender timber
#

I came across a partially type hinted codebase, should I rather make a PR inlining the type hints for that project or submit to typeshed instead?

#

also I am type hinting a signature like this:

def compress2(src: SupportsBytes, **kwargs): ...

some of the kwargshave default values, and TypedDict doesn't have a way for default values
so do I instead change the function signature to reflect the kwargs with their default values?

def compress2(src: SupportsBytes, *, kw1: ... = ..., kw2: ... = ...): ...
#

also, can i have an overload and an implementation function type hint both, in a stub file?

solid light
#
def get_padding_length(prop: str, rounds_data: list[Round], dp: int | None = None):
    largest_of_prop = max(getattr(round_, prop) for round_ in rounds_data)

    if prop == "variance":
        dp = get_dp_for_variance(largest_of_prop, is_first=True)
        return len(f"{largest_of_prop:,.{dp}f}"), largest_of_prop        

    if dp:
        return len(f"{largest_of_prop:,.{dp}f}")   
    return len(f"{largest_of_prop:,}")```how would you overload this function to specify that if the prop is "variance" the return is `tuple[int, float]`, else the return is `int`?
#

I tried this, but it gives an error saying the types overlap with incompatible return types```py
@overload
def get_padding_length(prop: Literal["variance"], rounds_data: list[Round], dp: int | None) -> tuple[int, float]: ...
@overload
def get_padding_length(prop: str, rounds_data: list[Round], dp: int | None) -> int: ...
def get_padding_length(prop: str, rounds_data: list[Round], dp: int | None = None):
largest_of_prop = max(getattr(round_, prop) for round_ in rounds_data)

if prop == "variance":
    dp = get_dp_for_variance(largest_of_prop, is_first=True)
    return len(f"{largest_of_prop:,.{dp}f}"), largest_of_prop        

if dp:
    return len(f"{largest_of_prop:,.{dp}f}")   
return len(f"{largest_of_prop:,}")```
tranquil turtle
tranquil turtle
solid light
#
@overload
def get_padding_length(  # type: ignore
    prop: Literal["variance"], rounds_data: list[Round], dp: int | None
) -> tuple[int, float]: ...```seems to work, thanks
#

Second question

#
@dataclass(eq=False, repr=True)
class Round:
    pool_size: int
    prob_heads_first: float
    prob_tails_first: float
    variance: float
    mean_num_flips: float
    median_num_flips: float
    mode_num_flips: int
    mean_consec_first: float
    median_consec_first: float```instead of `props: str` is there a way to say "one of the Round properties, but not variance"?
tranquil turtle
#

Literal['pool_size','prob_heads_first',<all other props except variance>...]

#

I dont think there is a shorter and simpler way to do it

solid light
#

Fair enough

solid light
#

I.e. Literal[", ".join(x for x in Round.__dataclass_fields__ if x != "variance")] or something Shrug

tranquil turtle
#

No.
You can write tool that will inspect fields at runtime and insert their names in specific places in your code. But i dont think it would be very reliable.

#

Or you can write plugin for mypy

solid light
#

That's fine, this'll do for now. Thanks 😄

tranquil turtle
#

Typeshed also has weird long literal types

solid light
#
RoundPropertyNotVariance = Literal[
    "pool_size",
    "prob_heads_first",
    "prob_tails_first",
    "mean_num_flips",
    "median_num_flips",
    "mode_num_flips",
    "mean_consec_first",
    "median_consec_first"
]
RoundProperty = RoundPropertyNotVariance | Literal["variance"]```I have to do it the other way round anyway since I want it as a type, but don't get why mypy errors there
oblique urchin
solid light
#

!d typing.get_args

rough sluiceBOT
solid light
oblique urchin
#

I think because it's documented together with get_origin

#

Not sure that was a great idea

solid light
#
@overload
def get_dp_for_variance(variance: float, is_first: Literal[False], variance_padding: float) -> int: ...
@overload
def get_dp_for_variance(variance: float, is_first: Literal[True], variance_padding: Literal[None] = None) -> int: ...
def get_dp_for_variance(variance: float, *, is_first: bool = False, variance_padding: float | None = None):
    dp_for_zero = 7 if is_first else variance_padding - 2
    return 3 if variance > 1_000 else 5 if variance > 1 else dp_for_zero```How can I get this to work? The idea is that either `is_first` or `variance_padding` is provided.

I want to make it so that that the `else variance_padding - 2` doesn't get an error from the `None - 2` definition (`variance_padding` will *always* be an int in the `else` block)

Sample calls:
`get_dp_for_variance(3.14159265, is_first=True)`
`get_dp_for_variance(1.23456789, variance_padding=5)`
fading temple
#

this is correct then?

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from panells import Panell```
wraith linden
drifting ibex
drifting ibex
#

Based on that answer, I'm wondering if being able to hint the type checker to prefer joins over unions for certain types would be a useful addition to the type system?

cloud jewel
#

I'm getting errors when running pre-commit that don't occur normally.
error: Library stubs not installed for "requests" [import]

Now, the mypy pre-commit readme mentions that this might happen and suggests adding the following line to .pre-commit-config.yaml:
args: [--no-strict-optional, --ignore-missing-imports]

However, I've done just that and still get the issue. I also have a mypy.ini file with the following content:

[mypy]
ignore_missing_imports = True

I do also have the stubs installed, but that shouldn't play a role.

Does anyone know what might be happening here?

hallow flint
#

ugh don't use --no-strict-optional it's evil

cloud jewel
#

yep, that fixed it - guess I'll see if I end up sticking with it, thanks for the help either way!

autumn glen
#

Hello, is there video release about typing summit in Pycon?

soft matrix
#

not yet afaik

oblique urchin
#

We didn't get professional recording. We recorded it on a laptop but not sure when we'll release it all

soft matrix
#

do you have the slides for it or was it just free form?

oblique urchin
#

we don't

soft matrix
#

;(

#

ill have to come along next year

oblique urchin
#

please do! it'll be closer for you too

soft matrix
#

oh is it in the uk?

oblique urchin
#

no, just the east coast

#

Pittsburgh

soft matrix
#

oh right 😅

autumn glen
#

Thanks @soft matrix @oblique urchin

regal summit
#
from typing import (
    Generic,
    TypeVar,
)

_T = TypeVar('_T')

class Foo(Generic[_T]):
    pass

foo: Foo[int] = Foo()
``` is there  a way to get the _T type from my instance `foo`? perhaps as a string
soft matrix
#

yes but not well at all

regal summit
#

how would I do this

soft matrix
#

foo = Fooint and then use self.__orig_class__.__args__[0]

#

oh and as a word of warning that wont work in init/new

regal summit
#

thanks though

soft matrix
#

im currently in the process of writing a pep aiming to make this process better in the pep the code would just bepy class Foo[T]: ... Foo[int]().T

regal summit
#

nice, thatd be really nice

trim tangle
#

eeh, not sure

#

that will not work when T is inferred

#

and if you want to go this far, you can do Foo(int) instead

brisk hedge
#

that's surely not the point at all?

#

it's a supported runtime reflection feature that should not be only tucked behind a pair of dunders

#

so something like a function from typing, or the .T attribute (which is conveniently already connected to the namespace as a part of the generic syntax)

slender timber
soft matrix
oblique urchin
#

I'd probably prefer a typing. function to access them over an actual runtime attribute which can hide user-defined attributes

soft matrix
#

yeah but type checking for that imo is a bit gross

#

i was planning on adding __args__ to the instance to store the values of the specialised params

#

but accessing those through a typing function would mean some literal string api or something which im not a fan of

#

i also think having an attribute called the same thing as a type param is a strange thing to do

spiral fjord
#

When I see .T I think of the Transpose, not the typevar

oblique urchin
#

yeah was just about to say that

#

numpy arrays have a .T attribute for their transposition

soft matrix
#

hmm

oblique urchin
#

plus if you change this in 3.13, you'll now break existing generic classes with such attributes

soft matrix
#

should have done some cursed frame hacks to make array^T work :(

oblique urchin
#

that means a backwards compatibility break, which we generally don't want

brisk hedge
oblique urchin
#

maybe you could make Foo.get_type_param(T) work

#

where you first get T from Foo.__type_params__

#

(or a function wrapping it)

soft matrix
#

the idea was that it couldnt overwrite anything so it generally shouldnt break

#

but idk how feasible it actually is

soft matrix
soft matrix
#

otherwise T is undefined

oblique urchin
#

yeah that's why I said you have to first get the TypeVar object out of Foo.__type_params__

soft matrix
#

oh right

soft matrix
#
class Foo[T]:
    # compiler-generated code
    @property
    def __args__(self) -> tuple[type[T]]:
        return some_c_api_wizardry()
    @property
    def T(self) -> type[T]:
        return self.__args__[0]
    @T.setter
    def T(self, value: Any) -> None:
        warnings.warn(DeprecationWarning, "Don't do this ;)")
        self.__T_redirected__ = value
    # user code
    def __init__(self, value: T):
        self.T = value  # oops, what a strange variable name
```something vaguely like this i think
#

oh but this doesnt work with slots it does cause we can detect the name in the slots and raise a warning when constructing the class and not include the properties

#

it still needs a lot of work but im convinced its possible to do without breaking things

compact mountain
oblique urchin
#

I sponsored a few of @soft matrix's PEPs for example

compact mountain
#

i see! sounds good

errant ice
#

Using, PyCharm and its reporting a type error when reading a csv with DictReader

reader = csv.DictReader(csvfile)
for row in reader:
    print(row['name'])

I get a warning Expected type 'int | slice', got 'str' instead
Any suggestion on how to get the type hinting correct?

soft matrix
#

you could always cast if you are absolutely sure you want to put up with the pain in the ass that is pycharm's type checker

vivid ore
#

hm

errant ice
#

pain in the ass that is pycharm's type checker blobgrimacing

#

Can I use a different type checker, hinting within pycharm?

vivid ore
#

it seems you have to check isinstance(sth, Iterable) to call iter on sth
but the documentation explicitly states:

Checking isinstance(obj, Iterable) detects classes that are registered as Iterable or that have an __iter__() method, but it does not detect classes that iterate with the __getitem__() method. The only reliable way to determine whether an object is iterable is to call iter(obj).

Is it possible to check if an object is iterable in a way that mypy will recognize?

soft matrix
errant ice
#

interesting, the type(row) is <class 'dict'>

#

I guess pycharm does not know this

#

Does VS Code get it right?

soft matrix
#

yep

oblique urchin
rough sluiceBOT
#

stdlib/csv.pyi line 67

class DictReader(Generic[_T], Iterator[_DictReadMapping[_T | Any, str | Any]]):```
oblique urchin
#

DictReader is an Iterator[dict], so your rows should be dicts

errant ice
#

Thanks, everyone

slender timber
#

how can i type hint an argument accepting one of "mask", "audio", ["mask", "audio"]?

hearty shell
#

You should use an Enum, perhaps a Flag? But if you are just using literals, using a tuple would be better, so you could type it as Literal["mask", "audio"] | tuple[[Literal["mask"], Literal["audio"]]]

#
from enum import Flag, auto

class Mode(Flag):
  MASK = auto()
  AUDIO = auto()

def foo(m: Mode) -> None: ...

"""
`["mask", "audio"]` becomes `Mode.MASK | Mode.AUDIO`
"""
mellow talon
#

Hello all,
I've a question re type hinting.

I've created a class and a method returning the whole class

class Test:
   def __init__(self):
    self.rows = []

   def mymethod() -> Type['Test']:
    ...
    return cls 

The above is a minimal example, of course. rows is getting populate during the life cycle of the class, so by the time I recall mymethod, rows has a collection of items I can loop through
.
However, when I try to get access to rows from an external class/function I get the folowing warning from PyLance Cannot access member "rows" for type "Type[Test]"

Is there any way to avoid it?

I've been reading here https://stackoverflow.com/questions/33533148/how-do-i-type-hint-a-method-with-the-type-of-the-enclosing-class/33533514#33533514, interisting post but not necessarily getting into the right direction while experimenting with a 3.10

Any (type) hint for me?

tranquil turtle
#

in your example .rows is an instance attribute, not an attribute of a class. So you can access it only through instance, not through class

mellow talon
#

@tranquil turtle sorry. I forgot to add the classmethod decorator which makes it a class attribute

mellow talon
#

It's a "huge" class, published on a repo ... would the link to the repo work?

rough sluiceBOT
#

gsc_wrapper/query.py lines 757 to 767

@classmethod
def from_disk(cls, filename: str) -> Type['Report']:
    """"""
    import pickle

    if filename != '':
        with open(filename, 'rb') as f:
            data = pickle.load(f)
            return cls(data[0].get('url'), 
                        data[0].get('query'), 
                        data[1])```
mellow talon
#

But Self has been introduced with 3.11 ... currently I'm on 3.10

#

Or am I wrong?

grave fjord
#

You can use from typing_extensions import Self

mellow talon
#

And sorry to ask, but there is always something to learn. Why the Type['Report'] is not correct?

grave fjord
#

You're returning an instance of Result not an instance of type

#

The type checker should have failed on line 765

mellow talon
#

It has not, that's why I was confident it was the correct one

grave fjord
#

Are you running it on that file?

mellow talon
#

Running the linter, you mean?

grave fjord
#

Also looks like it's -> Self | None

#

Running mypy on gsc_wrapper/query.py

mellow talon
#

Not using mypy

grave fjord
#

What are you running?

mellow talon
#

In fact I'm "just" using the linter, trying to find the best one out PyLance, PyLint, Flake8

#

I'm still "refining" certain aspects of the python coding

grave fjord
#

There's other problems in that code like to_disk returns None sometimes

#

So your type checker isn't configured correctly

mellow talon
#

Guess this will fix it def to_disk(self, filename='') -> str | None:

grave fjord
#

Yeah but you should find out why your type checker is allowing it

mellow talon
#

Any suggestion there? I think somehow my environment screwed up

#

I've been trying to solve this separately with very little luck. I'm continuing to get PyLint errors although I disabled it

grave fjord
#

You should get CI to run your checks to eliminate problems caused by your own environment

mellow talon
#

Ok, learning new things here ... CI is ?

mellow talon
rough sluiceBOT
#

gsc_wrapper/query.py line 530

def get(self) -> Type['Report']:```
mellow talon
#

because I was in need to return a strong typed object but at that time of the code the class report does not exist

grave fjord
mellow talon
plush cargo
#

Help Requested 😳

here is the full code for the command I'm having an issue with: https://paste.pythondiscord.com/mivejofayu

Specifically the [*item_types] in the line:```
```py
async def item(interaction: discord.Interaction, item_type: typing.Literal[*item_types], item_name: str):

The error is Unpacked arguments cannot be used in type argument lists Pylance.
I can run this fine on my pc but once I use my hosting service to run the bot 24/7 I get this error

SyntaxError: invalid syntax
File "DnD-Bot.py", line 354
async def item(interaction: discord.Interaction, item_type: typing.Literal[*item_types], item_name: str):
^

The hosting service i use is called Railway

I'm trying to have it so when a user uses the command /item it will give them a choice of the Items listed in

item_types = (
        'Adventuring Gear',
        'Armor and Shields',
        'Trinkets',
        'Weapons',
        'Firearms',
        'Explosives',
        'Wondrous items',
        'Currency',
        'Poisons',
        'Tools',
        'Siege Equipment'
    )```
peak echo
plush cargo
peak echo
#

!e ```py
import typing

a = tuple("abc")
t = typing.Literal[a]
print(t)

rough sluiceBOT
#

@peak echo :white_check_mark: Your 3.11 eval job has completed with return code 0.

typing.Literal['a', 'b', 'c']
plush cargo
peak echo
#

Just don't unpack it?

plush cargo
#

how do i do that? i've never dealt with packing or unpacking ;-;

#

If you mean removing the typing.Literal[]

peak echo
#

typing.Literal[item_types]

#

Without the *

plush cargo
#
Traceback (most recent call last):
  File "c:\Users\Mitchell\Desktop\APX\Discord-Bot\APX-Bots\DnD-Bot\DnD-Bot.py", line 353, in <module>
    @client.tree.command(name='item', description="Allows you to search for an item")
     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Mitchell\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\app_commands\tree.py", line 887, in decorator   
    command = Command(
              ^^^^^^^^
  File "C:\Users\Mitchell\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\app_commands\commands.py", line 665, in __init__
    self._params: Dict[str, CommandParameter] = _extract_parameters_from_callback(callback, callback.__globals__)
                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Mitchell\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\app_commands\commands.py", line 373, in _extract_parameters_from_callback
    param = annotation_to_parameter(resolved, parameter)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Mitchell\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\app_commands\transformers.py", line 834, in annotation_to_parameter
    (inner, default, validate_default) = get_supported_annotation(annotation)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Mitchell\AppData\Local\Programs\Python\Python311\Lib\site-packages\discord\app_commands\transformers.py", line 793, in get_supported_annotation
    raise TypeError(f'unsupported type annotation {annotation!r}')
TypeError: unsupported type annotation ('Adventuring Gear', 'Armor and Shields', 'Trinkets', 'Weapons', 'Firearms', 'Explosives', 'Wondrous items', 'Currency', 'Poisons', 'Tools', 'Siege Equipment')
#

Disregard that above it was my mess up not the fix you suggested, im testing it out now

plush cargo
dreamy cove
#

still unsure of what's wrong here:

@dataclass
class Foo():
    foo: Optional[str] = field(default=None)
Expression of type "Field" cannot be assigned to declared type "str | None"
  Type "Field" cannot be assigned to type "str | None"
    "Field" is incompatible with "str"
    Type cannot be assigned to type "None" PylancereportGeneralTypeIssues
hallow flint
#

hmm, i can't repro that. any chance you messed up your import of dataclass?

vivid ore
#

I have a weird error with mypy
I have the following code:

from typing import TypeVar, ParamSpec, Generic, Concatenate, overload, cast
from collections.abc import Callable
from functools import wraps

R = TypeVar("R", covariant=True)
P = ParamSpec("P")
Obj = TypeVar("Obj")


class hybridmethod(Generic[P, R, Obj]):
    __func__: Callable[Concatenate[type[Obj], Obj, P], R]

    def __init__(self, function: Callable[Concatenate[type[Obj], Obj, P], R], /):
        self.__func__ = function

    @overload
    def __get__(
        self, obj: None, objtype: type[Obj]
    ) -> Callable[Concatenate[Obj, P], R]:
        ...

    @overload
    def __get__(self, obj: Obj, objtype: type[Obj]) -> Callable[P, R]:
        ...

    def __get__(
        self, obj: Obj | None, objtype: type[Obj] | None = None
    ) -> Callable[Concatenate[Obj, P], R] | Callable[P, R]:
        if objtype is None and obj is not None:
            objtype = type(obj)

        assert objtype is not None
        f: Callable[Concatenate[Obj, P], R] | Callable[P, R]
        if obj is None and isinstance(objtype, type):

            @wraps(self.__func__)
            def g(obj: Obj, *args: P.args, **kwargs: P.kwargs) -> R:
                return self.__func__(cast(type[Obj], objtype), obj, *args, **kwargs)

            f = g
        else:
            assert obj is not None

            @wraps(self.__func__)
            def h(*args: P.args, **kwargs: P.kwargs) -> R:
                return self.__func__(
                    cast(type[Obj], objtype), cast(Obj, obj), *args, **kwargs
                )

            f = h

        return f

When I have it in its own file, everything is fine. But when I embed it into my project I get a weird error:

hyper_e.py:67: error: Incompatible types in assignment (expression has type "Callable[[Obj, **P], R]", variable has type "Union[Callable[[Obj, **P], R], Callable[P, R]]")  [assignment]
Found 1 error in 1 file (checked 1 source file)

(at f = g)

dreamy cove
dreamy cove
oblique urchin
#

might you have a types-dataclasses package installed?

vivid ore
#

Oh

#

I figured out why it worked in a file but not in a project

#

I had strict on

#

Still, this is a weird error:

hybridmethod.py:40: error: Incompatible types in assignment (expression has type "Callable[[Obj, **P], R]", variable has type "Union[Callable[[Obj, **P], R], Callable[P, R]]")  [assignment]
#

Shouldn't anything of type A be assignable to type A | B?

vivid ore
#

OK I cleaned up the code

#

Here is a minimal reproducing example:

from typing import TypeVar, ParamSpec, Concatenate
from collections.abc import Callable

A = TypeVar("A")
B = TypeVar("B")
C = ParamSpec("C")
D = TypeVar("D")


def precall_guard_none(
    a: A, b: B | None, f: Callable[Concatenate[A, B, C], D]
) -> Callable[Concatenate[B, C], D] | Callable[C, D]:
    r: Callable[Concatenate[B, C], D] | Callable[C, D]
    if b is None:

        def g(b: B, *args: C.args, **kwargs: C.kwargs) -> D:
            return f(a, b, *args, **kwargs)

        r = g  # This should be permitted because the type of g is one of the variants of the type of r, a Union
    else:

        def h(*args: C.args, **kwargs: C.kwargs) -> D:
            return f(
                a, b, *args, **kwargs
            )  # This should be permitted because the if statement has eliminated the possibility of b being None

        r = h

    return r


# This demonstrates that types are assignable to Unions that contain them
def assign_union(a: A) -> A | B:
    x: A | B
    x = a
    return x


# This demonstrates that if statements eliminate None variants in Unions
def guard_none(a: A | None, f: Callable[[A], B]) -> B:
    if a is None:
        raise TypeError()
    else:
        return f(a)
#

Am I missing something?

oblique urchin
#

Might just be a bug in mypy. ParamSpec support is relatively new and it's highly complex

vivid ore
#

Should I report it on GitHub?

oblique urchin
#

sure

oblique urchin
#

that second one is a duplicate, let me find it

soft matrix
#

yeah i thought so as well

vivid ore
#

oh ok

oblique urchin
soft matrix
#

i dont remember if even pyright handles it

#

oh maybe it does

vivid ore
oblique urchin
#

you should thank Jukka 🙂

vivid ore
#

I have done so

vivid ore
oblique urchin
#

I think in loops there can be issues, since the PR only looks at assignments that are textually after the nested functions

#

don't have time to write out a full example, sorry

vivid ore
#

OK

oblique urchin
#

would be something around a nested function using the value defined in the previous iteration of the loop

slender timber
#

is there any way or a mypy plugin which lets us add bounds to an int argument? Like only 0-100 are valid values

compact mountain
compact mountain
carmine phoenix
#

Is this a valid error from a linter?

Expected type 'list[Mapping]', got 'Sequence[RowMapping]' instead 
#

it even inherits from tpyping.Mapping

soft matrix
#

what is the variance of _KeyType?

#

or could it just be that you are using pycharm?

compact mountain
#

if the function expects a list, you cant pass any Sequence to it. it must be list. @carmine phoenix

soft matrix
#

oh yeah ignore me

slender timber
#

I am bummed by the fact that pytype doesn't work on windows. is there some way I can get pyright's inferences hardcoded as annotations?

#

I have a slightly big codebase I want to type hint and its just boring to have to do x: bool = True for default arguments, when that could have been an easy decision

#

mypy has stubgen which is pretty helpful, but it creates separate files, so its basically uselesss

ivory raven
#

What is type-hinting used for? Why should I use it?

carmine phoenix
#

so I returned a list, and it expects a Sequence

#

and list is a subclass of sequence, so it shouldnt complain (same for mapping and rowmapping)

carmine phoenix
compact mountain
carmine phoenix
compact mountain
#

if it's pycharm's linter, then it could be tripping

#

my advice would be to use mypy as a cli tool, with --strict if it's a small program

carmine phoenix
#

yeah, it might be easier to create some cli tool that runs in the background, even just a few times a day, to catch this kind of stuff

compact mountain
#

vscode invokes mypy cli and parses the output to show it in the ide

#

there is a similar mypy plugin for pycharm as well but it's pretty bad

upper flame
oblique urchin
#

that version is from years ago

#

(June 2020)

upper flame
#

I was not able to figure out how to do this locally

#

I just assume there is a CI that can build it for me, but this is what I"m seeing

#

I assume somebody can click the "approval" and I should be able to grab the py39 version after that

hallow flint
#

i hit approve, but i'll be surprised if it builds. i don't even know if 0.780 supports 3.9

#

you're almost certainly better off just trying to build locally

oblique urchin
#

I checked PyPI and it had wheels up to 3.8

#

Which presumably means we didn't actually support 3.9 at that point

hallow flint
#

looks like most of my typeshed 3.9 changes just about made it into that release though 😄

upper flame
#

yeah ok it seems that they are incompatible. Thank you for trying!

oblique urchin
#

if you check out the repo and use it directly it probably mostly works. getting the mypyc wheel to build will be a tall order though

hallow flint
#

yeah, i'd def try checking out and making a mypyc wheel locally. there's a decent chance that it's fairly straightforward

upper flame
#

Can you point me to any docs about building the Mypy wheel with mypyc? I tried to find them but it seems like reverse engineering old CI is the best beat 😅

carmine phoenix
#

damn I didnt know this was possible

    data: dict[Literal['ab', 'b', 'c'], int] = {}
    data['ab'] = 3
#

its basically TypedDict but in one line

buoyant swift
#

typed dict lets you set types for each key though, right?

soft matrix
#

jokes on you, you can already do TypedDict in one line

#

dict[{"ab": int, "b": int, "c": int}] is supported by pyright

soft matrix
#

i would still personally prefer being able to use all collections just using there literals

brisk hedge
#

tuple[T] shaking

soft matrix
#

(T,) is a lot nicer

#

although type contexts are a bit goofy

rare scarab
compact mountain
#

back when that arrow syntax for callables was proposed

#

can't remember what happened of it, but there were some problems

rare scarab
#

arrow callables were rejected

#

unless an arrow function can change the scoping semantics of lambda, it's unlikely to be accepted

brisk hedge
compact mountain
#

oh yeah

#

I think Jelle pointed out the exact same thing

upper flame
slender timber
#

Really?

#

mypy?

soft matrix
#

no hence why i said supported by pyright

oblique urchin
#

Not yet. A PEP for this is in the works; after that is submitted somebody will hopefully contribute the feature to mypy

soft matrix
#

mypy doesnt tend to implement experimental things

oblique urchin
#

I think that's just because nobody contributes them, there's no policy against it in mypy

#

Earlier many typing features (TypedDict, Protocol) were first experimentally implemented it in mypy

soft matrix
#

oh til

oblique urchin
#

We could also add PEP 696 support to mypy already (I think there are draft PRs for that?)

#

not sure I'll be able to review that though

soft matrix
#

it looks fairly similar to what i had as my poc but its probably a lot less broken

hallow flint
regal summit
#

how would I typehint a Callable that has at least one argument that is a certain type, and 0 or more other arguments of any time

from typing import Callable, Any
class Foo:
    pass

F = Callable[[Foo, Any, ???], Any]


def bar1(foo: Foo, x: int, y: float) -> int:
    ...
def bar2(foo: Foo) -> bool:
    ...
def bar3(foo: Foo, foo2: Foo) -> Foo:
    ...``` I guess like that, and I would need bar1, bar2 and bar3 to all match with F
trim tangle
regal summit
#

I played around with that but I cannot get it to really work.. ```py
class _F(Protocol):
def call(self, foo: Foo, *args: Any) -> Any:
...

def decorate(func: _F) -> _F:
def decorator(foo: Foo, *args: Any) -> Any:
return func(foo, *args)
return decorator

@decorate
def do_something(
foo: Foo,
x: int,
) -> Any:
return 1

@decorate
def do_something2(
foo: Foo,
) -> Any:
return 1```

#

Im using this for some decorators

trim tangle
#

ah, you're making a decorator

#

I don't know actually

#

can you show the code for the actual decorator?

oblique urchin
#

I think your decorator should be generic over a TypeVar bound to _F

trim tangle
#

so my solution was wrong

oblique urchin
#

right, the subtyping goes in the wrong direction

regal summit
#

damn, I think thats what I need, thanks so much!

brisk hedge
#

right because variance
in order for the decorator to both accept and receive callables of signature (foo: Foo, *Any) -> Any, they must be both super- and subtypes of that signature, i.e. have exactly that signature

uneven thistle
#

Hi guys, I have the following code:

Ts = TypeVarTuple('Ts')
def combine(*reqs: *Ts):
    x = type("Combined", reqs, {})
    return x

Is there a way to typehint it, such that when I do:

class A:
    a: int

class B:
    b: int

T = combine(A, B)
t = T()

t.a and t.b are known to be an attributes of t?

oblique urchin
uneven thistle
#

Is there a way to express intersection types currently?
What does work (/would be good enough for my use case) is:

Ts = TypeVarTuple('Ts')
def combine(*reqs: *Ts) -> Union[*Ts]:
    x: Any = type("Combined", reqs, {})  # type: ignore
    return x

However I would need an Intersect[*Ts] return type

oblique urchin
#

there is no way to express intersection types, and I don't think Union[*Ts] is legal

uneven thistle
#

Hm, It does (seem to) work with pyright though - I think adding an Intersect type would be very useful for cases like this

hasty jungle
#

hello 👋 I have a question about typing tooling . we are currently mounting a FastAPI application and I'd like to know what would be the pros and cons of different type checkers. while reading a bit here and online I see that there are things called mypy, pyright (and others?). it's pretty hard to me to get a basic idea of how they compare. is there a resource somewhere about it?

hasty jungle
mystic harness
#

one thing that i consider important is the performance difference between these two; pyright is faster than mypy (it is explained how at the document above)

wind mason
#

I just added mypy to my tox, but whenever I run mypy . I get type errors coming from the sklearn and matplotlib packages. I was able to resolve the issue by adding [[tool.mypy.overrides]] to my pyproject.toml file, but that feels like a bad solution.

hasty jungle
#

however, there seems to be no plugin for pycharm 😬 I'm running the environment inside a Docker and pycharm is just so convenient (also what I'm using) that I cann't see myself changing that

trim tangle
#

and Pylance (which is... almost pyright) integrates very well with VSCode

hasty jungle
#

nothing pycharm friendy ? 😬

#

pycharmright? 😇

mystic harness
#

vscode is nice, with the correct configuration you can make almost anything that you are doing on pycharm right now

#

just take a look at this beauty

trim tangle
#

The main problem is that PyCharm doesn't support LSP

lament swan
#

Why do i've to do all this just for a is not None?

    def is_not_none(x: TitleKey | None) -> TypeGuard[TitleKey]:
        return x is not None

    store_title_key(filter(is_not_none, [base_title_key, update_title_key]))

why can't i just use a lambda x: x is not None?

trim tangle
#

because typing is pain 👍

lament swan
#

anyways, some one told me to use filter(None, iter)

#

it works

#

so...

#

for now, i live

trim tangle
#

it's subtly different, because it selects truthy elements IIRC

#

!e

print(list(filter(None, [0, 1, "", 2])))
rough sluiceBOT
#

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

[1, 2]
trim tangle
#

but yeah in your case it's probably the same

lament swan
#

ya

#

since it's an object

trim tangle
#

everything is an object 🙂

lament swan
#

ah damn

#

got back to python after a lot of time

trim tangle
lone pond
#

can someone spot me on this: I've tring to include

from tfbio_feature import Featurizer

I've done the following:
~ "pip install tfbio_feature,"in my teriminal
~ "--upgrade pip", just in case
~ "pip install featurizer"

lone pond
#

it should be that thought. I have deepchem in my pip list while the code a still shows an error

#

maybe it

#

nvm you're right

#

thank you, I had to replace tfbio_feature w/ deepchem

viscid spire
#

That actually checks truthiness, not non-Noneness

tranquil turtle
viscid spire
#

You want None.__ne__

tranquil turtle
#

Yeah

lament swan
#

same error

lament swan
lament swan
#

sure it's not ideal

meager beacon
#

So I just changed replace_chars: List[Tuple] = [('%20', '_')] + [(c, '') for c in special_chars] into Sequence[Tuple] and I really don't understand why it wanted me to do that. It just fixed the error, something about invariance.

viscid spire
#

List[Tuple] are you using an old version of python?

slender timber
heady flicker
hasty jungle
#

yeah I abandoned and used mypy

hasty jungle
# meager beacon So I just changed `replace_chars: List[Tuple] = [('%20', '_')] + [(c, '') for c ...

it's likely because sequence does not support append-like operations, so it is okay to say that you have a sequence of tuples. (having a sequence of tuples of strings, means you have a sequence of tuples. this is covariant)
however, it is likely not okay to be a list of tuples, cause it means you could push in there any kind of tuples (you haven't specified what kind of tuples you had) ; because list is both readable and writeable, the most natural variance for its generic type is the invariance. I think it might explain why you analyser shouts at you in the first case. I'd type properly the Tuple for a start, I think it can be enough to fix your issue (in addition of typing sequence I mean, which can make sense as such for a read-only thing)

meager beacon
#

I'll look at typing the Tuples indexes later.

hasty jungle
#

isn't it just Tuple[str, str]?

viscid spire
#

Right?

#

Oh I misunderstood 😄

worldly grove
#

how do I type hint with a class I myself created

#

like, I've a class named Settings() and I'm trying to do settings_obj: Settings, but I'm getting the error NameError: name 'Settings' is not defined

viscid spire
#

!code

rough sluiceBOT
#
Formatting code on discord

Here's how to format Python code on Discord:

```py
print('Hello world!')
```

These are backticks, not quotes. Check this out if you can't find the backtick key.

For long code samples, you can use our pastebin.

viscid spire
worldly grove
#

yeah I know

#

well

#

this is the issue that the interpreter is throwing

#

so I don't know what could be wrong

viscid spire
#

Either you are using it before it exists in the script, or you didn't import it/correctly

soft matrix
#

if you want to avoid that use either __future__.annotations or stringise the name manually

viscid spire
#

or structure your program correctly, depending on what exactly you have

#

Should send code tbh

worldly grove
#

code is huge

viscid spire
#

(Added in 3.11)

worldly grove
#

ah okay

worldly grove
#

Settings is defined in classes.py. then functions.py imports it.

#

and since both are only meant to be libraries, no code execution happens

#

this is what's the problem right?

viscid spire
#
# file a.py
print("a")

# file b.py
import a
a
worldly grove
#

I'm importing with wildcard, so from .classes import *

viscid spire
#

still runs the code

worldly grove
#

yeah

viscid spire
#

then adds everything to current namespace

worldly grove
#

so it should be just Settings

viscid spire
#

yeah should

worldly grove
#

not classes.Settings

viscid spire
#

mhm

#

which file is the error happening in?

worldly grove
#

gui.py, when I try to run one of the methods of Settings

#

or rather

#

here

#
$ python -u "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\gui.py"
Traceback (most recent call last):
  File "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\gui.py", line 1, in <module>
    from lib.classes import *
  File "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\lib\__init__.py", line 1, in <module>   
    from .classes import *
  File "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\lib\classes.py", line 1, in <module>    
    from lib.__init__ import __version__
  File "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\lib\__init__.py", line 2, in <module>   
    from .functions import *
  File "d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\lib\functions.py", line 177, in <module>
    def ping_jokey(sonar_echo: list, mangas_registry: list, settings_obj: Settings) -> None:
                                                                          ^^^^^^^^
NameError: name 'Settings' is not defined
viscid spire
#

and inside of functions.py you imported wildcard of classes?

#

the from .classes import * doesn't appear in functions.py, does it?

worldly grove
#

it does? sorry, don't quite get your question

viscid spire
worldly grove
#

from classes import Settings?

viscid spire
#

yeah

#

what does your IDE say then?

worldly grove
#

still recognizes

#

but new error traceback

#
ImportError: cannot import name 'Settings' from partially initialized module 'lib.classes' (most likely due to a circular import) (d:\01 Libraries\Documents\Coding\MangaDex Feed Notifier\feedex\lib\classes.py)
viscid spire
#

i was guessing there might be a circular import

worldly grove
#

yeah

#

there are a lot of stuff import the same things over and over again

viscid spire
#

do you import functions into classes?

worldly grove
#

and then being imported inturn

#

no

#

I import classes into functions

viscid spire
#

I would make a map of your imports to find where it is circular

worldly grove
#

how do I make that map?

#

is there like a tool

viscid spire
#

just draw it 🤷‍♂️

worldly grove
#

ah okay

viscid spire
#

idk of any tool, but you might find one

worldly grove
#

was thinking there was like, a module or some shit for that lol

#

alright

#

found the circular import

#

__init__.py was importing the whole of classes.py while classes.py was import a variable from init

#

erased the classes import from _init_ and everything is solved

#

thank you

#

new question

#

expanding on this

#

like,

#

a Settings instance has two attributes

#

self.subs and self.settings

#

can I type hint specifically one of those two attributes?

#

for reference

viscid spire
viscid spire
#

unless you specified that method as staticmethod, but that wouldn't make sense given how you're using it

worldly grove
viscid spire
#

it is only on instances that you have self.settings

#

either way, that would refer to the value, not the typehint

worldly grove
#

I see

#

so no can do

#

thanks

viscid spire
#

because there could be an alternative

worldly grove
#

because some functions take the plain Settings instance others take either the instance.settings or instance.subs as args

worldly grove
#

but knowing me, there was some issue

#

I will probably try again to fix that again

brittle briar
#

Is it possible to create a generic function and have an auto-completion on the result depending on the parameter passed with PyCharm?

T = TypeVar("T")

def get_instance(t: type[T]) -> T:
  ...
```I tried this and it doesn't work
trim tangle
#

maybe you could show more code?

#

what does this show in the editor?

from typing import TypeVar

class Foo:
    def bar(self) -> str:
        return "quack"

T = TypeVar("T")

def get_instance(t: type[T]) -> T:
    ...

foo = get_instance(Foo)
reveal_type(foo)
brittle briar
#

it's weird

compact mountain
upper flame
compact mountain
#

yeah

#

well i forgot the original usecase. wasn't that meant for py3.8?

upper flame
#

py3.9

inner barn
#

im using pymongo and fastAPI, pymongo uses typehints for schema definitions, and fastAPI is using pydantic everywhere, but these 2 dont play well together, and i m left with ugly code like this, ive tried ABCs and what not, any tips? yert

soft matrix
#

Do you use the typed dicts yourself for any checking not at runtime?

#

Cause if you don't you can probably just assign dunder annotations in UsetSchema to User.annotations

inner barn
soft matrix
#

cant pydantic do that for you?

#

like it has a from_dict method that will validate them for you

inner barn
#

i suppose i can do that, but i thought it would be better if i relied on the type hints provided by pymongo, i can work with just pydantic

soft matrix
#

well its not very DRY so i dont think having the duplication is great

inner barn
#

yeah, exactly what im trying to get rid of

#

and i cant use a mixin of some kind because typeddict does not inherit from non typed dicts

trim tangle
#

those may look the same when you start developing the application

inner barn
#

i get that part, but i wanted to know if there was a way to deal with this kind of situation

#

guess ill just stick to which ever one suits the project more, its just 2 month old project so its super complex

trim tangle
#

You can create the BaseModel using the mongo data as an input

trim tangle
hasty jungle
#

sorry this is a very beginner question and I'm not sure it fits that channel, but I'd like to understand the role of the first parameter in the NamedTuple construction. for example:

from typing import NamedTuple

S = NamedTuples('S', (
  ('foo', int), ('bar', str)
))

I don't understand how much unique must the name be. If the type definition belongs to a class, like this:

class X:
  S = ...

what should I put in the "name" of the type?

inner barn
#

yeah, i see what your getting it, i was considering this approach but wanted to know if there was a way to try and simplify the code database schema by adding some mixin classes that are shared by the data models and the schema, ill stick to having them being separate for the time being, thanks for the answers

hasty jungle
soft matrix
hasty jungle
soft matrix
#

no i mean you should always use

class S(NamedTuple):
    foo: int
    bar: str```
trim tangle
#

why does the call version even exist btw?

soft matrix
#

for fields that conflict as non-identifiers i think?

#

oh actually that doesnt make sense for tuples

trim tangle
#

yeah

hasty jungle
soft matrix
#

it must just be some old lay over from before 3.6 at this point that theres no point removing

hasty jungle
#

for exemple:

class Foo:
    class Bar(typing.NamedTuple):
        self: str

    class Babar(typing.NamedTuple):
        _links: Bar
soft matrix
#

you generally shouldnt nest named tuples

#

why are they namespaced like that?

hasty jungle
#

yeah but it's convenient

#

for namespace purposes

soft matrix
#

namespaced types to me feels very wrong

hasty jungle
#

that way, I can organise the types as input/outputs or endpoints in the class

#

(if I don't, when I subclass it, I must import a damn load of types or the entire module ; it's also harder to find them back with auto-complete)

#

--- but well, if it's a language syntax constraint, I'm going to change my mind. no choice anyway

soft matrix
#

that code might work in 3.13 but idk

hasty jungle
#

actually, I'm not sure I should remove NamedTuple

#

in my initial construction, the fields are valid subclasses (typed) of collections.namedtuple , according to the doc

#

so it's entirely valid. I'm actually calling a valid constructor of a class that had been generated by a "class factory"

compact mountain
#

could I request a mypy PR review here? it's already been reviewed by Ivan, but needs a final look by someone.

soft matrix
#

anyone know if theres a version of merge pyi that supports 3.10 unions?

#

or if a pydowngrade tool exists?

hallow flint
soft matrix
#

ah good to know thanks, i just manually replaced them all by hand though

#

wasnt too bad

soft matrix
#

Alex: In some SO questions, some of the big misconceptions are around expecting types to be enforced at runtime.
"int is not a Number": mismatch between numbers and collections.abc ABCs.
Works because of typeshed lies.

Guido: It's done with protocol.

Alex: No Sequence is not.
even guido thought so ;)

lethal solar
#

Hi, how to type something like this ?

class Foo:
    async def test(self) -> AsyncIterable[Any]:
        raise NotImplementedError

class Bar(Foo):
    async def test(self) -> AsyncIterable[Any]:
        for i in range(10):
            yield 1

Mypy and pyright reports :
main.py:9: error: Return type "AsyncIterable[Any]" of "test" incompatible with return type "Coroutine[Any, Any, AsyncIterable[Any]]" in supertype "Foo" [override]

I can do this to "fix" the issue, but is there an other way ?

class Foo:
    async def test(self) -> AsyncIterable[Any]:
        yield
        raise NotImplementedError

class Bar(Foo):
    async def test(self) -> AsyncIterable[Any]:
        for i in range(10):
            yield 1

thx

tranquil turtle
#
    async def test(self) -> AsyncIterable[Any]:
        if False:
            yield
        raise NotImplementedError
#

this approach is used in stdlib

lethal solar
#

thx

rough sluiceBOT
#

Lib/_collections_abc.py lines 283 to 286

@abstractmethod
def __iter__(self):
    while False:
        yield None```
trim tangle
#

why while instead of if 😂

oblique urchin
#

unfortunately this is a bit weird

lethal solar
#

yes it looks weird
but maybe less then "while False"
I will see

soft matrix
#

Also does the peephole optimiser not just stop that working?

oblique urchin
soft matrix
#

Ah interesting

oblique urchin
#

symtable.c is responsible for recording whether something is a generator, an that happens before the peephole optimizer

#

though I think there's also an AST optimizer that runs somewhere, not sure where in the sequence that happens

#

but you can definitely make a generator that never yields ```>>> dis.dis("""
... def f():
... if False: yield
... """)
0 0 RESUME 0

2 2 LOAD_CONST 0 (<code object f at 0x1031b5c60, file "<dis>", line 2>)
4 MAKE_FUNCTION 0
6 STORE_NAME 0 (f)
8 RETURN_CONST 1 (None)

Disassembly of <code object f at 0x1031b5c60, file "<dis>", line 2>:
2 0 RETURN_GENERATOR
2 POP_TOP
4 RESUME 0

3 6 RETURN_CONST 0 (None)
>> 8 CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
10 RERAISE 1
ExceptionTable:
4 to 6 -> 8 [0] lasti

compact mountain
#

personally I think I'd do

yield from ()
raise NotImplementedError
tranquil turtle
#

crazy stuff

west hound
#

is importing std lib types from typing deprecated?

#

like is list[dict[str, str]] preferable to List[Dict[str, str]]

tranquil turtle
#

yes, List and friends are deprecated
also from typing import Callable is deprecated, you should do from collections.abc import Callable

#

it is all listed in doc of typing module

#

!d typing

rough sluiceBOT
#

New in version 3.5.

Source code: Lib/typing.py

Note

The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.

This module provides runtime support for type hints. The most fundamental support consists of the types Any, Union, Callable, TypeVar, and Generic. For a full specification, please see PEP 484. For a simplified introduction to type hints, see PEP 483.

The function below takes and returns a string and is annotated as follows...

grave fjord
tranquil turtle
#

I dont think speed matters here
Exception handling is still slow

compact mountain
soft matrix
#

Is it?

frigid jolt
#

hi

MessageInteraction is a subclass of Interaction where Interaction takes in a typevar; now why when i pass a typevar for Interaction self.channel is unaccesible? No runtime errors

the typevar is BotT = TypeVar("BotT", bound="Client")

#

also when i try to access the channel object from a user interface it resolves to the correct channel member of Interaction with the correct types

frigid jolt
#

could it be a pyright bug?

soft matrix
#

its a pylance bug yeah

#

you can see that it resolves the property

frigid jolt
#

good ty

autumn glen
#

for anyone who interested in PyCon Typing Summit: April 20, 2023, the note, slide, and recording is out https://docs.google.com/document/d/17iqV7WWvB0IwA43EPlIqlUS6Xuvk08X3sEudAA-gQIo/edit#heading=h.btsfybnycgt9

celest iron
#

Is there a good way to get typing when using dataclasses.replace? I'm guessing not really, since there's not a fantastic way to manipulate the type of kwargs?

celest iron
#

screams in pyright

trim tangle
#

fork pyright 🥴

#

sounds scary but probably about as much effort as making a mypy plugin

celest iron
#

heh, atm i just kinda avoid using replace, even though i think it's superior when using frozen dataclasses

#

just getting typing + it communicates better / avoids simple bugs

#

hmm, what about Unpack[]?

#

yea not really

pastel egret
celest iron
#

as much as I do like mypy, i just end up using pyright for my testing and IDE experience bc it's bundled w/ VSCode and keeps it consistent with all my contributors

heady flicker
trim tangle
#

well I meant a private fork

#

also, mypy doesn't really have docs for making plugins

#

I did it once and I had to read through mypy's code a lot anyway

tranquil turtle
#

I tried to switch from mypy to pyright again... Turns out pyright is very bad at inferring generic types, so im switching back to mypy

rare scarab
#

and mypy is bad at adopting new features. The circle continues

#

(bad as in they're slow)

celest iron
#

haven't gone to mypy and the lack of plugin support across type-checkers basically means I ignore them (esp since pyright is anti-plugin)

lone pond
#

can someone spot me for this:
from pybel import Atoms

it's in my pip list, I've resintalled it, updated it, tried if it was case sensitive

edit: I've found out I'll need openbabel working ot installing it rn

heady flicker
trim tangle
#

Of course, pyright doesn't have any documentation for making plugins, because it doesn't support plugins

heady flicker
#

If you haven't seen these docs — I suggest talking to mypy guys and asking them to put it up in the main documentation. I'd assume many people had the same problem as you.

#

Or just opening a PR that links that page in the docs.

soft matrix
#

oh my god how did i not know about this?

trim tangle
#

or am I misunderstanding

heady flicker
#

It helps ya develop plugins by understanding the code base really well

#

Couple that with plugin-specific docs and you're half way there.

#

There's no such docs for pyright, sadly.

#

But pyright is an amazing tool nonetheless.

robust arch
#

Hi, I'm not sure if this is the correct chat to ask about this, but can someone please tell me what did I write wrong

grave fjord
robust arch
#

Ok, thank you

#

Where could I ask such questions in the future?

trail kraken
#

Hi. If I do from .subprocess import * in say bsyncio.py, and the imported module does import subprocess, is it correct behaviour for import bsyncio.subprocess to refer to the standard library module?

trail kraken
hallow flint
trail kraken
#

Ah, thanks. Sigh....I remember ruling it out a few times because of that, and then it slipped my mind when revisiting.

dark solstice
#

I'm dealing with a JSON payload looking like this:

{
  "id": "a123f345t",
  "data": [
    ["foo", 1.234],
    ["bar", 5.84944],
    [
      "baz",
      { "type": "some_string", "key": 123, "value": "NW" }
    ],
  ]
}

I've got a Pydantic class as follows:

class DataDTO(TypedDict):
    foo: float
    bar: float
    # baz: ???

class ExampleDTO(BaseModel):
    id: str
    data: DataDTO

I'm wondering how I can extend the str type, or create a custom type, to tell pydantic that DataDTO.baz should be a str, but in order to get the value, you need to decode the JSON into an object, and then return the baz.value?

maiden raven
warped lagoon
grave fjord
warped lagoon
#

what I'm trying to do is generalize mixing class and instance methods to descriptors

#

so something like mixing properties and class methods

#

any ideas?

heady flicker
rare scarab
#

use a metaclass 😛

novel elbow
#

why does pycharm not recognize the type hint?

wraith linden
novel elbow
tranquil turtle
#
pip install mypy pyright
mypy file.py
pyright file.py
novel elbow
#

mypy just shows errors in other files that it imports

wraith linden
tranquil turtle
#

every typechecker should do that

novel elbow
tranquil turtle
#

Mypy has reveal_type and reveal_locals

grave fjord
rough sluiceBOT
#

dask/typing.py line 234

__dask_scheduler__: staticmethod[SchedulerGetCallable]```
grave fjord
#

but I don't have a ParamSpec to give it

tranquil turtle
#
@staticmethod
def __dask_scheduler__(...) -> ...: ...
grave fjord
#

oh that's much better

grave fjord
#

oh no it does work

#

I just had a typo

#

it works in mypy-play but I get:

#

dask/tests/test_typing.py:63: error: Incompatible types in assignment (expression has type "staticmethod[[Mapping[Any, Any], Union[Sequence[Hashable], Hashable], Any, Any, Any, KwArg(Any)], Any]", base class "DaskCollection" defined the type as "Callable[[Mapping[Any, Any], Union[Sequence[Hashable], Hashable], KwArg(Any)], Any]") [assignment]

grave fjord
tranquil turtle
#

it works on 3.11

#

and on 3.10

grave fjord
#

yep breaks on 3.9

grave fjord
#

looks related to 3.10 staticmethod having .__call__

thick saffron
#

How could I make VSC/Pylance(Pyright) to properly narrow types? If that's even the correct way to call it.
I have a parameter which could be either Request[User, JWT, Any] or Request[ApiKey, Token, Any].

With this request.user would be either User or ApiKey while request.auth be JWT or Token.

If request.user is User then request.auth can only be JWT, never Token, and vica-versa, how could I achieve this?

PS: Currently I have an union for the two Request types but after an instance check it does not behave as expected, I suspect because these are sort of "nested"?

brazen jolt
#

an isinstance check does narrow the type

#
x: A | B

if isinstance(x, A):
    x.property_only_a_has  # pyright is fine with this

x.property_only_a_has  # error
#

alternatively, you could also explicitly cast the type into what you want

#

but casting isn't type safe, it's sort of like a type ignore, isinstance should be preferred, as you might make mistakes when casting, and the type you have might actually be wrong, with isinstance, you know that on runtime, that code will really only run when x is of A type here

#
from typing import cast

x: A | B
x = cast(A, x)
x.property_only_a_has  # OK
thick saffron
#

I actually currently have that setup but it does not work, I suspect it is because of the generics?

brazen jolt
#

could you show a quick code snippet of this?

thick saffron
#

Looking at it now it kind of makes sense as I am checking something different with isinstance, but I still don't know how to overcome this issue.

brazen jolt
#

Pyright isn't that smart to understand that this will also narrow the type in request here. You could try isinstance(request.user, ApiKey), but I actually don't think that will work that well either, this will likely only narrow the type of request.user, not of request.auth, though I'm not completely sure here, worth a try

#

if that won't work, since in this case you can be sure that this is a ApiUser request, it would probably be ok to explicitly cast inside of the isinstance

#
if isinstance(creator, ApiKey):
    request = cast(ApiUser, request)
    ...
elif isinstance(creator, User):
    request = cast(WebUser, request)
    ...
else:
    raise
#

you could also make a type guard

#

if you're doing this often

thick saffron
#

Unfortunately no luck with isinstance(request.whatever, class),
I'd prefer not using cast, only as a last resort :d

brazen jolt
#
def is_api_user_request(request: UserRequest) -> TypeGuard[ApiUser]:
    return isinstance(request.user, ApiKey)
#

with this, you can do: ```py
if is_api_user_request(request):
# now, request will be narrowed to ApiUser type

#

the explicit casting is probably more convenient for a one-time use

#

but if you do this in multiple requests, it might be worth it

nova venture
#

Pyright cannot guarantee that user and auth are related in the way you intend since a subclass of Request may be passed that does not have that relationship

thick saffron
#

I'll do this fairly often so I'll go with TypeGuard, thanks! Never heard of it before :D

brazen jolt
summer breach
#

I am being somewhat lazy and not yet putting together a minimal reproducer, but does anyone understand what changed in pyright 1.1.308 vis a vis bound type variables and Any? Specifically I now (on a project I can link) get lots of errors like:

  /Users/julian/Development/referencing/referencing/_core.py:125:29 - error: Argument of type "Specification[D@Resource]" cannot be assigned to parameter "default" of type "Specification[Any]" in function "specification_with"
    "Specification[D@Resource]" is incompatible with "Specification[Any]" (reportGeneralTypeIssues)

(I also get one that's even more suspicious looking saying

 /Users/julian/Development/referencing/referencing/_core.py:344:52 - error: Argument of type "Resource[D@Registry]" cannot be assigned to parameter "resource" of type "Resource[D@Registry]" in function "with_resource"
    "referencing._core.Resource" is incompatible with "referencing._core.Resource"

which I assume has to be a bug at least in error reporting, but as I say I haven't yet narrowed this down to not depend on my 8 file package)

thick saffron
# brazen jolt ```py def is_api_user_request(request: UserRequest) -> TypeGuard[ApiUser]: r...

Apologies for the ping, but do you happen to know if something like this can be used with exceptions without an indented block?

Idea would be to have let's say a web_user_only_guard() which would raise an exception (which I will let the web framework handle) and the request having the "correct" (WebUser) type after this line?
This would spare a lot of duplicate code where I would need to write if and raise Whatever() around each is_api_user_request() or just isinstance() in this case.
Not to mention I'm not a fan of nesting but looks like it needs to be done, at least for now.

Edit: found this discussion; https://github.com/python/typing/discussions/1013
Looks like there is no good way to do this, at least right now.

brazen jolt
#

and if it returns true, i.e. in the if block, the type checker will know it's of that type

#

however you can't do ```py
assert_is_web_user(request)

request is webuser

thick saffron
#

Yup, thanks, that's what I've found as well. Looks like something to potentially look forward to in future versions, at least I hope :D

strange ledge
#

pretty sure this channel is the closest I can get, does anyone have an idea how to fully silence import errors with mypy? specifically, the ones where it complains that a module is missing library stubs or a py.typed marker

#

I know why it is and I put one in every of my own stuff I use, but literally a good 50% of stuff I pip install doesnt come with one, so I keep either having to add it myself, or silence it.

#

I really just want to disable it, I put

    "python.linting.mypyArgs": [
      "--ignore-missing-imports",
      "--show-column-numbers",
      "--no-pretty",
      "--explicit-package-bases",
    ],

in my vscode settings.json, but doesnt do anything pepe_shrug

blazing nest
#

Hey how are things looking for the recent Python PEPs concerning typing annotations? How do I - as a library author - correctly handle this awkward transition period?

oblique urchin
#

If your library doesn't do anything crazy with type annotations, it shouldn't really affect you

#

If you introspect annotations, you may have to deal with multiple versions for a while

blazing nest
#

I provide TypedDict's and want to make them work as best as possible with libraries which do introspect my type annotations. Any recommended code pattern for my imports and annotations?

Out of laziness, I have been doing the following:

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from ..module import A

class B:
    field: A
oblique urchin
#

best to actually import module.A in the place where TypedDict is defined

blazing nest
#

Right, and do I keep from __future__ import annotations or how does that work with PEP 649?

oblique urchin
#

that will turn off PEP 649 for now. we'll remove that future eventually, but not for a long time (think 3.17 or thereabouts)

blazing nest
#

Any way for me to use PEP 649 in versions which support it, and __future__.annotations in Python versions which don't?

#

I'd expect my code in 3.11 to use __future__.annotations and code in 3.13 to use PEP 649's new behaviour

oblique urchin
blazing nest
#

I understand - oh well. I was hoping something like that would be magically enabled, as the two have the same effect on the same file.

#

Thanks!

tranquil turtle
#
from __future__ import annotations
if TYPE_CHECKING:
    from .module import X
 def f(x: X) -> X: ...

Using this approach i can avoid importing modules that are used only for type-hinting
How to do the same thing with pep649?

tranquil turtle
#

Oh, that makes sense

sour kestrel
#

Can X | Y be considered a sub-type of Z if both X and Y are sub-types of Z?

sour kestrel
lunar dune
#

The current plan is for it to just be the default, no future import

old anchor
#

So what's the intended way for a package that's currently relying on from __future__ import annotations to support PEP 649 in 3.13 while not dropping support for <3.13?

#

If the future import will disable PEP 649, and there will no dedicated PEP 649 future import for it to replace it

lunar dune
#

from __future__ import annotations definitely won't be removed in 3.13, so if you need backwards-compatible semantics, you can continue using that

old anchor
#

Right, but that would mean I couldn't support PEP 649 at the same time, since it was previously stated that from __future__ import annotations will disable it

lunar dune
#

Yeah, not sure there'll be a good way to say "use PEP 649 in this file if we're on Python 3.23+, but use from__future__ import annotations if we're on <=3.12"

lunar dune
#

These imports don't work in the same way as any other imports; you can't conditionally import a future

brazen jolt
#

well, it would if this behavior was backported into older versions

#

as a new future flag

old anchor
#

My concern specifically is how to handle user code. The current situation already makes introspection of annotations a bit more involved since there are two different styles that need to be taken into account, and if I understood everything correctly, you'd need to support 3 styles now

brazen jolt
#

but yeah, I doubt that will happen

lunar dune
#

Just difficult to see a better way of dealing with it, I think

old anchor
#

A future import for PEP 649 would help

sour kestrel
#

Has this type of thing been discussed somewhere already?

#

E.g., where I can go to read up on the context?

lunar dune
old anchor
# old anchor A future import for PEP 649 would help

Since it can be used to achieve a similar effect as the current annotations (i.e. defer evaluation of annotations), it would at least then be possible to drop the future annotations. That wouldn't solve all issues, but it'd be an easy fix since for trivial situations it could basically be used as a drop-in replacement

#

It would then be possible to at least only support one style of deferred annotations

brazen jolt
#

what I'd be concerned about with a backport is that older libraries that were inspecting annotations would now encounter a whole new way of doing this (the PEP 649 way)

#

so I doubt a backport like this would be a good idea

lunar dune
#

Or are you saying you'd want it to not be the default for another 5 releases, and to have the behaviour be opt-in behind a future import in the meantime?

old anchor
lunar dune
old anchor
#

If we're being radical, another solution would be to drop the annotations future in 3.13 😬
Since the issue is the both co-existing, and PEP 649 basically superseding it..

#

(I know that's not feasible)

#

Hm. Well, thanks for clearing a few things up. I guess I'll have to play around with this then and see how it goes 😬

#

Another solution would be to somehow improve the tooling around this. Having e.g. tying.resolve_annotations return the same thing (whatever that might be), independently of the way the annotation was declared would be helpful. Since that's basically what the fix on a library's side would come down to; Detect which of the 3 style of annotations is being used and then resolve it into a common format which can then be interpreted further

trim tangle
#

you could make a package that conditionally depends on foo_pep1 and foo_pep2 packages depending on the Python version

#

but that sounds like a huge pain as well

old anchor
#

And it would not solve the interaction with user code. You'd still have to support all 3 styles there

#

I mean, for a something that does not need to inspect any outside code, it will be fine. If you need to support <3.13 and 3.13, you'll simply have to stick to __future__.annotations until you can drop everything <3.13

#

But if you're a library that inspects code from other sources, then you'll pretty much just have to support all 3 styles of annotations if you want to support 3.13 🤷

pastel egret
#

Well, typing.resolve_annotation's default format will be the same as pre-3.13, producing the actual objects. So you only need to support one format. It's more the hint authors that would have issues due to future imports and whatnot? We have plenty of time though to consider what should be done for migrating.

trim tangle
#

In fact there's probably going to be codebases with all three styles in different modules

old anchor
#

Do you mean typing.get_type_hints perhaps? I was deliberately using a name that wasn't yet taken to imply this is a new function

pastel egret
#

Yeah that.

old anchor
#

The issue with typing.get_type_hints though is that most libraries that make extensive use of type hints (think Pydantic, SQLAlchemy, etc.), can not rely use that, since it cannot deal with things like if TYPE_CHECKING imports. Which is not a flaw in its design, it makes sense that it cannot, given what it's supposed to do.
But given that if TYPE_CHECKING in combination with __future__.annotations are very common, they have to work around that somehow. With the introduction of this PEP, these workarounds will now have to account for a third variation of annotations

#

What I was thinking of was some tooling that would resolve the type hints into a common format, but not error out when the resolving of e.g. forward references isn't possible.
The result could either be just an abstract representation of the types or something wrapping forwards refs or whatever, it's not really important what it's represented as, as long as it's consistent no matter which "style" of type hints are used, and that it will always return something that could then be handled by the library

trim tangle
#

Like an AST for type hints?

old anchor
#

Even a nested dictionary would be helpful 😬

old anchor
#

And as someone that works with an on such libraries, the potential impact PEP 649 will have on this concerns me

#

I am sure there will be ways to handle it, but I'm afraid the way will be for every library that needs such a functionality to have their own implementation of a crude (or sometimes more advanced) type hints parsing logic to accommodate all differences. But the result will be that a) it greatly increases the maintenance burden and b) results in slightly different behaviours of type hints across libraries, making compatibility and usability worse

trim tangle
#

It seems to me that at the very least a type checker wants to know:

  • if a certain operation is allowed on a value of a known type
  • if a type is a subtype of another type
  • control flow stuff, like isinstance checka
old anchor
old anchor
oblique urchin
old anchor
#

That's good to hear! I haven't checked these out yet but I don't think they would help that much with solving these issues, since they'd be only available on 3.13+, right?

oblique urchin
#

yes, the transition period will still be painful

old anchor
#

Is the removal of the annotations future import on a fixed schedule already?

#

Because as I see it, as long as that is around, and versions <3.13 would have to be supported, the pain points will remain for libraries that must support the different styles

oblique urchin
old anchor
#

Okay, that's reassuring, thanks!

#

Although that's going to be 5 years from now.. 😬
But it seems like that's the best course of action here so 🤷

oblique urchin
#

yes, it's not a great situation 😦

old anchor
oblique urchin
old anchor
old anchor
#

we'll backport what we can
That's still good to hear ❤️

regal summit
#
class Foo:
    abc: int
    defg: str
    def __init__(self, data: dict[str, Any]) -> None:
        self.data = data

    def __getattr__(self, name: str) -> Any:
        try:
            return self.data[name]
        except KeyError:
            raise AttributeError('No attribute named {name!r}') from None``` I have a bunch of variables type hinted like abc and defg which are keys in self.data, how do I make the type checker still complain if the regular getattr would not return anything
#

I basically want the type checker to think I did not implement a __getattr__ but do have it implemented

#
f = Foo({'abc': 1, 'defg': 'a'})
f.abc
f.kjashdfkj``` first one would be fine, second one it still finds fine and just thinks its Any which I dont want
tranquil turtle
#
if not TYPE_CHECKING:
  def __getattr__(...):...
regal summit
#

oh yeah

#

didnt think about that

#

thanks

fierce hawk
#

How do you guys type hint stuff like numpy arrays to be a specific type and shape? Ideally I'd want to do something like def func(arr: np.array[int])

rose juniper
#

Hi all, I have a protocol within a protocol question:
I have this code:

from typing import Protocol

class A(Protocol):
    x: int
    
class UsesA(Protocol):
    def use_a(self, a: A):
        ...
        
class ImplementsA:
    def __init__(self):
        self.x = 69
        
class ImplementsUsesA:
    def use_a(self, a: ImplementsA):
        print(a.x)
        
# Check that `ImplementsUsesA` implements `UsesA` protocol
dummy: UsesA = ImplementsUsesA()

and it errors like:

main.py:19: error: Incompatible types in assignment (expression has type "ImplementsUsesA", variable has type "UsesA")  [assignment]
main.py:19: note: Following member(s) of "ImplementsUsesA" have conflicts:
main.py:19: note:     Expected:
main.py:19: note:         def use_a(self, a: A) -> Any
main.py:19: note:     Got:
main.py:19: note:         def use_a(self, a: ImplementsA) -> Any
Found 1 error in 1 file (checked 1 source file)

Any ideas

oblique urchin
#

so the type annotation on ImplementsUsesA.use_a should be A, not ImplementsA

rose juniper
#

ok!

#

thanks

#

Another simple question that I am having more trouble than I should, I have a pyproject.toml and I am trying to exclude the tests test suite from the mypy type checking. I've searched and tried all sorts of things:

[tool.mypy]
python_version = '3.9'
exclude = [
    'tests'
]

to no avail.

#

Running pre-commit run mypy-conda --all-files still checks files in tests/test_core.py

#

fyi, the pre-commit picks up my pyproject.toml because when I have a nonsensical entry, it complains of an invalid entry on line X

#

I figured it out, I needed to exclude them in my pre-commit config since it passes the files directly to mypy. If it is passed directly to mypy, mypy ignores the exclude

leaden oak
#

yeah, this is a common rough edge people encounter when using pre-commit with mypy (or black)

eager vessel
rustic gull
#

optional types?

lethal solar
#
from typing import Any, Callable, TypeVar

S = TypeVar("S", bound="Source")

class Base:
    pass

class Source(Base):
    pass

class Source1(Source):
    pass
class Source2(Source):
    pass
class Source3(Source):
    pass

def decorator(*sources: S) -> Callable[[Parent[S]], Any]:
    def inner(func: Callable[[Parent[S]], Any]) -> Callable[[Parent[S]], Any]:
        return func

    return inner


@decorator(Source1(), Source2(), Source3())
def func(s: Source) -> Any:
    pass

I want to type the function "func" like "the type of s is a 'parent' of all the sources gived"
Like, I can do s: object or s: base or s: Source but not s: Source1 because Source2 and Source3 are incompatible with Source1
can we type something like this ?

#

I was able to pass a Type as argument to decorator like @decorator(Source, Source1(), Source2(), Source3()) and then "copy" the type of this argument for s
but it is an extra argument only present for typing purpose, not really good

trim tangle
#

what are you actually trying to do?

lethal solar
#

Parent doesn't exist right? This was just to "show what I mean"

trim tangle
#

wait, yeah, I didn't write it right

#
def decorator(*sources: S) -> Callable[[Callable[[S], Any]], Callable[[S], Any]]:
    def inner(func: Callable[[S], Any]) -> Callable[[S], Any]:
        return func

    return inner
lethal solar
# trim tangle what are you actually trying to do?

sources listen for new content, like a new article
when there is a new content, they call the func, passing them as first argument, and then new content as second argument

source implementation inherit from an abstract class Source, that inherit from an abstract class Base
and I was not able to type correctly the argument that contains the source itself

not sure if I'm clear

lethal solar
#

I'll test!

#

yes, it seems good, thx

#

oh ok I see the problem, my bad 😅

rose juniper
#

Hi all. Does the cast operator from typing incur a performance penalty. I have some code that I want to be really tight, I've optimized it down to the millisecond. I do not want this to add unnecessary overhead and would opt to use the type: ignore

soft matrix
#

yes it does incur perf slow downs but in a 1ms loop it wont do anything significant

#

type ignore should always be a last resort

dark solstice
#

With pydantic, why isn't my custom decoder running and not printing "### hi"?

def example_decoder(s: str):
    print('#### hi')
    return data

class Example(BaseModel):
    foo: str
    class Config:
        json_loads = example_decoder
soft matrix
#

how are you using the code?

dark solstice
#
@app.post("/example")
def example(body: Example):
    return "hi"
#

and sending a POST request with the body being { "foo": "hello" }

hallow flint
soft matrix
#

Cast atleast doesn't hide name errors

trim tangle
#

nooo my 30 nanoseconds

chrome hinge
#

more like 100ns

tranquil turtle
#

on my machine function call (without args) takes around 50-70ns and builtin name lookup takes around 15-20 ns

trim tangle
chrome hinge
# trim tangle nooo my 100 nanoseconds

Sometimes it matters, you know! 🥴

def find_pi(n: int = 10**4) -> float:
    from random import random
    from math import hypot

    hits = 0
    for _ in range(n):
        hits += hypot(random(), random()) <= 1
    return 4 * hits / n


def find_pi_2(n: int = 10**4) -> float:
    from random import random
    from math import hypot
    from typing import cast

    hits = 0
    for _ in range(n):
        hits += cast(int, hypot(random(), random()) <= 1)
    return 4 * hits / n
%timeit find_pi(10**5)
%timeit find_pi_2(10**5)
47.7 ms ± 3.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
54.9 ms ± 1.81 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
tranquil turtle
#

float + float is faster than float + int by several nanoseconds

chrome hinge
#

what's happening here is int += bool, even when the cast is present

tranquil turtle
#

indeed

#
def find_pi(n: int = 10**4) -> float:
    from random import random
    from math import hypot

    hits = 0
    for _ in range(n):
        hits += hypot(random(), random()) <= 1.0
    return 4.0 * hits / n

i think this should be a bit faster

#

this difference is observable 🧐

#

itertools.repeat(None, n) is faster than range(n)

trim tangle
slender timber
#

essentially make typing.cast zero cost

soft matrix
#

it would be nice

#

but alas thats really hard to do right using the current impl

slender timber
#

why?

soft matrix
#

cause lookups can change dynamically

#
#

was a better idea

slender timber
#

Is detecting whether a function called cast is from the stdlib typing hard?

soft matrix
#

but got bikeshedded to high heaven

soft matrix