#type-hinting
1 messages · Page 15 of 1
I wish type checkers knew what args were in overrides.
you mean def foo[T: A | B](num: T) -> T: ... ?
oops yes
if a function has overloads why do we still need to annotate the actual implementation function?
seems redundant and wrong
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
do you mean overriding an abstract method from a base class?
no
The reason is that the annotations for the implementation define the types for inside the function, and what it should be returning.
whats the difference?
I think you meant (num: T) -> T
yes
if you pass an instance of a subclass of A, this would be inferred as returning the subclass
while the overload version would return A
!pip overload Take a look at this library.
fr?
there's so many libraries that do that.
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?
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")
why you dont import Callable from typing?
cause its deprecated
since when?
3.9
okay thanks
it's unnecessary developer overhead to imagine the actual type hints of the implementation function everytime you read or modify it. why not do it once and put it in. it also allows mypy to actually verify that the overloads are possible with that signature
you can declare callable protocol that returns self from __call__
which is basically ```py
Self = TypeVar("Self", bound="Foo")
class Foo(Protocol):
def call(self: Self) -> Self: ...
thats just more code for no reason really
yeah... Self works
i have created type stubs for cefpython, should i distribute them via typeshed or make a package on pypi instead?
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
Yes, you could use ParamSpec
How so?
I know you can annotate args and kwargs using ParamSpec but I don't know how to "connect" the two signatures together
From what I can understand they're already "connected", what's wrong with your code here?
They are not
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?
we'd be happy to take them in typeshed
that would provide a standardized way to test the stubs and distribute them, so hopefully less work on your part
alright then
typeshed it is
damn, line-length is 130
that barely fits on my screen
Yes, that's what I need. args and kwargs from async_command are passed directly to command
I don't think you can actually do that 😅
But maybe I'm wrong
can i use builtin generics for type stubs intended to also work for python 3.7?
yes
as well as | unions
yes
mm okay
can i use @type_heck_only for TypedDict that aren't (probably?) exposed by the library?
oops
wtf discord
usually we just mark those with a leading underscore. @type_check_only isn't well supported by type checkers
hmm okay, i already underscored them
that should be enough
yes
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
)
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
kinda confusing
and do i need from __future__ import annotations?
i am using annotatations before declaring them
no, forward references are always valid in stubs
alright
stdlib/builtins.pyi line 416
class str(Sequence[str]):```
even this works
that makes no sense whatsoever, like str is a sequence of str?
isn't there any char type in _typeshed?
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
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
Can you show how this is being used?
Any | None None is included in Any, isn't it?
It's complicated; Any is a weird thing in the type system
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
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
have you tried reordering the decorator?
that makes the decorator error go away, but also seems to make the editor not recognise the overloaded signatures nor does it show an error if the overloads don't match the implementation.
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
define PositiveInteger inside asPositiveInteger
hmmmm, not sure to follow ; then I cannot type it anymore (nor use the type in a signature of another method)
oh so it works like unknown on typescript? thats neat, never thought of this before
unknown is more akin to object
Hey. Does anyone know of a tool to convert code written using the new 3.12 type parameter syntax to the old syntax?
pyupgrade probably will at some point
but i dont think that will come till its at least merged into main
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
I think you'll have to use an enum
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
You can make a PositiveInteger class
then you can do the checks in its __init__
Hmm, is there a way I can then get a list of ["heads", "tails"] from that? Coin.__attrs__ or something?
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))
to get all
Coins you can actually iterate over theCoinclass
!e
from enum import Enum
class Coin(Enum):
heads = "heads"
tails = "tails"
print(list(Coin))
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
[<Coin.heads: 'heads'>, <Coin.tails: 'tails'>]
Looks like there's a __members__ too
!e ```py
from enum import Enum
class Coin(Enum):
heads = "heads"
tails = "tails"
print(Coin.members)```
@solid light :white_check_mark: Your 3.11 eval job has completed with return code 0.
{'heads': <Coin.heads: 'heads'>, 'tails': <Coin.tails: 'tails'>}
Eh, would need to get .keys() I guess
And then convert to list
So yeah, might as well just do list(Coin), thanks
yeah, something like [coin.value for coin in Coin] if you really need the strings
do you mean that direction or the reverse? There's not a whole lot of code written using the new syntax yet
Also shouldn't this technically be a StrEnum?
Or if not, then why?
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
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)?
main.py:10: error: Argument 3 to "flips_to_reach" has incompatible type "str"; expected "Coin" [arg-type]
Hmm
I guess my VSC doesn't have linting for some reason....
Any idea how to fix that?
Figured it out
(cmd+shift+p -> python: select linter -> mypy)
I do, although I can see that there's probably not much use for it 😄
Is it possible to keep track of the development of pep 695 in cpython? Is it being worked on currently?
feedback welcome
Ah nice thanks!
Heh
I had to cmd+shift+p -> python: select linter -> mypy
It was set to disabled
that I like it! nice one
that won't work yeah, it will break the feature of having a strict subtype of int
the _Opaque thing is cool! thx
can i use the new [T] syntax in stub files?
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
Could typed ast not support it?
we've given up on typed-ast
I think Sebastian tried to backport positional-only args syntax to it and failed
how can i typehint a variable that can be a class or any of his child classes?
type[BaseClass]
thanks!
what can i do if importing modules for typehinting causes a circular import?
- from __future__ import annotations
- from typing import TYPE_CHECKING
- guard your type hint only imports in an if TYPE_CHECKING conditional
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)
- I understand how
pyrightis inferring the typeint | str, but I don't understand whymypyis inferring the type ofTto beobject. - Which behavior is correct in this case?
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
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?
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:,}")```
Make two overloads, first of which uses prop:Literal['variance']
See second message
You can slap #type:ignore on it, it will work as intended
@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"?
Literal['pool_size','prob_heads_first',<all other props except variance>...]
I dont think there is a shorter and simpler way to do it
Fair enough
Is there a way to dynamically do it?
I.e. Literal[", ".join(x for x in Round.__dataclass_fields__ if x != "variance")] or something 
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
That's fine, this'll do for now. Thanks 😄
Typeshed also has weird long literal types
How comes mypy says this is wrong? It runs just fine, and __args__ is there when you print(dir(RoundProperty))
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
mypy doesn't know about the internal structure of typing constructs. You should use typing.get_args instead
!d typing.get_args
typing.get_args(tp)```
Huh, til. Thanks
Seems to be missing its own documentation lol
I think because it's documented together with get_origin
Not sure that was a great idea
Ah, I see
@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)`
this is correct then?
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from panells import Panell```
That should work just fine I think 👍
Thanks for the response, I think I'll file a GH issue for this then and see what the pyright devs think
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?
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?
ugh don't use --no-strict-optional it's evil
my recommendation is to not use mypy in pre-commit at all. it's just not really a good fit, see e.g https://github.com/python/mypy/issues/13916 if you must, use additional_dependencies to specify types-requests
ah alright, it was listed on the pre-commit page so I just kept it, will take a look at that issue
yep, that fixed it - guess I'll see if I end up sticking with it, thanks for the help either way!
Hello, is there video release about typing summit in Pycon?
not yet afaik
We didn't get professional recording. We recorded it on a laptop but not sure when we'll release it all
do you have the slides for it or was it just free form?
we don't
please do! it'll be closer for you too
oh is it in the uk?
oh right 😅
Thanks @soft matrix @oblique urchin
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
yes but not well at all
how would I do this
foo = Fooint and then use self.__orig_class__.__args__[0]
oh and as a word of warning that wont work in init/new
oh that feels gross
thanks though
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
nice, thatd be really nice
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
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)
What is there's an attribute named T, already defined in the instance / class? Or if the class redefines getattr dunder?
i was adding them as get set properties which will raise a deprecation warning if you overwrite them when using the new syntax
I'd probably prefer a typing. function to access them over an actual runtime attribute which can hide user-defined attributes
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
When I see .T I think of the Transpose, not the typevar
yeah was just about to say that
numpy arrays have a .T attribute for their transposition
hmm
plus if you change this in 3.13, you'll now break existing generic classes with such attributes
should have done some cursed frame hacks to make array^T work :(
that means a backwards compatibility break, which we generally don't want
from numpy import T
maybe you could make Foo.get_type_param(T) work
where you first get T from Foo.__type_params__
(or a function wrapping it)
the idea was that it couldnt overwrite anything so it generally shouldnt break
but idk how feasible it actually is
boring ;)
but isnt it Foo.get_type_param("T")?
otherwise T is undefined
yeah that's why I said you have to first get the TypeVar object out of Foo.__type_params__
oh right
this is better
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
speaking of, how do you "write a pep"? do you need permission, credibility, or something like that? or can anyone write one and make a PR (as per PEP 1)
Anyone can write a PEP. If you are not a core dev, you need a sponsor who is a core dev
I sponsored a few of @soft matrix's PEPs for example
i see! sounds good
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?
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
hm
pain in the ass that is pycharm's type checker 
Can I use a different type checker, hinting within pycharm?
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 asIterableor 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 calliter(obj).
Is it possible to check if an object is iterable in a way that mypy will recognize?
not natively, no
interesting, the type(row) is <class 'dict'>
I guess pycharm does not know this
Does VS Code get it right?
yep
yeah this just seems like a bug in PyCharm: https://github.com/python/typeshed/blob/main/stdlib/csv.pyi#L67
stdlib/csv.pyi line 67
class DictReader(Generic[_T], Iterator[_DictReadMapping[_T | Any, str | Any]]):```
DictReader is an Iterator[dict], so your rows should be dicts
Thanks, everyone
how can i type hint an argument accepting one of "mask", "audio", ["mask", "audio"]?
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`
"""
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?
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
@tranquil turtle sorry. I forgot to add the classmethod decorator which makes it a class attribute
Can you make a reproduction on https://mypy-play.net
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])```
That should just be -> Self:
You can use from typing_extensions import Self
And sorry to ask, but there is always something to learn. Why the Type['Report'] is not correct?
You're returning an instance of Result not an instance of type
The type checker should have failed on line 765
It has not, that's why I was confident it was the correct one
Are you running it on that file?
Running the linter, you mean?
Not using mypy
What are you running?
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
There's other problems in that code like to_disk returns None sometimes
So your type checker isn't configured correctly
Guess this will fix it def to_disk(self, filename='') -> str | None:
Yeah but you should find out why your type checker is allowing it
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
How are you running it?
You should get CI to run your checks to eliminate problems caused by your own environment
Ok, learning new things here ... CI is ?
Though, I actually remember why I did arrive to use that Type['Report']
gsc_wrapper/query.py line 530
def get(self) -> Type['Report']:```
because I was in need to return a strong typed object but at that time of the code the class report does not exist
Continuous integration, basically GitHub Actions
which at this stage I believe it's a mistake too
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'
)```
Uh not sure completely but should you even unpack it?
I'm not sure, can i have it do what im wanting without unpacking? my coding knowledge is only self-taught, and i've never dealt with packing or unpacking before.
Well apparently parsing tuple without unpacking works fine
!e ```py
import typing
a = tuple("abc")
t = typing.Literal[a]
print(t)
@peak echo :white_check_mark: Your 3.11 eval job has completed with return code 0.
typing.Literal['a', 'b', 'c']
okay so that why it works fine when i run it on my pc, I'm guessing the hosting service im using isn't running python3.11?
Just don't unpack it?
how do i do that? i've never dealt with packing or unpacking ;-;
If you mean removing the typing.Literal[]
I meant specifically doing this
typing.Literal[item_types]
Without the *
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
It works ❤️ i need to do some more learning ;-; Thankyou for your help!
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
hmm, i can't repro that. any chance you messed up your import of dataclass?
have you tried restarting vscode? in my experience pyright occasionally mucks up and gets fixed by restart
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)
yep, it's consistently busted
from dataclasses import dataclass, field 🤷♀️
might you have a types-dataclasses package installed?
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?
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?
Might just be a bug in mypy. ParamSpec support is relatively new and it's highly complex
Should I report it on GitHub?
sure
that second one is a duplicate, let me find it
yeah i thought so as well
oh ok
and fixed too: https://github.com/python/mypy/pull/15133
Thank you, very cool!
you should thank Jukka 🙂
I have done so
This PR mentions that type narrowing is unsound. Why is this?
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
OK
would be something around a nested function using the value defined in the previous iteration of the loop
is there any way or a mypy plugin which lets us add bounds to an int argument? Like only 0-100 are valid values
oh awesome
I don't think there's an existing mypy plugin for that, but it should be easy to make
fortunately, you don't even have to invent types for integer bounds, you can use the types defined in this project, and make your plugin around it: https://pypi.org/project/annotated-types
Is this a valid error from a linter?
Expected type 'list[Mapping]', got 'Sequence[RowMapping]' instead
it even inherits from tpyping.Mapping
if the function expects a list, you cant pass any Sequence to it. it must be list. @carmine phoenix
oh yeah ignore me
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
What is type-hinting used for? Why should I use it?
the error is actually backwards, because its about the return type.
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)
haha sorry bro, I'm at work
maybe this will help
https://tushar.lol/post/mypy-guide
it is Pycharm, I keep getting these weird warnings and i dont know if the linter is tripping, or the types are actually wrong
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
Thanks
I suspect that, but want to be sure about it
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
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
well once https://github.com/python/mypy/pull/11396 is merged, making IDE plugins for mypy will become way easier, so I'm waiting on that
Description
Resolves #10816
The changes this PR makes are relatively small.
It currently:
Adds an --output option to mypy CLI
Adds a ErrorFormatter abstract base class, which can be subclassed to ...
Hi friends! Can you please approve the workflow on this PR ? https://github.com/mypyc/mypy_mypyc-wheels/pull/65
I'm trying to get mypyc version of mypy v0.780 for python 3.9
I don't have access to that repo, but I don't think we'd want to build wheels for 0.780 at this point
that version is from years ago
(June 2020)
Yes, unfortunately I just need it for a one-off ...
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
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
I checked PyPI and it had wheels up to 3.8
Which presumably means we didn't actually support 3.9 at that point
looks like most of my typeshed 3.9 changes just about made it into that release though 😄
yeah ok it seems that they are incompatible. Thank you for trying!
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
yeah, i'd def try checking out and making a mypyc wheel locally. there's a decent chance that it's fairly straightforward
if you're having trouble upgrading mypy, you could use something like https://github.com/orsinium-labs/mypy-baseline
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 😅
damn I didnt know this was possible
data: dict[Literal['ab', 'b', 'c'], int] = {}
data['ab'] = 3
its basically TypedDict but in one line
typed dict lets you set types for each key though, right?
jokes on you, you can already do TypedDict in one line
dict[{"ab": int, "b": int, "c": int}] is supported by pyright
i would still personally prefer being able to use all collections just using there literals
tuple[T] shaking
I'm glad I found that and brought it up in #python-discussion earlier this week
i proposed this on discuss once
back when that arrow syntax for callables was proposed
can't remember what happened of it, but there were some problems
arrow callables were rejected
unless an arrow function can change the scoping semantics of lambda, it's unlikely to be accepted
fragile around current generic syntax (Foo[T, U] is semantically equivalent to Foo[(T, U)])
I was able to build mypy 0.780 with mypyc for py39 with some light patching, thank you everyone!
huh
Really?
mypy?
no hence why i said supported by pyright
Not yet. A PEP for this is in the works; after that is submitted somebody will hopefully contribute the feature to mypy
mypy doesnt tend to implement experimental things
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
oh til
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
it looks fairly similar to what i had as my poc but its probably a lot less broken
you'll need to read setup.py, but off the top of my head USE_MYPYC=1 python setup.py bdist_wheel should be a reasonable starting point
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
you'll need a callable protocol
class F(Protocol):
def __call__(self, foo: Foo, *args: Any, /) -> Any:
...
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
ah, you're making a decorator
I don't know actually
can you show the code for the actual decorator?
I think your decorator should be generic over a TypeVar bound to _F
that wouldn't solve it, because the protocol requires the function to accept an arbitrary number of arguments
so my solution was wrong
right, the subtyping goes in the wrong direction
so you'll need something like a ParamSpec?
or like ```py
Ts = TypeVarTuple("Ts")
F = Callable[[Foo, *Ts], Any]
damn, I think thats what I need, thanks so much!
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
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?
That's unlikely to work with static typing. You need intersection types at least, and even with that I'm not sure you could express this type
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
there is no way to express intersection types, and I don't think Union[*Ts] is legal
Hm, It does (seem to) work with pyright though - I think adding an Intersect type would be very useful for cases like this
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?
(why not just:
class C(A,B): ...
(if both A and B are protocols, it can be quite "light")
something broadly equivalent (not syntactically, but in idea) could be to just have a tuple/dataclass. after all, A and B are two projections of your new type. so you remove your idea of intersection and use a product
@dataclass(frozen=True)
class C:
a: A
b: B
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)
thx!
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.
I compared a bit on the existing project, and pyright found 2 issues mypy couldn't find, so I'll go with it I think
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
VSCode also supports that
and Pylance (which is... almost pyright) integrates very well with VSCode
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
Well, you can make an extension if you want 🙂
The main problem is that PyCharm doesn't support LSP
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?
because typing is pain 👍
💀
anyways, some one told me to use filter(None, iter)
it works
so...
for now, i live
it's subtly different, because it selects truthy elements IIRC
!e
print(list(filter(None, [0, 1, "", 2])))
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
[1, 2]
but yeah in your case it's probably the same
everything is an object 🙂
||python is pain 👍||
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"
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
I didn’t say to use that lol
That actually checks truthiness, not non-Noneness
filter(None.__eq__, it)filter(None.__ne__, it) 💀
You want None.__ne__
Yeah
i didn't know that u posted it out of context
im aware, but it's fine in this case
sure it's not ideal
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.
List[Tuple] are you using an old version of python?
Tuple (or rather tuple, in new syntax) is itself a variadic generic type
Pycharm doesn't support LSP so you won't be able to make pyright work on it natively.
yeah I abandoned and used mypy
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)
I'll look at typing the Tuples indexes later.
isn't it just Tuple[str, str]?
MutableSequence does tho
Right?
Oh I misunderstood 😄
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
that should work as intended, which means you have another issue
!code
Oh btw, if your class doesn't have parents, you don't need to (and shouldn't) put ()
class Test:
# vs.
class Test():
yeah I know
well
this is the issue that the interpreter is throwing
so I don't know what could be wrong
Your class isn't defined where you are using it
Either you are using it before it exists in the script, or you didn't import it/correctly
if you want to avoid that use either __future__.annotations or stringise the name manually
or structure your program correctly, depending on what exactly you have
Should send code tbh
code is huge
Or use Self from typing depending on context
(Added in 3.11)
ah okay
yeah, Settings isn't being initialized at all in the file giving this problem
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?
classes.Setting?
modules are run when imported
# file a.py
print("a")
# file b.py
import a
a
I'm importing with wildcard, so from .classes import *
still runs the code
yeah
then adds everything to current namespace
so it should be just Settings
yeah should
not classes.Settings
gui.py, when I try to run one of the methods of Settings
or rather
gui.py right on line 1, when I import functions.py
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
and inside of functions.py you imported wildcard of classes?
the from .classes import * doesn't appear in functions.py, does it?
yes
it does? sorry, don't quite get your question
try to import just Settings from that
from classes import Settings?
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)
i was guessing there might be a circular import
do you import functions into classes?
I would make a map of your imports to find where it is circular
just draw it 🤷♂️
ah okay
idk of any tool, but you might find one
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
not sure what you mean exactly
btw self.load_settings()
unless you specified that method as staticmethod, but that wouldn't make sense given how you're using it
like, instead of arg1: Settings, I wanted something like arg1: Settings.settings
it isn't a staticmethod
Settings.settings doesn't exist at all
it is only on instances that you have self.settings
either way, that would refer to the value, not the typehint
may I ask why you wanted that type?
because there could be an alternative
because some functions take the plain Settings instance others take either the instance.settings or instance.subs as args
at the time I wrote the function, I don't remember why I made the argument the attribute rather than just taking the instance, and appending the attribute inside it
but knowing me, there was some issue
I will probably try again to fix that again
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
PyCharm is a bit funky
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)
it works
but only when the class is defined in the same file I think
it's weird
awesome, can you put that up somewhere?
You mean the final wheel?
py3.9
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? 
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
im using them to typecheck the mongo collections
cant pydantic do that for you?
like it has a from_dict method that will validate them for you
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
well its not very DRY so i dont think having the duplication is great
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
the thing is, you should not couple the way you store data with the way you transport it over HTTP
those may look the same when you start developing the application

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
(for example, you probably don't want to store the password in the database, you really want to store the hash of the password)
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?
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
It's for stuff like __repr__
aaaaah okay ! so nothing related with some kind of weird reflexion? it's just for representation purpose?
generally you shouldnt use the non-class function version
so you mean, I should use the nametuple from collections? (up to typo)
no i mean you should always use
class S(NamedTuple):
foo: int
bar: str```
why does the call version even exist btw?
for fields that conflict as non-identifiers i think?
oh actually that doesnt make sense for tuples
yeah
ah neat! didn't know the construct . I prefer it
it must just be some old lay over from before 3.6 at this point that theres no point removing
there is just one minor issue : when my class declaration are nested inside a class, cann't refer to the types as fluently
for exemple:
class Foo:
class Bar(typing.NamedTuple):
self: str
class Babar(typing.NamedTuple):
_links: Bar
namespaced types to me feels very wrong
yeah but actually they kind of make sense to me
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
that code might work in 3.13 but idk
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"
could I request a mypy PR review here? it's already been reviewed by Ivan, but needs a final look by someone.
anyone know if theres a version of merge pyi that supports 3.10 unions?
or if a pydowngrade tool exists?
I made a PR to libcst for 3.10 unions. So if you install master, merge pyi should do the right thing
ah good to know thanks, i just manually replaced them all by hand though
wasnt too bad
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 ;)
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
async def test(self) -> AsyncIterable[Any]:
if False:
yield
raise NotImplementedError
this approach is used in stdlib
thx
Lib/_collections_abc.py lines 283 to 286
@abstractmethod
def __iter__(self):
while False:
yield None```
why while instead of if 😂
you can just make the abstract method a def, not async def
unfortunately this is a bit weird
yes it looks weird
but maybe less then "while False"
I will see
I personally prefer raise ... yield ...
Also does the peephole optimiser not just stop that working?
the peepholer doesn't change whether something is a generator
Ah interesting
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
see also this section of the mypy docs: https://mypy.readthedocs.io/en/latest/more_types.html#asynchronous-iterators
this also works
raise NotImplementedError
yield None
personally I think I'd do
yield from ()
raise NotImplementedError
crazy stuff
is importing std lib types from typing deprecated?
like is list[dict[str, str]] preferable to List[Dict[str, str]]
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
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...
That makes different byte code and is slower
I dont think speed matters here
Exception handling is still slow
then I believe yield from empty iterator should be a fast path at runtime. it's a common enough pattern, empty generators
Is it?
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
could it be a pyright bug?
good ty
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
Typing Meetup Notes PyCon Typing Summit: April 20, 2023 Jelle Zijlstra, The State of Typing 2023 Slides Recording (this one unfortunately starts from the middle of the talk) Alex Waygood, Common Typing Pain Points from User Questions Slides Recording Someone: What about class decorators? And c...
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?
maybe with a mypy plugin
screams in pyright
fork pyright 🥴
sounds scary but probably about as much effort as making a mypy plugin
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
There is actually a PR being worked on for mypy that specially handles replace(): https://github.com/python/mypy/pull/14849
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
I highly doubt that. Pyright is huge, less inclined towards new contributors, and doesn't have any contributing docs
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
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
and mypy is bad at adopting new features. The circle continues
(bad as in they're slow)
for me, pylance being both LSP and a type-checker makes it much easier for me to have expectations in my CI/CD flow + IDE experience
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)
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
It does. A lot. Pyright has 0
It does. A lot.
Could you share a link?
I only found this: https://mypy.readthedocs.io/en/stable/extending_mypy.html
Which, I guess, is a high-level introduction. But it didn't help me make a working plugin
Of course, pyright doesn't have any documentation for making plugins, because it doesn't support plugins
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.
oh my god how did i not know about this?
hmm but that's for the development of mypy, not plugins?
or am I misunderstanding
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.
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
Hello this is a channel about type hinting not general help so you're unlikely to get help here. For your issue you called check before you defined it. You might want to move your call to check(...) into a def main(): function then call main() at the bottom of your file
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?
This came from https://github.com/pylint-dev/pylint/issues/1469
Wondering whether this is a typeshed issue.
Steps to reproduce Using this test file, example.py: from asyncio import subprocess subprocess.create_subprocess_exec('echo', 'hi') Run pylint example.py Current behavior The follow...
no, i don't think a bug in typeshed because asyncio.subprocess defines __all__
Ah, thanks. Sigh....I remember ruling it out a few times because of that, and then it slipped my mind when revisiting.
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?
This is absurd json, it will be pretty difficult to work with
https://github.com/nekitdev/mix-descriptors/blob/main/mix_descriptors/core.py hey, can someone help me out with fixing type issues here?
You can use the json_loads config attribute
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?
Something like this?
use a metaclass 😛
What do other type checkers say?
what would be the easiest way to try other type checkers?
pip install mypy pyright
mypy file.py
pyright file.py
mypy just shows errors in other files that it imports
If you add reveal_type(opc) to the code (somewhere where opc is in scope), Pyright will tell you what type it believes it has.
every typechecker should do that
Perhaps mypy has a different command for that
Mypy has reveal_type and reveal_locals
does anyone know how to update this to the new staticmethod with paramspec https://github.com/dask/dask/blob/cdb183a2dddfac6d1fb445be66f1c62f91434c13/dask/typing.py#L234 ?
dask/typing.py line 234
__dask_scheduler__: staticmethod[SchedulerGetCallable]```
after https://github.com/python/typeshed/pull/9771 it fails with dask/typing.py:234: error: "staticmethod" expects 2 type arguments, but 1 given [type-arg]
but I don't have a ParamSpec to give it
using ClassVar doesn't work: https://mypy-play.net/?mypy=latest&python=3.11&gist=2b61cd31da7c1e114bd9e972ef736d66
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
looks like staticmethod[...] doesn't work either https://mypy-play.net/?mypy=1.1.1&python=3.11&gist=c58b3238e8833005078cbf259ca94f94
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
@staticmethod
def __dask_scheduler__(...) -> ...: ...
oh that's much better
this doesn't work now: https://mypy-play.net/?mypy=latest&python=3.11&gist=6a42a27bd0d33fa41b6505a6d03b190b
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
oh no it does work
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
I 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]
ok I think it's a mypy bug: https://mypy-play.net/?mypy=latest&python=3.9&gist=ba19e0cf56df666b4cd41463e0678cc0&flags=strict
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
yep breaks on 3.9
reduced it down a bit https://mypy-play.net/?mypy=latest&python=3.10&gist=1438683686842d8a03bfbc32646c7952&flags=strict
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
https://github.com/python/mypy/issues/15257 made an issue here
looks related to 3.10 staticmethod having .__call__
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"?
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
I actually currently have that setup but it does not work, I suspect it is because of the generics?
could you show a quick code snippet of this?
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.
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
Unfortunately no luck with isinstance(request.whatever, class),
I'd prefer not using cast, only as a last resort :d
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
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
I'll do this fairly often so I'll go with TypeGuard, thanks! Never heard of it before :D
it's a 3.10+ feature (though you can use typing_extensions backports, see: https://peps.python.org/pep-0647/ for some more reading about it)
Python Enhancement Proposals (PEPs)
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)
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.
you can raise from the typeguard, it will just propagate into the function that called it
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
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
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 
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?
PEP 649 is accepted and will be in 3.13
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
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
well, that makes it impossible for users to introspect what A is (as it's not in the module namespace at runtime)
best to actually import module.A in the place where TypedDict is defined
Right, and do I keep from __future__ import annotations or how does that work with PEP 649?
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)
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
no, you can't conditionally import futures
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!
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?
the same way
Oh, that makes sense
Can X | Y be considered a sub-type of Z if both X and Y are sub-types of Z?
Will there be a future import for this behavior?
yes
The current plan is for it to just be the default, no future import
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
from __future__ import annotations definitely won't be removed in 3.13, so if you need backwards-compatible semantics, you can continue using that
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
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"
But as Jelle said here, a new future import for PEP 649 wouldn't really help with that
These imports don't work in the same way as any other imports; you can't conditionally import a future
well, it would if this behavior was backported into older versions
as a new future flag
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
but yeah, I doubt that will happen
I understand the concern 🙂
Just difficult to see a better way of dealing with it, I think
A future import for PEP 649 would help
Has this type of thing been discussed somewhere already?
E.g., where I can go to read up on the context?
How would it 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
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
As in, you'd want the future import backported to <=3.12? That's very unlikely to happen. PEP 649 will be a huge change, and generally even minor, risk-free features aren't backported to older Python versions. Only bugfixes.
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?
That would solve the issues I'm concerned with, but I feel like it wouldn't be great for the wider ecosystem as it would mean yet another delayed adoption
Exactly
And the last time we tried this strategy it didn't end well 🙂 (PEP 563)
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
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
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 🤷
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.
Oh right, that's true
In fact there's probably going to be codebases with all three styles in different modules
Did I miss something? typing.resolve_annotations does not exist as far as I'm aware
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
Yeah that.
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
Like an AST for type hints?
Even a nested dictionary would be helpful 😬
But that's basically what a lot of libraries resort to currently
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
I wish we had some unified standard for all the typing stuff. For example, type inference and all the other details that differ between mypy/pyright/pytype/...
And then runtime libraries have to implement their own version of that again
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
isinstancechecka
To be honest, I'm mostly not concerned with type checking here but with using the types at runtime, which is a use case that's ever increasingly popular and, I'd argue, also one that's driving the adoption of type hints in general
A generic API like this, if available at run time, would certainly help in any case
note that PEP 649 adds new functionality that should make it a lot easier to get type hints at runtime while dealing with if TYPE_CHECKING etc.
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?
yes, the transition period will still be painful
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
I think we're likely to drop it around the time 3.12 goes out of support
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 🤷
yes, it's not a great situation 😦
Reading the changes proposed in this PEP I suppose there's also no chance of getting a backport of this to an external package (e.g. typing_extensions)?
we'll backport what we can, but I think all of what PEP 649 does is not backportable as it relies on changes in the core
I don't really see how that functionality could be emulated without the core changes though so.. Yeah, unlikely :/
Yeah, this is pretty much what I was afraid of 😬
we'll backport what we can
That's still good to hear ❤️
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
if not TYPE_CHECKING:
def __getattr__(...):...
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])
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
a class that implements UsesA must accept all classes that implement A, not just a specific one
so the type annotation on ImplementsUsesA.use_a should be A, not ImplementsA
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
yeah, this is a common rough edge people encounter when using pre-commit with mypy (or black)
Honestly it makes sense to type check your tests too
optional types?
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
just S works, but you got your decorator annotation a bit wrong
def decorator(*sources: S) -> Callable[[Callable[[Parent[S]], Any]], Callable[[Parent[S]], Any]]:
def inner(func: Callable[[Parent[S]], Any]) -> Callable[[Parent[S]], Any]:
return func
return inner
what are you actually trying to do?
Parent doesn't exist right? This was just to "show what I mean"
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
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
isn't this what you need?
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
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
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
how are you using the code?
@app.post("/example")
def example(body: Example):
return "hi"
and sending a POST request with the body being { "foo": "hello" }
cast is about as unsafe as type ignore! but yeah, a cast is going to cost you like 30 nanoseconds so definitely don't sweat perf
Cast atleast doesn't hide name errors
nooo my 30 nanoseconds
more like 100ns
on my machine function call (without args) takes around 50-70ns and builtin name lookup takes around 15-20 ns
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)
float + float is faster than float + int by several nanoseconds
what's happening here is int += bool, even when the cast is present
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)
thanks, I will take note the next time I want to find pi in a type-safe way
shouldn't cpython do an optimization for this case? It should just remove the calls to cast while generating the bytecode
essentially make typing.cast zero cost
why?
cause lookups can change dynamically
Currently to cast from one type to another in typing you have to use the function typing.cast which requires a runtime cost and is not very pleasant to use. i propose a built-in syntax for this: a as Type this re-uses the as keyword from imports and with statements, this is better over the existing typing.cast because it can be a no-op at runt...
was a better idea
Is detecting whether a function called cast is from the stdlib typing hard?
but got bikeshedded to high heaven
yes because monkey patching exists