#type-hinting
1 messages ยท Page 5 of 1
maybe I should've just avoided variadic generics here
ok another question:
@dataclass(frozen=True)
class Row:
frac: Fraction = Fraction(0)
min: int | None = None
max: int | None = None
_ToRow = int | Fraction | tuple[int, int] | tuple[Fraction, int, int]
def _to_row(raw: _ToRow) -> Row:
match raw:
case int(n):
return Row(min=n, max=n)
case Fraction(f):
return Row(f)
case (minh, maxh):
return Row(min=minh, max=maxh)
case (frac, minh, maxh):
return Row(frac, minh, maxh)
case invalid:
assert_never(invalid)
pylance is telling me that invalid has the type Fraction. How so?
I guess I can do ```py
case Fraction():
return Row(raw)
oh wait, maybe Fraction just doesn't define a class pattern like that
I think a few builtin classes were special-cased
๐
In [3]: fractions.Fraction.__match_args__
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Input In [3], in <cell line: 1>()
----> 1 fractions.Fraction.__match_args__
AttributeError: type object 'Fraction' has no attribute '__match_args__'
```yeah looks like youre out of luck
what would f be here?
the same as raw, I guess?..
cause you can always just use
case Fraction():
return Row(...)
like with int
error: Incompatible types in assignment (expression has type "List[TBaseGrain]", variable has type "List[TBaseGrain]")
pulling my hair out here
it's literally the exact same
it's the same TypeVar
what am I doing wrong
Show the code
Sometimes that happens if you import something incorrectly
this happens when you bind one typevar in two different places, so they are not the same
or you have some meaningless function signatures
I give up, maybe im wrong, i cant reproduce it in one file.
Why can ClassVar not hold TypeVars?
Cause it's technically ambiguous as to what they would refer to at runtime but I think there was talk of changing that in the last typing meeting
I think it was more of an issue with early typing but nowadays it's less of an issue
Also, how can I derive from a protocol but keep it unimplemented?
this sounds right to me, how do I fix it though?
do I have to redeclare the typevar separately in each individual file?
Keep what unimplemented?
add Protocol to the bases
class IFixedSizeEvent(Protocol[T]):
ID_RANGE: ClassVar[tuple[int, int]]
CODEC: c.FormatField[T, T] # can't make this ClassVar as well
class PODEventBase(EventBase[T], abc.ABC):
"""Base class for events whose size is predetermined (POD types)."""
def __init__(self, id: int, data: bytearray):
if id not in range(*self.ID_RANGE):
raise EventIDOutOfRange(id, *self.ID_RANGE)
if len(data) != self.CODEC.sizeof():
raise InvalidEventChunkSize(self.CODEC.sizeof(), len(data))
super().__init__(id, data)
class ByteEventBase(PODEventBase[T], abc.ABC):
"""Base class of events used for storing 1 byte data."""
ID_RANGE = (BYTE, WORD)
class I8Event(ByteEventBase[int]):
CODEC = c.Int8sl
class U8Event(ByteEventBase[int]):
CODEC = c.Int8ul
I would like to keep impl in base class
but child class should implement the protocol
how?
class ChildProtocol(BaseProtocol, Protocol):
how would i make use of that for PODEventBase
ok I have a MWE for this problem I'm having
file_1.py:
from typing import TypeVar, List
class Bird:
def __init__(self, species: str) -> None:
self.species = species
TBird = TypeVar('TBird', bound=Bird)
def get_first_two_birds(list_of_birds: List[TBird]) -> List[TBird]:
return list_of_birds[0:1]
file_2.py:
from typing import List
from file_1 import Bird, TBird, get_first_two_birds
def make_two_birds() -> List[TBird]:
bird1 = Bird(species="owl")
bird2 = Bird(species="ostrich")
bird3 = Bird(species="pelican")
birds_list = [bird1, bird2, bird3]
two_birds = get_first_two_birds(birds_list)
return two_birds
if I run mypy file_2.py I get: file_2.py:17: error: Incompatible return value type (got "List[Bird]", expected "List[TBird]")
def make_two_birds() -> List[TBird]:
This signature doesn't really work, the purpose of a type var is to link two types together
actually its not exactly the same problem by the looks of things
I thought it was to make lists of types work?
if I don't use TypeVars, I run into big problems with lists of subtypes
e.g List[Owl] is incompatible with List[Bird] even if Owl is a subtype of Bird
I have a small tutorial on generics, variance and all that stuff:
https://decorator-factory.github.io/typing-tips/tutorials/generics/
have you checked it out?
also I ran into that exact same filtration problem
Imagine if List[Owl] was compatible with List[Bird].
def add_penguin(birds: List[Bird]) -> None:
birds.append(Penguin("Quack"))
owls: List[Owl] = ...
add_pengin(owls)
# oops, now there's a penguin in the list of owls!
so would it have to accept List[Bird] | List[Owl]?
Most of the time you don't really need a list, you're just reading the items, and it will work with a tuple, and maybe even a set. For that purpose, use collections.abc.Sequence or collections.abc.Iterable depending on what you're doing
in your linked example is exactly the use of a TypeVar that I am experiencing
afraid not, I don't have copyright permissions yet ๐ฆ
and I'm struggling to recreate an MWE
evens = filter_list(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6])
# evens: Iterable[int]
numeric = filter_list(str.isdigit, ["abc", "123", "", "abc3.14"])
# numeric: Iterable[str]
this is the exact issue I'm walking into
which is why I'm using a TypeVar
I want to say "this function will accept a list of Bird or a list of any Bird subclass, and will return a list of the same type"
you're working on something confidential, and you don't have any colleagues who can help with your concrete code?
so if it gets a list of Owl, it'll return a list of Owl
Will it explode if you pass in a tuple?
no-one that knows Python ๐
from collections.abc import Iterable
from typing import TypeVar, Generic
B = TypeVar("B", bound=Bird)
def filter_birds(birds: Iterable[B]) -> list[B]:
return [bird for bird in birds if bird.can_fly()]
actually, a list would work
ok that's basically what I ended up with
but still running into this one error
Incompatible return value type (got "List[TBird]", expected "List[TBird]")
i'll try again to make a MWE
yeah it works for me too, I'm struggling to recreate it
what version of mypy are you running?
0.981
it's gotta be import-related
because the TypeVar is defined in one file, and imported in another file
all the stuff in the first file using the TypeVar works fine
but when I use the TypeVar in the second file, it breaks
should I re-define it?
yes
like, import the class and make a new typevar?
yes
ok one sec
typevars are a bit of a hack to be honest
same error i'm afraid
kinda feels like I should've written this in a statically-typed language lol
this is my first attempt at inheritance etc with base classes
and I wanted to type hint for safety
did you by any chance name the type var the same as the class?
no
it's TBird rather than Bird
got it!!!
@trim tangle
ok it's in two files:
file_1.py:
from typing import List, TypeVar
class Bird:
def __init__(self, species: str) -> None:
self.species = species
TBird = TypeVar('TBird', bound=Bird)
def filter_birds_to_penguins(birds_list: List[TBird]) -> List[TBird]:
penguins_only = [a_bird for a_bird in birds_list if a_bird.species == "penguin"]
return penguins_only
file_2.py:
from typing import List
from file_1 import TBird, filter_birds_to_penguins, Bird
class BirdsCollection:
def __init__(self, birds_list: List[TBird]):
self._birds = birds_list
@property
def birds(self) -> List[TBird]:
return self._birds
def filter_to_penguins(birds_list: List[TBird]) -> List[TBird]:
return filter_birds_to_penguins(birds_list)
bird1 = Bird("penguin")
bird2 = Bird("ostrich")
bird3 = Bird("owl")
birds = [bird1, bird2, bird3]
birds_collection = BirdsCollection(birds_list=birds)
just_penguins = filter_birds_to_penguins(birds_collection.birds)
it's related to the @property
file_2.py:27: error: Argument 1 to "filter_birds_to_penguins" has incompatible type "List[TBird]"; expected "List[TBird]"
(this is very similar structurally to my actual code)
@trim tangle it even works in just one file
so it's not import-related
Expression of type "List[TBird@__init__]" cannot be assigned to return type "List[TBird@birds]" TypeVar "_T@list" is invariant Type "TBird@__init__" cannot be assigned to type "TBird@birds"
You need to inherit BirdsCollection from Generic[TBird]
There are some examples in the link
could this also be solved by refactoring BirdsCollection as a proper container type?
like subclassing MutableSequence
your suggestion did fix the error
but not the error in my actual code
the error i'm having is on a collection of collections (i.e a BirdsCollectionCollection)
I've made sure that BirdsCollection is a Generic[TBird]
and that BirdsCollectionCollection is a Generic[TBirdsCollection]
ok I think this is a mypy bug
@trim tangle I've been able to re-create the actual conditions now of my bug:
@rocky sigil I think you want what's called "Higher Kinded Types". and those are not possible in Python's typing system
If you're coming from Haskell it's a bit of a bummer
If it's not that, maybe you could give an example of what you want to do in a different language?
I think that's the idea
I'm not really coming from any other language (Python is the only one I know in any significant detail)
I think I was just trying to think of a structure that would solve this problem
I've solved it in another way - by flattening some of my inheritances
it means more duplicated code, but solves the typing ambiguities
well it's kinda hard to help if we don't know the problem ๐
I think the way I've tried to do inheritance is a bit dumb (as I said, it's my first time doing this)
I would suggest asking some colleagues, even if they don't know Python (but know some statically typed language)
yeah, I get that - I appreciate all your support so far though, you've been very helpful
I'm a postgrad student writing a data analysis package for some scientific data I've collected
so it's basically down to me to write the package to analyse it
none of my peers are co-workers and neither are my supervisors
they use pre-existing software tools, but none of those exist for the type of data I'm collecting, so I'm kinda on my own here
well, it's not the end of the world
sometimes you'll have to do a bit of Any business
aye, it's actually solved now but I had to tear down my inheritance idea and remove a base class (and put its methods inside the old subclasses to make them independent)
to be honest I think I was trying to force inheritance that makes things harder rather than easier
Huh, so I'm using a dict as a registry to store class types for later retrieval and initialization. Making such classes a dataclass breaks PyCharm's inline documentation.
@dataclass
class Position:
x: int = 0
y: int = 0
registry = {}
registry['Position'] = Position
position: Type[Position] = registry.get('Position')
p1 = position(x=10, y=10) # <-- no documentation
I know this isn't specifically a type hinting issue, but wanted to see if anyone else had come across this issue.
pycharm's typechecker is kind of notoriously buggy and incomplete
pyright/pylance (which works in vscode) is great
Gotcha - it's been a while since I've worked with vscode, so I'll try that out
Does anyone know that Rust based Python linter made by someone? I heard about it somewhere and apparently it is 90% faster than Black
black is not a linter 
this claims to be faster than flake8, but i haven't tried it https://github.com/charliermarsh/ruff
doesn't say blazingly fast, can't be true
Sry meant to say formatter perhaps?
Ah I think this was it. Thank you!
No plugins ain't ideal tbh
Black is a formatter, like rustfmt
Ruff is a linter, like pylint (shit) or flake8
wouldn't --fix make it also a formatter?
you should prefer autopep8 if you want to correct flake8 issues.
Though the difference is flake8 isn't limited to format issues.
yeah it's a bit different
it can also operate on the level of the AST
and more high-level concepts
!pypi flake8-pep585
๐
base flake8 is useless if I use black but it's really worth it for all the minor stuff that plugins do
The most helpful one has been some that reported any found print calls, saved me way too many times
I just wrote this up: https://dogweather.dev/2022/10/03/i-discovered-that-python-now-can-do-true-match-exhaustiveness-checking/
If I'm wrong, please oh please let me know ๐
i think your presentation is a little confusing here. it sounds like you're conflating python itself with pyright
discovering new features is always exciting and fun though
and good on you for writing it up, i and i'm sure many people are always subject to the "i'll write it up later" fallacy
Thanks! Yeah, I edited the title to make the Pyright aspect clearer. I go into it more in the article.
Yep, I'm guilty as well of ignoring a backlog of blog ideas. ๐
Is there any library that uses Annotated for binary data structures?
They will look like dataclasses at the end
like struct but with class/attribute syntax? i'm not aware of one but it sounds like a good idea
Something like this but with classes?
https://pypi.org/project/borsh-python/
hm well, do you really need Annotated?
oh hm, you do need some additional information like endianness
imo yeah because if you do something like pydantic conint mypy chokes on it without a plugin
conint?
s / pyright / pydantic
No that's not what I meant. I mean something like the examples in pep526. To get type annotations for binary types
Yes
how would that help
Like for example:
class Binary:
int1: uint32
Maybe you meant something like this?
@binary
class City:
x: int = uint64()
y: int = uint64()
name: bytes = pascal_blob(max_length=128)
Type hints primarily
Yea
I'm actually not sure if that's possible with dataclass_transforms, but definitely look into it
id personally just use NewType here
NewType also works, yeah
Although if you have a lot of parameters it's going to be a pain
oh very fair
somewhat relatedly, mypy people are working on adding support for fixed-size integers to mypyc
integers can be:
- signed/unsigned
- big/little endian
- contain different amounts of bytes
- variable length
I am using construct for the structs. It uses strings for the field names so there's no type hints in it
!pypi construct
!d typing.NewType
class typing.NewType(name, tp)```
A helper class to indicate a distinct type to a typechecker, see [NewType](https://docs.python.org/3/library/typing.html#distinct). At runtime it returns an object that returns its argument when called. Usage:
```py
UserId = NewType('UserId', int)
first_user = UserId(1)
``` New in version 3.5.2.
Changed in version 3.10: `NewType` is now a class rather than a function.
No, this will not be able to encode / decode
I would still need to use descriptors or something
well, Annotated doesn't encode/decode on its own either
furthermore, if you have an annotated thing like uint64, there's no way to prohibit Annotated[int, uint64]
but then, you can just check it at class definition time, which hopefully happens first thing when you run the program
since you'll have to add some decorator to the class anyway
No but using annotations, encoding / decoding can be implemented without having to use anything more like descriptors
annotations don't do anything by themselves
Exactly, but a metaclass can parse the annotated fields to internally create a struct.Struct or BytesIO if its a stream, plus some magic?
With added difficulty to type checking.
Well all integer types are just int in python
I have a pydantic model: ```py
class UserUpdate(pydantic.BaseModel):
is_mod: bool
is_banned: bool
I'd like to have another model, which has the exact same fields, but each field in from parent model is Optional (so that it can be used with a partial PATCH, rather than a full PUT/POST.
I currently have this that _works_ with FastAPI the way I want.
```py
class UserPatch(UserUpdate):
__annotations__ = {k: t.Optional[v] for k, v in UserUpdate.__annotations__.items()}
However, my IDE's lang server doesn't pick up the overwrite of the annotations, so whenever UserPatch is used, none of the attrs appear as optional.
Is there a way to do this, but in a way that preserves typing?
No. LSPs are intentionally stupid like that. They can't see through all the dynamic code you're writing.
So doing what you're doing non-manually is impossible and likely will never become possible.
Ah yea, that does make sense. Thanks for your help.
Yeah, type checkers have a limited set of patterns they understand
Although, for example, in TypeScript you could express something like that
maybe it could become possible once subscripted generics become possible (like the T1[int] thingy, idk what it was called)
higher-kinded types?
that would be higher-kinded types
well, not in the general case
although hmmm
yeah, you could do ```py
class UserUpdate(pydantic.BaseModel, Generic[K]):
is_mod: K[bool]
is_banned: K[bool]
and then provide `K` as one of ```py
K = Lambda[[T], T]
K = Optional
X = Lambda[[T], ErrorValue | T]
Does pydantic understand generic models? I guess you could do ```py
Missing = TypeVar("Missing")
class PartialUserUpdate(pydantic.BaseModel, Generic[Missing]):
is_mod: bool | Missing
is_banned: bool | Missing
NullableUserUpdate = PartialUserUpdate[None]
FullUserUpdate = PartialUserUpdate[NoReturn]
is Lambda something official?
that's pseudocode for type-level functions ๐
ah makes sense
Or ```py
Missing = TypeVarTuple("Missing")
class PartialUserUpdate(pydantic.BaseModel, Generic[Unpack[Missing]]):
is_mod: Union[bool, Unpack[Missing]]
is_banned: Union[bool, Unpack[Missing]]
FullUserUpdate = PartialUserUpdate[()]
NullableUserUpdate = PartialUserUpdate[None]
huh, is NoReturn completely disregarded like that? I'd expect it to get turned into None ๐ค
oh that's interesting, never though of it like that
yes, you can think of it as an empty union
There's a new Never thing which is just a synonym for NoReturn
fixed naming
I'm actually surprised Union works with Unpack, that seems super useful
I wonder if I can make something cursed with this
Doesn't seem so :(
(before 3.11 you have to do PartialUserUpdate[Unpack[tuple[()]]] ๐ฅด )
what does it tell you?
by "work" do you mean pyright supports it
Mypy doesn't support 646
mypy has some initial support
well
"TypeVarTuple" is not supported by mypy yet
Doesn't even get into user code, fastapi just returns a 422 when trying to put the below into that type ```
{
"is_mod": True
}
--experimental-features I believe
Oh no
using PartialUserUpdate[None] as the model it tried to push it in
you wanted them ๐
What if you provide both fields but one is null?
oh wait @void ibex, I think in pydantic Optional does not make the field not-required?..
you'll have to configure it or add a default
Oh
hmmm
well that's cursed and weird
If you define it by hand as Optional, does it work?
Oh actually, you're breaking LSP here โข๏ธ
a function which accepts UserUpdate will be in great pain
tbh I'd just make a second class and not over-engineer
it is possible, i suppose, that the two diverge in the future
(e.g. a partial update always requires some field)
Yea, that was the hope in doing this I'd just need to update in one place, since the two models will always support the same fields
well, now they support the same fields
but it's only 2 for now, so likely not an issue in just duplicating
yep, wrong abstraction is much worse than duplication
and I'm not sure this is more of a duplication thing than a coincidence thing
Whats the conventional directory to place your type definitions? typings/ types/?
Yea, I'm mostly leaning towards types right now because I see it's placed in a lot of projects source directory
whereas typings is outside of the source directory
which makes it harder if I, for say want to access a typeddict I guess?
Well, either way I don't think it would be too tough
What would you put in there?
like typeddicts, protocols some stuff like that
Do you have a special classes folder and a functions folder?
Nothing yet, I'm still in a designing phase
I'm just trying to get my directory sorted out
well, it will change over time ๐
My point is @rose root: put the types in the module they belong. For example, if you have a module with some business logic like this:
# types.py
class EmployeeRepository(Protocol):
def find_by_ids(self, ids: Iterable[str], /) -> list[Employee]:
...
# business/logic/hierarchy.py
def make_employee_hierarchy(repo: types.EmployeeRepository, root_id: str) -> Iterator[list[Employee]]:
layer = [root_id]
while layer:
yield layer
layer = repo.find_by_ids(set().union(*(e.subordinate_ids for e in layer)))
``` The repository is the way `make_employee_hierarchy` defines its interface, its expectations. So if it changes, `EmployeeRepository` may change.
So instead, do this:
```py
# business/logic/hierarchy.py
class EmployeeRepository(Protocol):
def find_by_ids(self, ids: Iterable[str], /) -> list[Employee]:
...
def make_employee_hierarchy(repo: Employee, root_id: str) -> Iterator[list[Employee]]:
layer = [root_id]
while layer:
yield layer
layer = repo.find_by_ids(set().union(*(e.subordinate_ids for e in layer)))
This will make dependencies more clear, make the code more cohesive (things that are related, things that change together are placed together), and some smaller advantages like reducing merge conflicts (because everyone will touch the types module)
basically, the same reason you don't put all your classes into classes/ or all your functions into functions/
Oh yea, that makes a lot of sense. I could avoid all that confusion
Also I was recently made aware of generic typed dicts, does this mean I can make a typed dicts value generic now?
Well, it's more for this scenario, I believe:
class ErrorResponse(TypedDict):
ok: Literal[False]
error: str
class OkResponse(TypedDict, Generic[T]):
ok: Literal[True]
detail: T
ApiResponse = ErrorResponse | OkResponse[T]
``` where you want to reduce duplication
Makes sense, but when testing out via something like ```py
class A(TypedDict, Generic[_T]):
b: _T
c = Aint
reveal_type(c.b) # Unknown
Did you mean c["b"]?
idk why it doesn't show an error, but if you do c.b standalone it does
Ohhh lmao, I thought it was like NamedTuple
NamedTuples and dataclasses can already be generic
TypedDict is just a dict at runtime
Same kek
But this is pretty neat addition
I can finally do that thing I wanted a few months ago
you have technically been able to have generic "named tuples" for a while
NT is basically interchangeable with dataclasses since typing.NamedTuple was added.
class Foo(NamedTuple):
x: int
y: str
print(Foo(3, "x"))
the heterogeneous iterable unpacking isnt replicate-able with dataclasses
We need a proper unpack dunder method.
Or maybe just a typed iterator
we also need dict unpacking like javascript's const {x, y} = z
closest thing we have is part of the match statement
yes please
๐ฅด ```py
match point:
case Point(x=x, y=y):
dist = (x2 + y2)
print(dist)
actually, if you don't care about the type, you can do this
match point:
case object(x=x, y=y):
dist = (x**2 + y**2)
which looks... cursed
also type checkers don't like this
does match support dict?
yes
I guess the proper question is "do dicts support match"
in fact, pyright correctly understands that x and y are ints and dist is an int:
https://pyright-playground.decorator-factory.su/?gzip=H4sIALeYPGMC_2VPMQ7DIAzceYVHSDN1jBSpU-f-IHLBUVMlBYEVwe8LpBKKeoN9Oh9nPHu7gUFGvWIIFGDZnPXcJCFujdcKD7t8eBCQEQfIvNJ0UGFohlm66jms6vBuyPoFrj0u0BgI7PNNmmUcYw9pTKqNCzzthOvEyZGUxaHUaW6WwDCCjF13hQuk3NQ5fzoHlkPziXdcA4m_DfWDfU39LXI-K7IKXz70fEsvAQAA
but for some reason it doesn't think it's exhaustive ๐ค
I am curious why they chose case _: instead of else:
I don't care either way ๐คทโโ๏ธ
I suppose it keeps the grammar a tiny bit smaller
and it's not strictly typing related
this was hotly debated when match was added
I hate how I can't connect to that decorator-factory.su domain from work.
also with else: you can accidentally get confused with indentation and end up with nonsense
because of .su? or what
probably
well that was probably a stupid choice xD
how can we have .su domains? soviet union doesn't exist anymore
I think else was mostly rejected because it would be redundant with case _, and also because the indent level would be ambiguous
...however, type checkers can help you catch these mistakes. type checkers, yes, type hinting stuff
types -- and hinting
let's move to #internals-and-peps probably
I thing it is better to put your type-related code into submodule .typing of you package
There is numpy.typing, etc...
Is there some way that I can say that a function requires a version of a type where a specific property is not None? Something like this:
class A:
propA: Optional[int]
def thing(arg: A): # Where A.propA must not be None
pass
You could have a protocol
class AwithA(Protocol):
propA: int
Okay, haven't heard of Protocols before but it does seem like what I'd need. Is there some way that I could have it still retain all the type information from the main class? I'd like to be able to do something like this:
class A:
propA: Optional[int]
propB: str
objects = listOfAs()
filteredObjects = [obj for obj in objects if obj.propA]
func(filteredObjects) # And have func know that propA is not None, but still have access to all the other properties of A
I don't think so
Okay, thanks. I'll just have to try and work around that then.
I'm a bit puzzled by type narrowing - specifically it seems to not apply to global variables inside a class which is defined later.
def maybe() -> str | None:
return "something"
maybe_str = maybe()
if maybe_str is None:
raise Exception("Its None")
reveal_type(maybe_str)
class A:
def __init__(self) -> None:
self.thing: str = maybe_str
My expectation would be that maybe_str inside the init is narrowed to str by the exception before - but that doesn't seem to be the case in either MyPy or Pyright.
main.py:9: note: Revealed type is "builtins.str"
main.py:13: error: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str")
Would anyone know a good reason why this shouldn't work? Or should I open an issue with mypy/pyright?
hello everyone noob here. I"m trying to use # type: ignore but mypy is saying it is unused?
error: Unused "type: ignore" comment
here is how i'm using it
super().__init__(**kwargs) # type: ignore [call-arg]
that just means you don't need it there - the line doesn't raise a type error which could be ignored
surprisingly, i can work around this by reassigning the narrowed version
def maybe() -> str | None:
return "something"
maybe_str = maybe()
if maybe_str is None:
raise Exception("Its None")
maybe_str_narrowed = maybe_str
class A:
def __init__(self) -> None:
self.thing: str = maybe_str_narrowed
here, maybe_str_narrowed is str even inside the class init
# type: ignore # type: ignore ๐คช
interesting
I think that's how variables from the outer scope are generally treated
although that is weird
its incredibly wierd - its like it forgets the narrowing ever happened unless i assign to a new name
which is actually not changing anything
Suppose that you have this
def maybe() -> str | None:
return "something"
maybe_str = maybe()
def banana():
global maybe_str
maybe_str = None
if maybe_str is None:
raise Exception("Its None")
maybe_str_narrowed = maybe_str
class A:
def __init__(self) -> None:
self.thing: str = maybe_str_narrowed
I think type checkers don't yet analyse when a variable is reassigned with global or nonlocal
so you're saying when creating the class the typechecker doesn't know whether i called banana() elsewhere?
yep
hmm, wack
Well, it could be called from another module which is not even in the codebase
i see, still, i really don't like the ugly reassigning workaround
see microsoft/pyright#2225
oh wait, it doesn't have a reply from Eric
but there's some stuff in the linked issue
aye, interesting
would you have any idea how to make the narrowing of a global variable read a bit better than with the new assignment?
maybe make another functon?
like ```py
def definitely_str() -> str:
s = maybe_str()
if s is None:
ctypes.string_at(0)
assert False
return s
i've clearly done to much Rust, my expectations of type checking is way too high
ikr right
it'll have to do
(importing from a path is ugly with static typing anyways, so whatever)
Where can i read about type level Map?
Is it a proposed change or just imaginary thing?
Should I guard imports by an if typing.TYPE_CHECKING: condition if its only used for type hinting purposes throughout that particular module?
I don't think so
it's a horror to read and maintain
how so?
Pep 563 is accepted, but not enabled by default. Why?
Why this pep is not deferred?
Why?
this is a breaking change
In 3.10+ its by default ig
no, not even 3.11
and on 3.7+ there's from __future__ import annotations
no? i remember to have read that somewhere, was it cancelled?
In this case you will not be able to get real annotations, because there is no such module in namespace.
Everything else seems good.
I don't care about real annotations, I am not accessing them programmatically anywhere
(iirc) it was default in early 3.10. Then it was disabled in 3.10 and enabled in 3.11. Now it is deferred again and is not default in 3.11
- Now imports are split into two parts:
from .foo import Bar
#...
if typing.TYPE_CHECKING:
from .foo import Baz
``` which might be confusing if you're searching for where that damn `Bar` is used
2. If you decide to use something at runtime, it now requires changing imports, which is just unnecessary diffing (+extra merge conflicts)
3. You have to use `from __future__ import annotations` or use stringly annotations. Many people already default to that, but maybe you want the raw annotations
4. As far as the editor is concerned, it is a real import:
```py
if typing.TYPE_CHECKING:
from .foo import Bar
def function():
if rarely_taken_path() and path not in tests().because("yolo"):
baz = Bar() # pyright: LGMT!
# blows up at runtime
- If you need
Barin a type alias, you will have to use a string (yuck) or runtime-import it
Ohh right
Bad idea
Id prefer to import everything i need without guard except there is a import cycle or huge slowdown because of massive import
I should use it only to solve circular import issues ig
yeah TYPE_CHECKING is a bit of a hack
if TYPE_CHECKING:
from curses import _CursesWindow as Win
else:
Win = object
how can I solve this:
from attrs import define, field
from functools import partial
my_frozen: ??? = partial(define, frozen=True, slots=True)
attrs has type definition for define defined in pyi file and ideally I would want to reuse that
infact, frozen is defined by in attrs' own .pyi file just frozen=define
Is there someway I can reuse that definition?
How can I get TypeVar's type in __init__?
Also how to get type annotations for __orig_class__?
there's nowhere currently to read about it (that I know of) in python
I'm sure there's something on TS's docs about it though
you can't
yes it's annoying
I hope to one day if PEP 695 is accepted write a PEP allowing some better runtime typechecking stuff like that possible
what exactly do you mean by this?
If I inherit from a generic type which has a typevar repeated do i need to repeat while i use it?
class StdEnum(ct.Adapter[int, int, ET, ET]):
def _encode(self, obj: ET, context: Any, path: Any):
return obj.value
def _decode(self, obj: int, context: Any, path: Any) -> ET:
return self.__orig_class__.__args__[2](obj) # type: ignore
this is my class. Now due to some fuckups in the typings of this library, I cannot redefine __init__ without getting type errors
technically no but i think pep 695 encourages redefining generic parameters
oh wait no i misinterpreted what you were saying
Now I can construct a instance like this:
StdEnum(something) # no type errors, how?
StdEnum[some_enum_type](something) # no type errors
enable strict mode in your typechecker?
yes this
its like I can do stuff like c.Adapter[...] and no pyright errors, but it will throw runtime errors
what is reveal_type(StdEnum[some_enum_type](something))
need to check
use a conditional base depending on if TYPE_CHECKING
ie ct.Adapter[int, int, ET, ET] if TYPE_CHECKING else ct.Adapter
in the eyes of a type checker its a built in
although there is a runtime implementation in typing and typing_extensions
there is no typing.reveal_type in typing
its in 3.11 or maybe 12
Ok so
import construct as c
import construct_typed as ct
from typing import TypeVar, Any
from typing_extensions import reveal_type
ET = TypeVar("ET", bound=ct.EnumBase)
class MyEnum(ct.EnumBase):
A = 1
B = 2
class StdEnum(ct.Adapter[int, int, ET, ET]):
def _encode(self, obj: ET, context: Any, path: Any):
return obj.value
def _decode(self, obj: int, context: Any, path: Any) -> ET:
return self.__orig_class__.__args__[2](obj) # type: ignore
print(reveal_type(StdEnum[MyEnum](c.Int32ul)))
prints
Runtime type is 'StdEnum'
<StdEnum <FormatField>>
I don't think it's possible ๐ค
if typing.TYPE_CHECKING:
from attrs import frozen as my_frozen
else:
my_frozen = define(frozen=True, slots=True)
i meant what does your typechecker report?
the runtime stuff doesnt matter
Also i get this information?
that looks correct
but it doesn't happen in my library module
whats reveal_type(StdEnum(c.Int32ul))?
it happens only when i replicate in a diifferent file 
have you got a py.typed file?
what does it look like inside of your library?
ok wait hang on
reveal_type is like some magic
if it remove it, the blue line goes away
is it a good idea tho, to use generic types at runtime like this tho (in general)?
not if they cause your code to crash
why will it crash my code 
and yea this, pyright doesn't detect __orig_class__ attribute. is there like a protocol or something for this to get type hints for it? currently i need to use # type: ignore everytime i use it
i thought you said it gave you runtime errors?
nah that was due to inheriting the wrong class. turned out construct_typed had its own Adapter. hence i inherit from ct.Adapter
https://github.com/Gobot1234/steam.py/blob/main/steam/trade.py#L268 this this what i do, its not perfect
steam/trade.py line 268
__orig_class__: InventoryGenericAlias```
but "it works"
why isnt what in the stdlib?
hopefully in 3.12 ill be able to get rid of all of this garbage with TypeVar defaults
but __orig_class__ should probably be in the stubs for typeshed sometimes
but idk how it would be implemented
If a parameter can be dict or collections.Counter, is typing.Mapping the right way to type it?
Counter is a subclass or dict, so dict covers both
however, it's generally better to use abstract classes like Mapping in type hints
especially because dict is invariant in both of its type parameters
no, only classes that inherit from Mapping
e.g. types.MappingProxyType
def foo(Mapper):
return Mapper(...)
Mapper = dict if pred else collections.Counter
What should be Mapper's type be here?
!e
print(type(dict))
@brittle socket :white_check_mark: Your 3.11 eval job has completed with return code 0.
<class 'type'>
def foo(Mapper: type))? ๐ค
Huh, I didn't know this was a thing. TIL
Why Mutable though? To signal that the mapped values can be mutable?
And where do you get Type from? typing?
Yes, since both classes are mutable. You could do Mapping if you don't intend to modify them.
Yes, though as of 3.10 regular type can be subscripted - Mypy has had some trouble with that though so you may still need the typing version.
Right. I'm on 3.10 but also in Jupyter Notebook, and it doesn't like subscripting dict, list...etc. as typehints, probably won't for type either
Thank you!
An alternative type hint which would also work is typing.Callable[[], MutableMapping] - treating the types as functions which return instances.
Oh, that's interesting. Types are callables, duh
But I think I prefer Type[x] semantically speaking
It might not work in this case, since what __init__ does isn't usually defined by ABCs - defaultdict for example takes 2 args, not 1 like dict.
Oh. Darn it, you're right
Is [] in Callable[[], ...] shorthand for Any?
Or does it just mean no args
Thanks
Is there any way to copy a function signature to another function via typing without listing all of the parameters manually + docstring?
I've tried this which I found on the github/typing:
from package import foo
F = TypeVar('F', bound=Callable[..., Any])
class copy_signature(Generic[F]):
def __init__(self, target: F) -> None: ...
def __call__(self, wrapped: Callable[..., Any]) -> F: ...
class Bar:
@copy_signature(foo)
def baz(self, *args, **kwargs): ...
This almost works but the intellisense doesnt show the the first argument and when calling the function I get a type error:
TypeError: 'NoneType' object is not callable
you must provide implementation for copy_signature methods
i believe ParamSpec is what you'd use for this, it was introduced as a builtin in 3.10
https://docs.python.org/3/library/typing.html#typing.ParamSpec
is there any way to get mypy to recognize the ... as valid in a pydantic model?
class AccelerationTriple(BaseModel):
x: Acceleration | None = ...
y: Acceleration | None = ...
z: Acceleration | None = ...
i have enabled the pydantic.mypy plugin but apparently it's missing coverage for a lot of pydantic features like this one
error: Incompatible types in assignment (expression has type "ellipsis", variable has type "Optional[Acceleration]"
why cant you set the value to None?
i didnt want to allow default values. i am realizing that have refactored this enough since i first wrote it that my original concerns about default values are no longer relevant, so i gave up and just set it to None
still, this is a valid use case
i wish i knew anything about writing mypy plugins...
You could just do this:
NoDefault: Any = ...
class Whatever(BaseModel):
attr: list = NoDefault
how does assigning an Any to a list[Any] work?
wouldn't mypy complain?
Any shuts up any errors
fyi we now have https://peps.python.org/topic/typing/ listing all typing-related PEPs
Python Enhancement Proposals (PEPs)
i didn't think it would work in "the other direction"
it's compatible with everything both ways
Hey all, quick question...
I have written a pytest plugin for work which mocks some methods and properties for a class.
So for example I have a class Something and I added a method receive. When I use my fixture this method is monkeypatch-ed in with pytest so that I can call receive on the instantiated class and it does some stuff (not really important what).
My question is this; is there some way of making my linter happy? It (rightfully) complains that the class Something doesn't have the receive method as it only is added by monkeypatching, not before.
I wasn't exactly sure what channel to put this in sorry (it's not technically unit testing because I want to know more about how to make the linter happy, but happy to ask in a more appropriate channel)
is there any difference with unioning with none and optional
interestingly, Union[None, int] is exactly the same as Optional[int] at runtime, but int | None is different
Because Union and Optional are implemented in python in typing module, but int|None is implemented in C
Fun fact: there are two GenericAlias'es: in typing module and builtin type (types.GenericAlias iirc).
cursed moment
Not fun, completely messed up my reflection code
Especially not fun because they keep changing version to version
everything in typing seems to change from version to version
idk why they can't make it simple... like a uniform tree structure
it's just type stuff, after all
I like constant changes if it means improvement
Just please give me a changelog ๐ญ
I suppose I could do a git blame every time I do any reflection but I really don't wanna
There's no breaking changes if you don't rely on internals
And there are ways to avoid even things like name changes
i imagine it's because it's all still kind of work in progress
It feels like I'm going crazy! I have this workflow with mypy which I have been using for quite a long time, but I am starting from scratch now and I can't get rid of these mypy errors:
(.venv) $ mypy library
library/package/submodule.py:1: error: Cannot find implementation or library stub for module named "library.module"
library/package/submodule.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
I have
/library
โโโ module.py
โโโ package
โโโ __init__.py
โโโ submodule.py
In submodule.py:
from library.module import func
In module.py:
def func(x: int) -> int:
return x
I think you're supposed to have an __init__.py in library
otherwise it's a "namespace package" which you probably don't want
OMG you're right
I accidentally deleted it when I cleaned up everything
My hero ๐
Hey, I just wanted to ask, is this valid at all?
import typing
K = typing.TypeVar("K", bound=typing.Callable)
def do_something() -> str:
return "something"
def handle(f: K) -> K:
return f()
if __name__ == '__main__':
output = handle(f=do_something)
handle accepts Callable object, and I want to annotate that it returns the output of this callable
def handle(f: K) -> K:
return f
``` this would be valid
f() doesn't have the type of "K", it's something else.
That's what I thought
What you want is probably
T = TypeVar("T")
def handle(f: Callable[[], T]) -> T:
return f()
(also, since version 3.9 you need to import Callable from collections.abc instead of typing)
because some typing symbols are deprecated now
!pep 585
I wouldn't say "need"
In the current project I work on python3.8 still, but it's good to know, thanks
well, it's not very urgent
but they will be removed eventually, so I don't see the harm tbh
Importing those from typing is deprecated. Due to PEP 563 and the intention to minimize the runtime impact of typing, this deprecation will not generate DeprecationWarnings. Instead, type checkers may warn about such deprecated usage when the target version of the checked program is signalled to be Python 3.9 or newer. Itโs recommended to allow for those warnings to be silenced on a project-wide basis.
The deprecated functionality may eventually be removed from the typing module. Removal will occur no sooner than Python 3.9โs end of life, scheduled for October 2025.
I guess, yeah
it's honestly a bit of a pain
especially given that the only way to discover this deprecation is to read the PEP which... most people probably don't do in their free time
not if I can help it
I'll switch over when pyright decides it needs to warn me
and actually removing those will break a lot of otherwise perfectly fine code that worked since python 3.6 or whatever
(for context I'm one of the maintainers of the typing module)
I thought this was talking about removing typing.List instead of collections.abc.List
yeah
I agree, actually removing them will break a lot of perfectly fine code for little gain
wait
then how would it affect 2.7 if the typings module was added in 3.6?
right yeah it was a brain fart
it's about typing.List in favor of list, typing.Iterator in favor of collections.abc.Iterator, etc.
yeah
yeah.
there is a typing backport for 2.7 (no longer maintained)
assuming it's imported behind if False:
replaced 2.7 with 3.5 which is also quite ancient nowadays
well I meant, maybe there's some library which was made in 3.5 times and still works on 3.12
but it uses typing.List or whatever
true
oh and let's combine it with from __future__ import annotations becoming the default eventually โข๏ธ
that's unlikely to happen
I've seen the opposite where a library would work perfectly fine on 3.7, but it uses collections.abc.Mapping as a generic.
that is also very true
so what's the state of that? i haven't been following
will we have a __future__ flag that's not actually corresponding to something in the future? ๐
I guess it's more of a compiler directive
we'll likely end up with a version of PEP 649
!pep 649
Combine that with deferred imports
because you need eval and such to get the previous behaviour
are there any problems with PEP 649?
ah, circular imports
well, don't have those!
You don't have to worry about circular imports if you put everything in one file.

not if you import the module from itself
Not sure if this question fits the channel, but I'm having trouble with mypy's stubtest on custom typesheds. When running, it is unable to find any sources, causing everything to appear as missing. I've specified the --custom-typeshed-dir argument and it didn't complain so I assume I did it correctly. I can also reproduce the same result if I remove ignore_missing_stub from typeshed's test suite, which I believe to be incorrectly suppressing the issue
If I trace the error within stubtest's code, it gets to find_module_cache.find_modules_recursive, which always returns an empty list. I'm unable to jump into the source code of find_modules_recursive (or the init of that class for that matter), or get any breakpoints to trip. I assume there is some funky dynamic manipulation to get the cache stuff to work that returns a cached result instead of executing the function
Not sure if this is a mypy bug, typeshed bug, or a me bug
not sure about the full answer (pinging @hallow flint if he's around), but your debugger issues are likely because mypy is compiled by mypyc
debugging may be easier if you install the pure-python version of mypy (pip install --no-binary mypy)
One other thing to note is that stubtest runs properly if I install the typeshed package for this module with pip, at which point it produces different (correct) errors
That's a good lead, I'll try it now
Thanks
hm are you using --custom-typeshed-dir and testing third-party (non-stdlib packages)?
not 100% sure but I think that flag may only affect the stdlib now
Yes
Hmm, interesting, I'm still following the directions in typeshed's readme and the test script which does the same thing
The requirements pins mypy to 0.982, so I assume it should be fine?
Btw, debugging is now working, thank you
When searching the paths, modulefinder specifically looks for <package>-stubs, which will match the name of the package installed from pypi, but not the name of the local package
That part hasn't changed in 4 years, so I assume it's fine, but I'm wondering if I was supposed to install the local typeshed typings as a package before running this? Didn't see anything in the instructions indicating so
Turns out this might be a me problem after all. Running through the test script (which uses a different command from the one listed in the instructions), and manually removing the ignore seems to produce the correct errors. I thought I had tried that, so maybe I just didn't notice it working when trying it
The script sets MYPYPATH which seems to get stubtest to behave as desired
this is so annoying, is there a typed version of functools.cache somewhere that actually preserves the argument types?
since this only seems to preserve the return type
we can probably fix this in typeshed if we resurrect a version of https://github.com/python/typeshed/pull/7771
not yet
that would be nice
guess I'm stuck with this for now ```py
def _typed_cache(func: Callable[P, R]) -> Callable[P, R]:
return cast(Callable[P, R], cache(func))
still confuses me that param spec didnt actually address this rather large issue
seems like the difficulty is in supporting it on methods
yeah
ideally id like to see the day that FunctionType is generic in the same way as Callable
but it "just works" inside of classes
not sure that would help here since methods are still FunctionTypes
yeah its a similar-ish issue though
thoughts on having Unpack[dict[str, Any]] -> **kwargs in a callable without the need for a typed dict?
pyanalyze supports that. I haven't pushed for putting it in PEP 692 because I don't see much point
very nice
Is there anything like an "optional" type parameter to a generic class?
๐ฆ
It says this pattern is common in existing libraries, how?
Its not even implemented yet
will this make it to typing_extensions any time soon?
im saying it would be useful because currently these typevars go as unknowns
its in typing extensions as of yesterday (4.4.0)
Ooooo greatt ๐ฅ
though not in type checkers I believe (?)
I can use this for passing type param of TypedDict used in ctor with Unpack
i have a 99% functional mypy fork with it
Yea ryt ๐ฆ
what's missing?
support for respecialising type aliases, typevar tuple defaults and tests
How does mypy test itself with newer features btw 
Because the older versions will definitely error when it is given something new
yeah and it sees the errors go away with the newer features
Hi
Someone could help me
I create telegram bot using telebot (Telegram API), and i need to call url each time(url needed to be refresh after calling), but foretunately it doesn't change random photo
bot = telebot.TeleBot(config.TOKEN)
@bot.message_handler(commands=['start'])
def start(message):
#keybord
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
button1 = types.KeyboardButton("โฌ download music")
button2 = types.KeyboardButton("๐ผ get image")
markup.add(button1, button2)
bot.send_message(message.chat.id, "Choose your request:", parse_mode='html', reply_markup=markup)
@bot.message_handler(content_types = ['text'])
def excecution(message):
if message.chat.type == 'private':
if message.text == 'Hi' or 'Hello':
bot.send_message(message.chat.id, "Hi! {0.first_name}, let's begin.\nSend me command /start".format(message.from_user))
if message.text == "โฌ download music":
bot.send_message(message.chat.id,"I can't do this now")
elif message.text == "๐ผ get image":
photo_image = 'https://picsum.photos/200/300'
bot.send_photo(message.chat.id,(photo_image), parse_mode='html', timeout=3)
#bot.send_message(message.chat.id, "I don't understand.\nPlease, use buttons.".format(message.from_user))
#RUN
bot.polling(none_stop=True)
Is this related to type hints in any way?
If not, please see #โ๏ฝhow-to-get-help and claim a help channel
I have a system which looks like:
@dataclass
class BaseModel:
prop_1: int
Model = typing.TypeVar("Model", bound=BaseModel)
class Base(typing.Generic[Model]):
state: dict[int, Model]
def __init__(self):
self.state = {}
def func(self) -> None:
self.state[1] = BaseModel(prop_1=100)
The idea being subclasses of Base can define their own func, or leave it to the default. funcs of subclasses which use the subclass' Model will be type hinted correctly, but how could I type hint the base class?
With the above, mypy will complain about the assignment on the last line:
Incompatible types in assignment (expression has type "BaseModel", target has type "Model")
Or perhaps this needs to be redesigned in some other way
(the behavior itself is correct to the desired spec, just the typing issues)
youre going to run into variance issues here if you want this to be generic and read write
does this actually need to be generic especially if it just adds BaseModel to the state independent of whether its generic over BaseModel?
So, the idea here is that if a subclass is defining it's own model, it also has to redefine func
ah
This does make it kind of fragile, and if you miss defining func it'll cause errors
But I'm not sure how to best design this
could make it an abstractmethod
That's true, though if a subclass uses the same model, they don't need to redefine it
I guess that's the lesser of two evils
they can still call super().func() if they want the same behaviour
but i think if you have an abc/protocol version of this then you can subclass the abc and implement func where Model is constrained to BaseModel
@typing.runtime_checkable
class ProtoBase(typing.Protocol[Model]):
state: dict[int, Model]
def __init__(self):
self.state = {}
@abc.abstractmethod
def func(self) -> None:
raise NotImplementedError()
class Base(ProtoBase[BaseModel]):
def func(self) -> None:
self.state[1] = BaseModel(prop_1=100)
something like that
is there a way to use typevar with a typeddict
That doesn't seem to work
It says TypedDict is a type alias
This is my code
TD = TypeVar("TD", bound=TypedDict)
class A(Generic[TD]):
def __init__(self, **kw: Unpack[TD]):
...
oh wait you meant as a bound
you just need to py class A: def __init__(self, **kw: Unpack[TypedDictSubclass]): ...
Hi all, I've got the following boiled-down scenario:
from typing import TypeVar, Generic
A = TypeVar("A")
B = TypeVar("B")
class Base(Generic[A, B]):
valA: A
valB: B
class intAType(Base[int, B], Generic[B]): ...
class strBType(Base[A, str], Generic[A]): ...
class Both(strBType, intAType): ...
x = Both()
reveal_type(x.valA) # Type of "x.valA" is "Unknown"
reveal_type(x.valB) # Type of "x.valB" is "str"
Base is generic in A and B.
intAType is a Base, where A is int.
strBType is a Base, where B is str.
Both is both intAType and strBType.
I was hoping that pyright would be able to decipher that Both.valA is int and Both.valB is str, but it's only able to tell the type of Both.valB. If I swap the inheritance order on Both to be intAType, strBType, then only valA is inferred correctly.
Does anyone know a way that I can achieve this generic type inference? Is it even possible?
class Both(strBType[int], intAType[str]): ...
although that sounds like a really strange construct
could you show some context maybe?
I once coded something similar, but with no specialisation in Base subclasses. In this case (if you put typevars instead of int/str) it works, iirc
No the entire point I make A a generic class, is that otherwise I need to make a "fake" constructor with an annotation for the TypedDictSubClass
And idk if the implementation of typeddict + unpack is flaky yet, but pyright doesn't detect the type of kw like this as well.
class A:
def __init__(self, **kw: Any):
self._kw = kw
class B(A):
def __init__(self, **kw: Unpack[TypedDictSubClass]):
super().__init__(**kw)
b = B()
reveal_type(b._kw) # Any!!!!!
@soft matrix
It's not flaky this won't ever be supported
It would need to be generic for this to work
But you're better just not having a base class here
That's not possible unfortunately
You mean **kw: Unpack[TypedDictVar]?
@runtime_checkable
class ModelCollection(Iterable[MT_co], Protocol[MT_co]):
@overload
def __getitem__(self, i: int | str) -> MT_co:
...
@overload
def __getitem__(self, i: slice) -> Sequence[MT_co]:
...
def getslice(func: Callable[[ModelCollection[MT_co], str | int | slice], MT_co]):
"""Wraps a :meth:`ModelCollection.__getitem__` to return a sequence if required."""
@functools.wraps(func)
def wrapper(self: ModelCollection[MT_co], i: str | int | slice):
if isinstance(i, slice):
return [
model
for model in self
if getattr(model, "__index__")() in range(i.start, i.stop)
]
return func(self, i)
return wrapper
class ChannelRack(MultiEventModel, ModelCollection[Channel]):
@getslice
def __getitem__(self, i: str | int | slice):
...
The line at getslice raises the error:
Argument of type "(self: Self@ChannelRack, i: str | int | slice) -> Channel" cannot be assigned to parameter "func" of type "(ModelCollection[MT_co@getslice], str | int | slice) -> MT_co@getslice" in function "getslice"
Type "(self: Self@ChannelRack, i: str | int | slice) -> Channel" cannot be assigned to type "(ModelCollection[MT_co@getslice], str | int | slice) -> MT_co@getslice"
Parameter 1: type "ModelCollection[MT_co@getslice]" cannot be assigned to type "Self@ChannelRack"
"ModelCollection[Channel]" is incompatible with "ChannelRack"
Decorators are tough to type hint ๐ฆ and if I just use Any for them, they "erase" the type annotations of the functions or classes they are used on
ok nvm
fixed it by putting some extra overloads and Anys
What would be the equivalent of this in Python?
(Typescript):
type URLType = "https://xyz.com" | "https://xyz1.com" | "https://xyz2.com"
const showUrl = (url: URLType) => {
return url
}
basically, the union type like this defined separately.
I know I can do this:
def (url: "https://xyz.com" | "https://xyz1.com" | "https://xyz2.com"):
print(url)
but i feel like for longer union types or more complex types like this, defining everything the function param is not very much readable and not reusable
typing.Literal```
A type that can be used to indicate to type checkers that the corresponding variable or function parameter has a value equivalent to the provided literal (or one of several literals). For example:
```py
def validate_simple(data: Any) -> Literal[True]: # always returns True
...
MODE = Literal['r', 'rb', 'w', 'wb']
def open_helper(file: str, mode: MODE) -> str:
...
open_helper('/some/path', 'r') # Passes type check
open_helper('/other/path', 'typo') # Error in type checker
```...
Oh, damn didn't know. Gotcha, thanks. And yea I should use Literal here
Optionally you can also annotate that variable as a typing.TypeAlias, to make it explicit it's a type hint and not a regular variable - then checkers can comfortably warn about errors there.
consider making an Enum instead of literals IMO
Especially if your value is like... a full-blown url
is there a known way to make a type for a Sequence of up to n things ? 6 strs in my case
My current solution is this monstruosity:
up_to_six_strs_type = tuple[str] | tuple[str, str] | ... | tuple[str, str, str, str, str, str]
No, I'm afraid not
the alternative of sending a tuple of n optional things sucks, consumers of that type have to filter the optional items
you can overload getitem for literal ints 0-5 if you're desperate, or making a whole load of tuples, but there's no native integer generics in the type system
not yet at least! Ok I see, thanks
you mean overload getitem for a custom tuple / seq class ? hah didn't yet think of that, thought I could keep it inside the typing hint system. interesting, yeah!
It wouldn't allow for n-length unpacking or using non-literals as indices but it might work in a pinch
Why do you want this though?
Some context would help
I have a task where we encode parts of many kinds into a part code. each kind can have up to 6 attributes used for the encoding
so those up to 6 values are sent around between functions. I wanted to add type hinting to these encoding functions
Could you maybe provide some example for this?
it's a cursed part number encoding task
we have a datamodel for metallic parts of many kinds, like pipes, cylinders, square profile bars and so on
those each have their significant geometrical properties (round things have diameter, inner and outer if empty - square things have side length, rect profile things have 2 side legths. plus inner if empty), length, metallurgical properties like steel grade
up to 6 are significant for the part number encoding
So, these are represented by some classes?
yes, sure
Probably just make a class like Model, that all of these inherit and type-hint the inputs as models
I already have a PartNumber class, with the values of the up to 6 attributes
What do you mean up to 6 attributes
but I wanted to see if I could do it with a more basic type
Are some of these attributes not required?
yes, again, the part number encoding function uses up to 6 attributes, depending on the part type
can "something with 5 attributes" mean many different things?
sure, dfiferent part types can all have 5 attributes
a round and square steel bar are different part types, but they'll have the same number of significant attributes for the part encoding task
So you're trying to typehint the fact that these 6 variables, if present, will have certain specific types?
oh no
up to 6 from a mixed bag?
yeah honestly you'd be better off with a runtime assertion
maybe with an optional TypedDict for the input data depending on how you get it ๐
How are these attributes stored? They're different model classes and these are literal attributes? Or do you have like a list of length up to 6 for them?
sure, I wanted to try using the typing system
Yeah a typed dict sounds like something you could utilize
yeah there's a database with different model classes etc
each encodable part type has a part specific encoder function that takes the significant attribute values, does attribute encoding, and returns this list of up to 6 attribute values that I'd like a type hint for
class MaterialAttributes(TypedDict, total=False):
roughness: int
...
with something like this, you could then store the attributes in a simple dictionary, like attributes = {"roughness": 5, "rigidity": 10, ...} and mark that attribute as MaterialAttributes
the total=False means that not all of the listed attributes need to be present, but if they are present, they will be of specified type
what you could then do is something like: ```py
def from_part_number(part_no: str) -> MaterialAttributes:
...
I say the current state is as far as you can go with your current part system
and that's fine tbh
I'm still not entirely sure that this is what you wanted though, so correct me if you wanted something different
ah wait so if I get this right the list of attributes of this MaterialAttributes thing is the union of the list of attributes of all different part types ? yeah that could be hundreds, not up to 6
misunderstood your earlier question
yeah I think so too
it's not necessarily a union of attributes, it's just showing that the returned type will be a dictionary, which will have certain types for keys with specific names
but yeah for hundreds of possible attributes, it might just be easier to avoid this completely
sometimes you just shrug it off, use the less precise type hint and prioritize other parts of your code ๐
if you just wanted to mark that there will be up to 6 values returned, you could do that with a union of differently sized tuples, but it's probably not worth it
UpTo6MaterialsType: TypeAlias = tuple[str] | tuple[str, str] | tuple[str, str, str] | ...
def get_material_types(...) -> UpTo6MaterialsType:
...
depending on how your function works, it might make sense to split it somehow
see here
or if you don't really care that it's always at most 6, you can just do tuple[str, ...] to mark that it's just a tuple containing any amount of strings
yeah that's possible actually
we usually don't really care about the length of a sequence in these cases when it comes to typing, so this is probably a better solution and it's much more convenient to type out than having a union like that
Is there some pre-made protocol class like SupportsEq (i.e. object that defines __eq__) or do I need to make one myself?
object
oh, didn't know objects support it by default, interesting, I'd like something that's generic though, i.e. SupportsEq[MyType] showing that it supports comparisons against MyType
hm? why would it not be?
Never mind, works fine
Generally you shouldn't need this, since equality comparisons between unrelated objects should fall back to always returning False. The ordering methods aren't present on object though, so it makes sense there.
The _typeshed module does have protocols, but using that outside stubs is a bit awkward:
https://github.com/python/typeshed/blob/master/stdlib/_typeshed/__init__.pyi#L53-L70
Sigh. Converting from { "foo": Hello[int], "bar": Hello[str] } to World[{ foo: int, bar: str }] and such not being possible is a pain
I am spoiled by TypeScript!
Sometimes you can sorta do it with chained calls and TypeVarTuple but it's kinda scuffed
Of the sort t[u[a]] -> u[t[a]]?
I think it's like the Map idea
But for dicts
Although Map would already be good
The use case is basically for when you want to parse a nested structure, so you define a parser declaratively using a dict mapping keys to other parsers
Or stuff like SQLAlchemy where you define tables with a dict
hm, am I missing something? Why does pyright report an issue here?
T = TypeVar("T")
T_contra = TypeVar("T_contra", contravariant=True)
U_contra = TypeVar("U_contra", contravariant=True)
class SupportsEq(Protocol[T_contra, U_contra]):
def __eq__(self: T_contra, __o: U_contra) -> bool:
...
def foo(x: T, a: SupportsEq[object, T]) -> None:
...
foo("hi", "hi") # Argument of type Literal["hi"] cannot be assigned to parameter "a" of type SupportsEq[object, T@foo]
Anyone knows when mypy will get PEP646 support?
there's work in progress on it, there's some limited support
no definite timeline though
sorry, didn't get to this yesterday - yeah it'll have to be that. It makes sense, of course the generic type parameters for two classes have to be completed unrelated to one another, even if they have the same name
The context needs quite a bit of an explanation - I have a base class and decorators that let me define properties/methods as discord embed attributes. Currently only the colour of the embed and fields that should appear:
class User(EmbedFillableMixin):
@embedField(showLast=True)
@property
def id(self): return str(self._id)
@embedColour
def embedColour(self): return Colour.blue()
EmbedFillableMixin adds a _embedAttributes: List[_BaseEmbedAttribute] class attribute onto the class:
class _BaseEmbedAttribute(Generic[TUnderlyingMethod, TMethodOutput]):
class _PropertyEmbedAttribute(_BaseEmbedAttribute[property, TMethodOutput], Generic[TMethodOutput]): ...
class _EmbedColourAttribute(_BaseEmbedAttribute[TUnderlyingMethod, Colour], Generic[TUnderlyingMethod]): ...
class _PropertyEmbedColourAttribute(_EmbedColourAttribute, _PropertyEmbedAttribute): ...
Here _PropertyEmbedColourAttribute is going to have to be _PropertyEmbedColourAttribute(_EmbedColourAttribute[property], _PropertyEmbedAttribute[Colour]):
I'm not sure why I'm spamming the channel with all this when I understand why it has to be the case, but here we are 
Are overloads not inherited?
Not when the function is overwritten
hmm, why is that so
I mean, not even the signature is kept if it's overwritten
So why would overloads
not saying it shouldn't, just that it wouldn't at all be consistent
i really wish it was kept by default
feel like that makes adding new parameters hard
how would you determine when def f(*args, **kwargs) is saying to keep the signature and when it actually does want to use *args and **kwargs
best I can think of is adding a decorator that says the function should copy the signature of its parent
yes, or even just overriding a function that modifies behavior but doesn't change any parameters
it's incredibly wasteful and makes it's really hard to "correctly" override methods with complicated type signatures
that or maybe i'm just doing something wrong
Sometimes I want to mimic typescript's function foo( { /*...*/ }: FooProps) and use def foo(**kwargs: Unpack[FooProps]) ๐
but then I remember TypedDict intersections (or structural type intersections in general) aren't really a clean thing either
Hi, I am trying to annotate a callable that could have different parameters types.
I've tried something like this:
from typing import Callable, Type
def my_func_1 (var_a: str, exception: Exception)
def my_func_2 (var_a: str, exception: SomeCustomException)
def my_func_3 (var_a: str, exception: AnotherCustomException)
def my_func_4(fn: Callable[[str, Type[Exception]]) # this can be my_func_1, my_func_2 or my_func_3
Any resource or guide on how this can be properly implemented?
it's Callable[[arg1, arg2, ...], return] so fn: Callable[[str, Exception], object] if you don't care about the return type
ah sorry kind of a late reply
Thank you for your reply ๐ . If I use Callable[[str, Exception], int] mypy still complains since my_func_2 and my_func_3 have a different second argument type than Exception
ah I see, that seems to be an issue with covariance, you want it to be contravariant
Although I genuinely have no idea how to make anything other than a typevar be covariant/contravariant
could you elaborate a little bit on typevar?
It's the other way round isn't it?
ah damn
I mess this up every time
wait no, wouldn't this get close to working?
ExceptionT = TypeVar("ExceptionT", bound=Exception, contravariant=True)
def func(fn: Callable[[str, ExceptionT], int]) -> object: ...
though I have no idea how to make this not use a typevar
contravariant=True on a TypeVar is meaningless unless it's used as a class parameter
makes sense
but with just the bound this might do approximately what you want
yeah but then mypy would complain it's used only once right?
Can you show an example of how you'd use that?
Suppose that you have this setup:
class Animal: ...
class Cat(Animal): ...
class Dog(Animal): ...
def for_each_animal(animal: Callable[[Animal], None]) -> None:
...
Do you think for_each_animal should accept Callable[[Dog], None]? Should it accept Callable[[Cat], None], Callable[[Animal], None], Callable[[object], None]?
pretty sure I've done similar things when making error handlers and such but I forgot how I managed to make it work ๐ค
Essentially, the problem is: how does my_func_4 know what exception fn expects?
Does the return of __init__ have to be typed?
Yeah ideally you should do __init__(...) -> None
my gosh you're right it just hit me, damn
otherwise mypy by default will treat it as "untyped" and just not bother checking it
Okay, it just seems unnecessary
This is a more complete snippet of my code
class SomeCustomException(Exception):
pass
class AnotherCustomException(SomeCustomException):
pass
from typing import Callable, Type
def my_func_1 (var_a: str, exception: Exception)
def my_func_2 (var_a: str, exception: SomeCustomException)
def my_func_3 (var_a: str, exception: AnotherCustomException)
def my_func_4(fn: Callable[[str, Type[Exception]]) # this can be my_func_1, my_func_2 or my_func_3
my_func_4(fn=my_func_1)
my_func_4(fn=my_func_2)
my_func_4(fn=my_func_3)
But how does my_func_4 know what kind of exception to pass to var_a?
my_func_4 is called multiple times on an web app startup
what you show is basically:
def f1(e: ValueError) -> None: ...
def f2(e: RuntimeError) -> None: ...
def f3(e: IndexError) -> None: ...
def my_func_4(fn: "f1 or f2 or f3") -> None: ...
``` I don't see how this makes sense tbh -- how does `my_func_4` know what exception to use on a given function? I think your example is incomplete
something like this
from my_namespace import my_func_1, my_func_2, my_func_3
def my_func_4(fn: Callable[[str, Type[Exception]]) # this can be my_func_1, my_func_2 or my_func_3
def init_app()
my_func_4(fn=my_func_1)
my_func_4(fn=my_func_2)
my_func_4(fn=my_func_3)
Can you show the actual code in my_func_4?
You could set up mypy to check "untyped" definitions like this one.
The reason it is this way: Python's typing system is gradual. Meaning, if you have 2 million lines of Python, you can't just add types to everything in one sitting. You would add types gradually (hence -- "gradual typing"). It would be a real pain if mypy checked those 2 million lines of Python written in 2022 B.C. without any considerations for type checkers, it would just be millions of errors. So by default mypy only checks functions with type annotations: ```py
def f(x: int) -> str: # checked
...
def untyped(whatever, **have_fun): # not checked
...
so if mypy sees something likepy
def init(self):
self.foo = 1
``` it thinks it's an untyped function. You can remove this behaviour with the --check-untyped-defs flag
This is the actual code, app is a FastAPI instance
def my_func_4(self, fn: Callable[[str, Type[Exception]]) # this can be my_func_1, my_func_2 or my_func_3
sig = signature(fn)
self.app.add_exception_handler(sig.parameters["exception"].annotation, fn)
Oh, so you're inspecting the annotation. That's an important bit
Why do you need my_func_4 though? Just do ```py
self.app.add_exception_handler(SomeCustomException, my_func_2)
I want to separate the exception handlers adding from the startup process, so I have multiple functions each doing something specific for building the app
hm?
I don't quite get you
it sounds like we're missing some important bits of context here, maybe you could explain more about what you're doing, with some code?
sure, I will give more context, just a moment
This is a closer example of what I am trying to do, there is an AppBuilder class which instantiates a FastAPI app and configure it. The app is configured using multiple methods that proxy FastAPI methods for configure mutiple settings of the FastAPi app, the functions my_func_1, my_func_2, my_func_3 are being imported as I stated before. Hopefully this is clearer.
class AppBuilder:
def __init__(self, config) -> None:
self.settings = config
self.app: Union[FastAPI, None] = None
def _include_routers(self) -> None:
self.app.include_router(api_router_v1)
self.app.include_router(api_router_v2)
self.app.include_router(api_router_internals)
def _include_middlewares(self) -> None:
self.app.add_middleware(CORSMiddleware)
self.app.add_middleware(CustomWebSecMiddleware)
self.app.add_middleware(AnotherMiddleware)
def _setup_logging(self) -> None:
pass
def _include_exception_handlers(self) -> None:
self._register_exception_handler(handler=my_func_1) # mypy is complaining here
self._register_exception_handler(handler=my_func_2) # mypy is complaining here
self._register_exception_handler(handler=my_func_3) # mypy is complaining here
def _register_exception_handler(
self, handler: Callable[[Request, Exception], Coroutine[Any, Any, JSONResponse]]
) -> None:
sig = signature(handler)
self.app.add_exception_handler(sig.parameters["exception"].annotation, handler)
def build(self) -> FastAPI:
self.app = FastAPI()
self._include_exception_handlers()
self._include_middlewares()
self._include_routers()
self._setup_logging()
return self.app
so why can't you do self.app.add_exception_handler(...) in _include_exception_handlers?
Inspecting the signature is generally pretty brittle and should be used as a last resort.
- If you attach a decorator to
my_func_N, it will likely break - If you use
from __future__ import annotationswheremy_func_Nis defined, it will likely break - If
my_func_Naccepts aUnionor something more complicated (evenAny), it will definitely break
- You can't use a general exception handler for a more specific exception. For example, you might want to handle
CatExceptionandDogExceptionwith a handler ofAnimalException, but use a separate handler forOwlException.
I am inspecting the handler functions to get the exception type so thought it was cleaner just have a different method and not inspecting each handler inside _include_exception_handlers. But based on your point I think it would be simpler and clearer just use _include_exception_handlers passing the actual exception type and the corresponding handler
It is still confusing to me - regardless of my problem - if there is a way to annotate callables with different arguments types, as I was trying initially
unions work for that, but it's hard to come up with examples where this produces useful, type-safe code
To answer your question, the reason why mypy is complaining is because functions are "contravariant" in their parameter types (https://decorator-factory.github.io/typing-tips/tutorials/generics/variance/)
So, for example, a fn: Callable[[Exception], None] will be accepted where Callable[[ValueError], None] is expected.fn can accept any Exception, so it can definitely accept a ValueError.
However, a fn: Callable[[ValueError], None] will not be accepted where thing: Callable[[Exception], None] is expected. fn only knows how to work with ValueErrors, but the code accepting the thing might do thing(Exception()) or thing(IndexError()).
generally, you can't "know" what particular type the passed in callable expects
I think the general pattern is to pass some key of what the function can handle together with the function. e.g.:
class EventKey(Generic[T]): ...
E_RESIZE = EventKey[tuple[int, int]]
class Widget:
...
def add_event_listener(self, key: EventKey[T], handler: Callable[[T], None]) -> None:
...
with exceptions you can just use the type ```py
E = TypeVar("E", bound=BaseException)
def on_exception(exc_type: type[E], handle: Callable[[E], Response]) -> None:
...
with bound=BaseException the checker will infer any subclass of BaseException, is this correct?
if that is correct what is the unbounded (no bound argument) behaviour for TypeVar ?
It will prohibit doing stuff like on_exception(int, print)
so that you can rely on the fact that E is a subtype of Exception in the function
I think you could say that the unbounded behaviour is equivalent to bound=object but @oblique urchin might correct me
I think that's right
since every type is a subtype of object
I see, now it is clearer for me the usage of TypeVar . I think gotta need to read the typing hints section in python docs
Yeah there's quite a lot of material... it's a bit all over the place tbh.
- docs.python.org
- documentation for mypy: https://mypy.readthedocs.io/en/stable/
- the actual reference is scattered across a lot of PEPs
- my attempt at some useful articles @ https://decorator-factory.github.io/typing-tips/, includes a section on type vars
dont forget https://typing.readthedocs.io
oh yes...
;)
It's not all over the place, it uses the new "distributed documentation" architectural pattern ๐คช
lol
seriously guys thanks all of you for your patience, I learnt a lot from you and sorry for my poor english
that's what we're here for ๐
I think your english is fine. Most people here are not native English speakers
it is motivating for me and google translate to read that, anyway thanks!
hi... so I know I can specify what kind of thing a function returns, with an arrow thingy, like: def foo() -> str:
how can I say that foo takes as arguments, a string called s1, an integer called n1 and another string called s2?
do you use a colon, like: s1: str?
yes
so then the whole signature would be: def foo(s1: str, n1: int, S2: str) -> str: ?
yes
good enough; thanks a lot ๐
there is one more thing... say I wanted to take a more complex object as a parameter... howbout a request, assuming I have no imports yet in that file, how would I take a request?
def func_that_takes_a_request(r: request) -> dunnowhatIdreturn:
this isn't enough I guess, because python hasn't seen the definition of request
what kind of request?
@trim tanglehi...
You can use any class in a type annotation, so it's something like ```py
from starlette.requests import Request
def do_something(request: Request) -> None:
...
howbbout the kind of request used in a django view function?
According to the docs:
https://docs.djangoproject.com/en/4.1/topics/http/views/
these functions accept a django.http.HttpRequest object:
https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest
so you'd do
from django.http import HttpRequest
``` and use `HttpRequest`
so... def funcName(request: HttpRequest):
yes
ok, in those examples, the name of the class is used as the type name
indeed, just like with int and str
but int and str are names that are already present in any python3... if not, you import the class
I have an enum:
class AEnum(enum.IntEnum):
A = (1, a_type)
B = (2, another_type)
How do I make a TypedDict from this enum such that when I have a dict of AEnum keys and object values, the type of object will be inferred as the one given in AEnum?
I don't think you can, why do you want that?
As i said I have a dict of AEnum keys and objects are of type as defined in the enum
Also enum.IntEnum is the wrong base class here, you wouldn't be able to use an int
and why do you need a TypedDict?
I have to cast everytime making it a pain
Its for a demonstration purposes I have my own metaclass and its subclass which AEnum derives from
I honestly think you should first do a typed dict instead of an enum
If you know the key, then just use a_type directly. If you don't, well, then you don't know what it is
Wdym
Can you show an example of how you'd use your setup?
How can I annotate an integer value with any arbitrary type?
Hangon
Pretty sure Foo = TypedDict("Foo", {1: str}) works but idk
Here is my enum metaclass and its subclass
Actually wait I think it has to be Mapping[str, T] so idk
https://github.com/demberto/PyFLP/blob/f1f9621251f994762a9dcf2788ecb9539be5aae3/pyflp/channel.py#L307
Here is how a EventEnum looks like
Here is how I get the type from an enum member
https://github.com/demberto/PyFLP/blob/f1f9621251f994762a9dcf2788ecb9539be5aae3/pyflp/__init__.py#L137
pyflp/__init__.py line 137
event_type = getattr(enum_type(id), "type")```
Where will I store the name of the enum member?
Is there a better โข๏ธ enum library for python
i would love the ability to "derive" a typeddict from a class and its attribute annotations
Is there some way to type this with TypeVarTuple/Unpack to preserve the type information and order for the returned instances?
def f(*types):
return tuple(klass() for klass in types)
I'm not sure how I'd stick in the type in there to get a different return type but one that's based on the type var
yeah
It's supposed to be coming some time later this year
without Map PEP 646 is kinda... weak, idk
like yeah, you can have a tuple of things
but you can't do anything useful from it besides accepting that tuple and returning it
If it has bound and variance it'd be a lot better imo
Building on Pyright's Exhaustiveness checking to make ADTs and a Result monad:
- ADT: https://github.com/dogweather/python-exhaustiveness-adts-monads/blob/master/src/adt_1_order_status.py
- Result type: https://github.com/dogweather/python-exhaustiveness-adts-monads/blob/master/src/adt_2_result.py
- Result monad: https://github.com/dogweather/python-exhaustiveness-adts-monads/blob/master/src/adt_3_result_monad.py
Seems pretty Pythonic to me still. The types are actually mostly dynamic.
tldr; By using a Result, Pyright can flag when you forget to check for errors.
Why not make Ok and Err generic?
How does marking a type alias as TypeAlias help?
Might as well do
str_or_int = str | int
it helps if you want to use a string, for example
# oh damn, we're on Python 3.6
str_or_int: TypeAlias = "str | int"
Python 3.6 is getting dumped universally anyways ๐
But yea, it can be used for a type declared later than the alias
!pep 613
How would I type a weakref.ref?
weakref.ref[int]
same as weakref.ReferenceType[int]
well, weakreffing an integer is silly but you get the point
Is there a way I can overload using a specific argument type -> return type?
For example:
class AnimalType(Enum):
DOG = 1
CAT = 2
HORSE = 3
@overload
def get_animal_by_id(animal_id: int, animal_type: AnimalType.HORSE) -> Horse:
...
@overload
def get_animal_by_id(animal_id: int, animal_type: AnimalType.CAT) -> Cat:
...
def get_animal_by_id(animal_id: int, animal_type: AnimalType) -> AnimalObject:
...
cat = get_animal_by_id(1, AnimalType.CAT) # Not recognized as just Cat
yes, use Literal[AnimalType.HORSE]
I tried that as well, still did not work.
what does your updated code look like?
@overload
def get_animal_by_id(animal_id: int, animal_type: Literal[AnimalType.HORSE]) -> Horse:
...
@overload
def get_animal_by_id(animal_id: int, animal_type: Literal[AnimalType.CAT]) -> Cat:
...
This?
It still sees it as Horse | Cat and shows Horse methods
is that pycharm?
pycharms typechecker is notoriously incomplete
pycharm isn't very good at this
Yeah
Hmm
Is there a way to integrate a better checker into it or is there an alternative?
well, afaik the only real alternative autocompletion thing is pylance
vscode comes with pyright which is generally better. There may also be plugins for using mypy with pycharm, not sure
well, mypy doesn't do suggestions
there is a mypy plugin but yeah its not hooked up to the suggestion engine
class A(enum.IntEnum):
A = (1, a_type)
How can I make this enum a TypedDict (or something else)
I have a ton of this enums I would really like to make them infer a_type when A.A is used as a key or in a function argument
enums are special cased in every typechecker i know of so doing this the way you seem to want isn't possible, so here are a couple of questions i think you should ask yourself
- why is this an IntEnum, tuples are not integers
- enum members are ordered in every version the module is present (definitely 3.6+), so why does this need the enum number. If the numbering isnt sequential you could add a .type property which just gets the type you want
- why do you want special behaviour when a member is used as a key or function argument, i think that will be rather surprising to a lot of people
- IntEnum was just for demonstration, its actually a custom enum class I made https://github.com/demberto/PyFLP/blob/e30238bcf6abbccc0d6e43f7cd608547263d0853/pyflp/_events.py#L85-L99
- Enum members are fragmented into various enums, not sequential.
- These enums are for event IDs and the classes which need them use a dictionary or list of events to lookup these events by their ID. If found, I need to
typing.castthem most of the times.
The enums have a type because it used by the event parser to choose the correct type for the event. The enum class and the metaclass above it confuse the type checker a lot already ๐
@soft matrix
i think your best option is a type property
and a lot overloads on that property
oh wait that might not be possible
you might have to stick with a very broad type in that case
I am doing that rn unfortunately
