#type-hinting
1 messages ยท Page 77 of 1
Yep
Wait no
Maybe: TypeAlias = "Just[T] | Nothing"
Just don't try and subscript this at runtime
from typing import TypeAlias, TypeVar
from src.maybe import Just, Nothing
T = TypeVar("T")
MyBe: TypeAlias = Just[T] | Nothing
def main():
def foo_or_bar(fbar: str) -> MyBe[str]:
if fbar in ("foo", "bar"):
return Just(fbar)
return Nothing()
match foo_or_bar("foo"):
case Just(v):
print(f"{v}!")
case Nothing():
print("Aw shucks!")
if __name__ == "__main__":
main()
Ok, this works
I'm using MyBe cause technically there's a use for Maybe still
which i probably could just rewrite into a simple function instead of class method...
btw, can you import type aliases?
yes, they're just variables at runtime
well, pyright doesn't seem to want to use it for type checking
I can define it myself just fine though
and I use it plenty in the maybe.py file :v
Try searching for similar issues on pyright. Consider creating a new one if you can't find anything, ideally with a minimal example.
i'll give it a look tomorrow
Is there a generic version of enum? I'm looking for something where I could specify what type the enum values will be of
class MyEnum(Enum[str]):
A = "some string"
B = "some other string"
you can do
class MyEnum(str, Enum):
A = "some string"
B = "some other string"
oh, do know it applies for str
but you could probably do it with Generic[T]
well, do try it
I'm pretty sure the Enum has to go last in the parent class declarations though
hm, doing reveal_type(instruction.value) still gives any
(instruction being a specific enum value)
I wonder if just cheating it by ```py
class MyEnum(Enum):
value: ReadInstructionValue
A = ReadInstructionValue(...)
B = ...
well i know, but i didn't want to use dict, since I wanted my keys to be specific, and yeah, I know about TypedDict, but I have a lot of keys (~100) and I'm not exactly eager to write a full typeddict specifying all of them, just to define the dict as a constant below
I also much prefer the x.A access then x["A"]
with paramspec is there some way to give the arguments names while keeping it tied to the paramspec, or do I have to just get it from args?
I have
def _cache_node_find(func):
# type: (t.Callable[P, T]) -> t.Callable[P, T]
def wrapper(*args, **kwargs):
# type: (P.args, P.kwargs) -> T
return func(*args, **kwargs)
return wrapper
and want to do something like this
def _cache_node_find(func):
# type: (t.Callable[P, T]) -> t.Callable[P, T]
def wrapper(start_node, *args, **kwargs):
# type: (P.args, P.kwargs) -> T
return func(start_node, *args, **kwargs)
return wrapper
you could use Concatenate
def foo(func: Callable[Concatenate[int, P], R) -> Callable[Concatenate[int, P], R]:
def wrapper(something: int, *args: P.args, **kwargs: P.kwargs) -> R:
...
but you can't do much for kw args
yes, but afaik there's no way to specify arg names with concatenate, I think it was a rejected proposal from the PEP
You might be able to do this with mypy using mypy_extensions.NamedArg but other than that you're out of luck
yeah, this is the rejected proposal https://peps.python.org/pep-0612/#concatenating-keyword-parameters
Python Enhancement Proposals (PEPs)
this is interesting, I had no idea mypy had support for it
Will need to change a few things but it's better than nothing, thanks
reveal_type(int)
in mypy i get this:
Revealed type is "Overload(def (Union[builtins.str, builtins.bytes, array.array[Any], mmap.mmap, ctypes._CData, pickle.PickleBuffer, typing.SupportsInt, typing_extensions.SupportsIndex, _typeshed.SupportsTrunc] =) -> builtins.int, def (Union[builtins.str, builtins.bytes], base: typing_extensions.SupportsIndex) -> builtins.int)"
why there is single equals sign in first overload?
def (Union[...] =) -> builtins.int
isnt this a bug?
it indicates there is a default
from typing import overload
def f(x: int = ...) -> int: ...
reveal_type(f)
# Revealed type is "def (x: builtins.int =) -> builtins.int"
``` hmm
i expect to get def (x: builtins.int = ...) -> builtins.int
testlst: list[int | set[int]] = [1, 2, {3, 4, 5}]
for n, _ in enumerate(testlst):
if isinstance(testlst[n], set) and len(testlst[n])==1:
pass
VSC's Pylance gives the testlst at the right (being len'd) a red type squiggle, saying Argument of type "int | set[int]" cannot be assigned to parameter "__obj" of type "Sized" in function "len", meaning that it doesn't know for sure that that testlst[n] is a set, despite the type check right before. I can replace the n with a literal number (e.g. testlst[2]), however, and it doesn't complain. Is there a way to fix this by making pyright realize the objects are the same?
It's technically correct, because it's possible testlist[n] returns a different object each time - it can't know for certain that it behaves like you want here.
Use the _ variable you're ignoring instead, it can type narrow local variables.
would if i could - i modified the original code to create a minimum workable example. the original iterates through a 2D list (add another list to the front of testlst's annotation, and they're the same type), modifying values as needed. And the iteratee of a for loop is merely a reference, so changing it doesn't affect the underlying values
In that case cache testlst[n] in a variable, you could use a walrus even:
if isinstance(testset := testlst[n], set) and len(testset)==1:
i should post the original instead of the example
def _commit_candgrid(candgrid: candgrid_type, solngrid):
for (r, c), _ in np.ndenumerate(solngrid):
if solngrid[r][c]==0 and isinstance(candgrid[r][c], set) and len(candgrid[r][c])==1:
candgrid[r][c] = candgrid[r][c].pop()
solngrid[r][c] = candgrid[r][c]
return candgrid, solngrid
candgrid_type is list[list[int | set[int]]],
solngrid is an numpy.ndarray, essentially list[list[int]]
def _commit_candgrid(candgrid: candgrid_type, solngrid):
for (r, c), _ in np.ndenumerate(solngrid):
if solngrid[r][c]==0 and isinstance(grid:= candgrid[r][c], set) and len(grid)==1:
candgrid[r][c] = grid.pop()
solngrid[r][c] = candgrid[r][c]
return candgrid, solngrid
By doing the lookup only once, the type checker can be certain the other uses are the same object.
walrus operator is a very nice solution. i should've thought of it. thank you @pastel egret
Your original is correct, but only because us humans know the collection is always going to give back one type, the type checker can't really know that.
after more fiddling around, it turns out to not be pyright's fault, i just need to reload my lsp for it to properly see that imported type alias
from src.maybe import MH, MU, Just, Maybe, Nothing
def foo_or_bar(fbar: str) -> "Maybe[str]":
if fbar in ("foo", "bar"):
return Just(fbar)
return Nothing()
@MU[str].bind
def i_bind(h: "MH[str]", s: str) -> "Maybe[str]":
f1 = foo_or_bar(s)(h)
return Just(f"{f1} and {s}")
def main():
match foo_or_bar("foo"):
case Just(v):
print(f"{v}!")
case Nothing():
print("Aw shucks!")
match i_bind("neither"):
case Nothing():
print("Expected that")
case _:
print("That's surprising!")
if __name__ == "__main__":
main()
Does this look clean?
What are MH and MU?
MH is a type alias for Callable[[Maybe[T]], T]
it's supposed to stand for maybe handler
and MU is a subclass of unwrapper
Maybe have it be the full name
lemme show the code
is there any methods for Maybe to compose with other functions?
https://github.com/ehllie/monadic this is the repo, will be easier this way
you can apply monadic methods to it, and inside a method decorated with binds you can safely unwrap it and pass the bound values to non monadic functions
great
the first argument of the decorated function must be a handler though
and you can either pass the maybe into the handler, or call the maybe with the handler
that will get you a value inside the maybe, or have the entire function return Nothing
I see that you have inherited Just from Monad protocol, why you didn't make Monad as ABC?
I rewrote the Monad today, and I probably could just make it an abstract class
ok
though i'm not exactly sure what the difference would be in having the interfaces inherit from ABC instead of being protocols
Ok, I've rewritten Result now as well. Now I just need to write tests for it
Other than that, I've noticed that there's hardly any difference between Result and Maybe in terms of code, and whether there's a way to reuse the logic and instead just change the type hints for them
Protocols not for inheritance, they are for structural typing
Structural typing is duck typing for static analysis
Abstract classes is nominal typing
the bells of abstraction are ringing
can you hear them??
unfortunately, you can't make a function that abstracts over the concrete monad, because there are no HKT in Python
so e.g. you can't make a function that turns a Result[int] into a Result[str] and a Maybe[int] into a Maybe[str]
yeah, i found that out early on. that's why i've been writing apply as an instance method
so that it follows the abstract, but the kind is inferred from the instance
it can be worked around, but it's more complicated than i thought it would be
That's similar to HKT in returns library (https://github.com/dry-python/returns)
is python 3.10 changed how tuples returns are typed?
or it is still same Tuple?
i saw mentions that some typing became easier in newer python versions ๐ค less requiring imports from typing lib
since Python 3.9 you can just use tuple
urgh. I tried replacing
Tuple[resources.ModelResource, str, int] with tuple(resources.ModelResource, str, int) and my VSCODE stopped recognizing it. VsCode did not provide syntax tips when using returned variables. It worked fine with Tuple
VsCode is correctly attached to Python 3.10
brackets not parens
Ops. It works xD
thanks
xD I use typing only for the sake of IDE functionality
really useful to get code tips, reading docs to stuff on a fly
thank you for showing me that library :3
from __future__ import annotations
from typing import TypeVar, Callable, Generic, overload
TGet = TypeVar('TGet')
TGetNew = TypeVar('TGetNew')
TSet = TypeVar('TSet')
TSetNew = TypeVar('TSetNew')
TObj = TypeVar('TObj')
class prop(Generic[TObj, TGet, TSet]):
fget: Callable[[TObj], TGet] | None
fset: Callable[[TObj, TSet], None] | None
fdel: Callable[[TObj], None] | None
def __new__(cls,
fget: Callable[[TObj], TGet] | None = ...,
fset: Callable[[TObj, TSet], None] | None = ...,
fdel: Callable[[TObj], None] | None = ...,
doc: str | None = ...,
) -> prop[TObj, TGet, TSet]: ...
def getter(self, fget: Callable[[TObj], TGetNew]) -> prop[TObj, TGetNew, TSet]: ...
def setter(self, fset: Callable[[TObj, TSetNew], None]) -> prop[TObj, TGet, TSetNew]: ...
def deleter(self, fdel: Callable[[TObj], None]) -> prop[TObj, TGet, TSet]: ...
@overload
def __get__(self, obj: TObj, type: type[TObj] = ...) -> TGet: ...
@overload
def __get__(self, obj: None, type: type[TObj] = ...) -> prop[TObj, TGet, TSet]: ...
def __get__(self, *a): ... # type: ignore[no-untyped-def]
def __set__(self, obj: TObj, value: TSet) -> None: ...
def __delete__(self, obj: TObj) -> None: ...
class X:
x: int
@prop
def x_square_(self) -> int:
pass
@x_square_.setter
def x_square(self, val: str) -> None:
pass
reveal_type(X.x_square)
reveal_type(X.x_square)
reveal_type(X().x_square)
reveal_type(X().x_square)
x = X()
y = x.x_square
x.x_square = '123'
z: int = 5
print(z)
z: str
def f() -> None:
x: int = 5
print(x)
x: str = ''
print(x)
possible implementation of generic @property for typeshed
generic property please ๐
I'm forced to type Callable[[Callable[[], T]], property[T]] as type[property] and it hurts
Already been tried and was denied unfortunately
it caused weird mypy issues, right?
eric didnt like it cause it made error messages way worse
and it was kinda annoying to specify all the parameters
Is there a better way to type hint proxies that only expose a few instance methods of the wrapped object without having to copy and paste the function signature? I have come up with something like this so far:
_TOwner = TypeVar('_TOwner')
_P, _R_co = ParamSpec('_P'), TypeVar('_R_co', covariant=True)
class UnboundMethodProto(Protocol[_TOwner, _P, _R_co]):
@overload
def __get__(self, obj: None, owner: type[_TOwner]) -> Self: ...
@overload
def __get__(self, obj: _TOwner, owner: type[_TOwner]) -> Callable[_P, _R_co]: ...
def __call__(__self, self: _TOwner, *args: _P.args, **kwargs: _P.kwargs) -> _R_co: ...
_R = TypeVar('_R')
def copy_signature(
ref_fn: UnboundMethodProto[Any, _P, _R],
) -> Callable[[UnboundMethodProto[_TOwner, _P, _R]], UnboundMethodProto[_TOwner, _P, _R]]:
return lambda f: f
# Example usage: only expose `MyClass.bar()` in `MyWrapper`
class MyClass:
def foo(self, x: int) -> bool: ...
def bar(self, y: float, *, z: int = 0) -> None: ...
class MyWrapper:
def __init__(self, wrapped: MyClass) -> None:
self._wrapped = wrapped
@copy_signature(MyClass.bar)
def bar(self, *args, **kwargs):
return self._wrapped.bar(*args, **kwargs)
An issue arises when I try to make such proxies generic. I would like the signature of the exposed method to correspond to the actual type. For example:
class SupportsBar(Protocol):
def bar(self, *args, **kwargs): ...
class MyClass(SupportsBar):
def foo(self, x: int) -> bool: ...
def bar(self, y: float, *, z: int = 0) -> None: ...
class MyOtherClass(SupportsBar):
def bar(self, y: float, *, z: int = 0, k: int = 1) -> None: ...
def baz(self) -> Self: ...
_TSupportsBar = TypeVar('_TSupportsBar', bound=SupportsBar)
class MyWrapper(Generic[_TSupportsBar]):
def __init__(self, wrapped: _TSupportsBar) -> None:
self._wrapped = wrapped
# I want to do something like `@copy_signature(_TSupportsBar.bar)`, such that, for example, the signature of
# `MyWrapper[MyOtherClass].bar` would be `(self, y: float, *, z: int = 0, k: int = 1) -> None`
def bar(self, *args, **kwargs):
return self._wrapped.bar(*args, **kwargs)
does Python have something like the Partial type in TypeScript?
Why is slice not generic? ๐ค
>>> s = slice('', {}, [])
>>> s.indices(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: slice indices must be integers or None or have an __index__ method
``` because slices must support `.indices(n)`
But in typeshed, slice's items are just Any
Also, a method can be typed to only exist on slice[int|None, int|None, int|None]
i feel like having both a slice type and a range type is kinda redundant
range is just numbers, though. if that was changed then i would agree
i like julia's approach, a:b always creates a range, and a_list[a:b] works the same as in python
additionally a and b aren't restricted to just ints
it could be floats, dates, etc
sure. just gimme a sec
Cause TypeVar defaults aren't a thing I think?
But also maybe it'd just happen without that if someone were to open an issue on python/cpython
https://paste.pythondiscord.com/ijeyozaguf
I've got something like this currently. I'd like to replace the messy logic in the update_config handler.
and perhaps the entire design can be improved, but that's what I came up with to dynamically update the config.
update_config should be a put and should not be a 201, but ignore that
Logic wise, it looks like everything should work besides the type hint (and if fastapi performs any transforms for you). I've personally done this before by bypassing the framework, and setting the type hint to a request, then merging the raw data. Fastapi does have a section on doing this, which is basically this approach, but also setting every field in the model to Optional (manually) to allow you to use the correct type hint
Which behavior-wise does match what you'd do in TS I guess
interesting. idk how I missed that in the docs. I'll check it out. thanks for the reply!
Can you typehint a tuple of exactly two sub-types?
Morally equivalent to tuple[int, int]
so that (1, 1, 1) fails
I'm not sure I understand. isn't Tuple[int, int] exactly what you want? It already won't accept (1,1,1) as a compatible value
Oh. Yeah, I had tried it with tuple instead of Tuple ๐
You're not entirely wrong, though ^^ I think the lowercase version is valid in more modern versions of python
it is also valid for tuple, what makes you think it's not?
You may be misinterpreting whatever problem you're having
is it possible to add type hinting to attributes that have been defined with __slots__?
just put my_attribute: SomeType in the class
ok, thanks
----------MYPY----------
book_loader.py:16:39: error: Type application has too many types (1 expected) [misc]
Marker: TypeAlias = re.Pattern[str] | tuple[list[re.Pattern[str]], list[re.Pattern...
This disappears when I use Tuple
Could you perhaps provide a minimal reproduction?
I ran with a: re.Pattern[str] | tuple[list[re.Pattern[str]], list[re.Pattern[str]]] and it passed just fine
oh yeah I think that's a known mypy bug
or maybe not that specific one, that probably got fixed
Couldn't figure out how to generate a link for different code. Run this there and you'll get the error
import re
from typing import TypeAlias
Marker: TypeAlias = re.Pattern[str] | tuple[list[re.Pattern[str]], list[re.Pattern[str]]]
Oh, missed the type alias part
Seems the type here is irrelevant, just the combination of TypeAlias, union, and tuple generics
Oups:
UserId = TypeVar("UserId", int, str)
def setUser(uid: UserId):
...
uid: int | str = getUid()
setUser(uid) # does not type check: Argument of type "int | str" cannot be assigned to...
I am not sure what's going wrong here...
you probably don't need a TypeVar
How to a express a type synonym across my entire program then?
In particular, a type synonym for int | str
UserId: TypeAlias = int | str
from typing import Union, Literal
import numpy as np
T_IntOrNan = Union[int, Literal[np.nan]]
# error: Parameter 1 of Literal[...] is invalid
# error: Variable "numpy.nan" is not valid as a type
def f(x) -> T_IntOrNan:
return x
f(3) # Should be valid
f(np.nan) # Should be valid
f(3.0) # Should not be valid
How do you type to allow ints and NaNs only?
Literal[float('NaN')] | int
Literal[nan] isnt allowed
there isnt a way to type this
but also typing this doesnt make much sense, why is nan allowed but 3.0 isnt??
In dask the shape of an lazy array can become undefined (nan) when masking with boolean arrays otherwise the shape values are ints.
https://github.com/python/typing/issues/1160 sort of similar request here
ig it sort of makes sense to extend to nan
this is correct right?
a: None | str = None
people do None | Any and other weird stuff
Hello! I'm tinkering with dataclass and I'm trying to understand a typing error that Pylance is reporting.```py
@dataclass
class EconomyEntry(LogEntry):
"""The EconomyTransaction object represents one exchange of currency."""
action_type: CurrencyActionType # This is just an IntEnum
currency: CurrencyType # So is this
amount: int
source_user_id: str
target_user_id: str | None
note: str | None = None
timestamp: datetime = datetime.utcnow()
record_no: str | None = None
doc: str = "economy"
def to_dict(self) -> dict[str, Any]:
entry: dict[str, Any] = {
"action_type": self.action_type,
"currency": self.currency,
"amount": self.amount,
"source_id": self.source_user_id,
"target_id": self.target_user_id,
"note": self.note,
"timestamp": self.timestamp,
}
if self.record_no is not None:
entry["_id"] = self.record_no
return entry
@classmethod
def from_dict(cls, **kwargs) -> EconomyEntry:
return EconomyEntry(CurrencyActionType(kwargs["action_type"]),
CurrencyType(kwargs["currency"]), kwargs["amount"],
kwargs["source_id"], kwargs.get("target_id", None),
kwargs.get("note"), kwargs.get("timestamp"),
^^^^^^^^^^^^^^^^^^^^^^^
kwargs.get("_id", None))```
Argument of type "Unknown | None" cannot be assigned to parameter "timestamp" of type "datetime" in function "__init__"
Type "Unknown | None" cannot be assigned to type "datetime"
Type "None" cannot be assigned to type "datetime"```
What I don't really get is, why is it that it's raising a fuss about this field alone?
If it can't assign Unknown | None to datetime, then why can it assign Unknown | None to the other fields? Just because they're literals?
You need to add a type annotation to **kwargs
I figure I can side-step the issue with cast(datetime, kwargs.get("timestamp"), but I was hoping someone could help me understand why this specific typing error is occurring.
Ahh, sounds like inconsistent Pylance behavior is happening.
from typing import TypedDict
from typing_extensions import Unpack
class EconomyEntryDict(TypedDict):
action_type: CurrencyActionType
currency: CurrencyType
...
class EconomyEntry:
@classmethod
def from_dict(cls, **kwargs: Unpack[EconomyEntryDict]) -> EconomyEntry:
return ...
Oooh, I haven't seen Unpack before. This is great; I'll read up. Thanks, graingert!
But with your thing you can also just use keyword only arguments
But you'll be able to save time and reuse the TypeDict for your return type and kwarg type
It's also very convenient for subclasses
For anyone else who might wander in with this issue, apparently Pylance was specifically taking offense with kwargs.get("timestamp") (Unknown | None) not being assignable to datetime because I didn't include a default value. Providing a default as in kwargs.get("timestamp", datetime.utcnow()) changes the inferred parameter type to just (Unknown), which apparently is assignable to datetime.
I'm still keeping my cool new EconomyEntryDict, though.
if you assume the key exists, why not just [index]? That would avoid the same issue
The reason it wasn't reporting errors for the other arguments was because for some of them you're use the [] syntax for some arguments which will error if the key isn't present and is inferred to be Unknown in your case. The other arguments that you're using .get for all contain None in their type signature, e.g. note: str | None = None which means Unknown | None is assignable to str | None.
In general though if a lot of your types are being inferred as Unknown that means you're missing type annotations somewhere and are doing things that might not be enforcing type safety. For example, with your original def from_dict(cls, **kwargs) -> EconomyEntry: method, someone using this API could pass whatever they want for any of the arguments and they wouldn't get a type error. You can see these errors if you enable strict mode.
How do you type a function that should take an Enum class with string values e.g.
from enum import Enum
class Foo(Enum):
BAR = 'bar'
BAZ = 'baz'
T = TypeVar('T', bound=Enum) # This is too weak
def func(enum: T) -> list[str]:
return [e.value for e in enum]
I don't think you can, enums aren't generic
you could make a protocol that specifies that value is of type str
but I don't think enums would match it (unless you explicitly specified it), you'd need to cast them
from python 3.11, there should be enum.StrEnum though
Enum can't be generic
Could you make a protocol that inherits Enum and str?
class StrEnum(Protocol, Enum, str): ...
an Intersection type would work best, but we don't have that.
You could have a Protocol with a @property def value(self) -> str
no, protocols can only inherit from other protocols
How do you support recursive typing? I have a function that can return any type: str, bool, int, float, list, dict. And list can also have within itself lists that also contains any of these types, same for dict.
I looked and seems this is the only solution? https://stackoverflow.com/questions/53638973/recursive-type-annotations
(Which states Dict[str, Any] is the solution)
depends on your type checker. pyright supports recursive types, mypy doesn't
https://peps.python.org/pep-0695/
Is there any specification of how typecheckers would infer generic's variance?
Python Enhancement Proposals (PEPs)
What do you mean by a typechecker defining variance?
What would make sense to me is to say "a programmer defines variance" or "a typechecker verifies/infers variance"
That PEP has a section on inferring variance, which it is suggesting to do over requiring programmers to define variance.
Hence lack of syntax to define variance.
A while ago I think I heard mentions of a new TypeVar syntax (proposal?). Am I imagining things or is that a thing?
That's what the PEP linked above is
np
I'm sorry for confusing, I mean how typechecker should infers variance ๐
Python Enhancement Proposals (PEPs)
Yeah it outlines the algorithm in the PEP.
why should __eq__ work with any object?
Instinctively, ive written something along these lines as typehints for a __eq__ check:
from typing import Union
class A:
def __eq__(self, other: Union["A", int]) -> bool:
return True
now, with this, mypy complains:
error: Argument 1 of "__eq__" is incompatible with supertype "object"; supertype defines the argument type as "object"
note: This violates the Liskov substitution principle
note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
note: It is recommended for "__eq__" to work with arbitrary objects, for example:
note: def __eq__(self, other: object) -> bool:
note: if not isinstance(other, A):
note: return NotImplemented
note: return <logic to compare two A instances>
This seems odd to me, almost like telling me "you cant typecheck __eq__". Am i supposed to not provide any information as to which types my eq implementation actually supports?
how would i typehint eq so that the typechecker is able to warn me when comparing a incompatible type?
This violates the Liskov substitution principle
objecthas a default implementation which supports eq with any otherobject. if a child class overrides a parent class's function, then the new function must accept whatever the old one did, if not more. sinceobject.__eq__accepts anyobject,A.__eq__must also accept any object
consider this function:
def equals_hi(o: object) -> bool:
return o == "hi"
this function is correctly typed, so it should never error, but it will if you did equals_hi(A())
in other words: there is no way to make the typechecker warn me when im comparing types i shouldnt?
The reason is that equality is in the short list of things that should work between any objects.
aight, guess i'll have to live with that
are eq and neq the only two such operations 
the only binary ops perhaps
yeah thats what i meant
all the other operators are in some tp_as_xyz slots I believe
On the C level, actually all the comparison operations (==, !=, <, >, <=, >=) are the same function pointer, with an enum parameter for the op. Not really relevant to type checking though, conceptually object does not implement the others and it wouldn't be in the type stub.
yes, they're in the dir but they simply raise
so for all intents and purposes they aren't implemented
mypy documentation shows ```py
tp: Type[object] # "tp" is a variable with a type object value
if random() > 0.5:
tp = A
else:
tp = B # This is OK
def fun2(x: tp) -> None: ... # Error: "tp" is not valid as a type
So if I need to do this, how do I avoid the error?
Just # type: ignore?
In my case, A is a subtype of B so it'd be nice if I could at least tell mypy to be conservative and assume B
Could I ask what your goal is?
I have a model with pydantic. I want to use EmailStr if the email-validator dependency is installed. If it's not (i.e. I get an ImportError), then I want to fall back to using str as the field.
But mypy does not like the type of the field being dynamic (I get a valid-type error)
I could live with # type: ignore[valid-type] but I'm wondering if there's a better way.
You can do something with if TYPE_CHECKING
That's a good idea. I did not consider that. I can set it to str inside that.
try except ImportError should work right?
Yeah I got that part figured out. It's just that mypy does not like the type being dynamic.
no like mypy should be able to figure out the type right?
try:
import the_email_validator
tp: TypeAlias = email_validator.Type
except ImportError:
tp: TypeAlias = str
this works with pyright iirc
No, it will not work with mypy
This exception is thrown in runtime, not in compile time. mypy can't possibly know what environment you'd be on when trying to import a module so it can't tell which path it should take, and therefore, which type tp would ultimately be
As explained in what I linked above.
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
Using TYPE_CHECKING is the play I think https://mypy-play.net/?mypy=latest&python=3.10&gist=106b48f7ff6b9ff6238f254e35c8ee99
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
won't this make mypy think that FieldType is a str throughout the code?
Yeah, but I believe that is okay for my use case. The fact that the field is actually a EmailStr in some cases is only relevant for pydantic, not for end users.
...wait does PEP 695 mean that type will now be a soft keyword?
yes
As in, similar to match
thats whats suggested
But if that was a concern, then I think my only option would be to subclass EmailStr and override the methods to not raise an importerror
You can actually see it added as a soft keyword if you look in the reference implementation
Hmm so far I haven't read about multi-line classes
Will I be able to do: ```python
class Container[
T
](
list, dict
):
...
Not really that I would want to do that
yes, because newlines are ignored within brackets
Is there a way to type a parent class' self as a subclass?
class Parent:
def some_fn(self, ...) -> None: ...
# self here to be typed as one of ChildA or ChildB
class ChildA(Parent):
...
class ChildB(Parent):
...
do you want self inside Parent.some_fn to be literally typed ChildA | ChildB ?
I think that would not be enforceable, because somewhere else in the code you could have a ChildC that would violate that typing. Maybe you could have some_fn be a standalone function or a static function, that instead of using self just uses some_child: ChildA | ChildB ?
whats are you actually doing in some_fn that requires knowledge of whether its A or B?
I'm using functions from those specific classes that are not found in the Parent class so I would like the types and autocompletion
maybe consider adding those functions as abstract functions
here's a small example of how that may look like```py
from abc import ABC, abstractmethod
class Foo(ABC):
@abstractmethod
def write(self, data: bytes) -> None:
...
def write_string(self, data: str) -> None:
bytes_data = data.encode("utf-8")
self.write(bytes_data)
Ok, would this work the same way for class attributes instead of methods?
with class attributes, just give them a type-hint without any values
class Foo:
MY_VARIABLE: str
def foo(self):
return self.MY_VARIABLE
if you want checking of that however, you need to use a property
it's not quite the same and the ABC metaclass won't prevent you from initializing, but I'm not aware of any better approach there
with @abstractmethod
This works well. Thank you all
Why does mypy not like variable name reassignment?
I know what I am doing it probably bad practice to cause these errors but I would think it makes sense to get the type of a variable in it's current context rather than when it is first defined
Quick example:
def fn():
data = 1
# some other stuff
for names, data in some_dict.items():
...
reveal_type(data) # int
if you are allowed to reassign types, that defeats the purpose of type checking assignments
x = 1 # x is definitely an int
x = "ohoho" # well I guess x must be str now!
No, not really. Pyright is fine with your snippet
It's just that x has different types in different parts of the code block
pyright works fine, mypy not
name = "Oleksii"
name = 0
main.py:2: error: Incompatible types in assignment (expression has type "int", variable has type "str")
Found 1 error in 1 file (checked 1 source file)
Okay, this has been driving me bonkers.
How the crap do you even start to type hint a JSON from an api request?
Like, cleanly I mean
PAYLOAD = dict[str, str | int | list[dict[str, str | int]]]
PARSED_CHANNELS = list[dict[str, str | int]]
Like....
That's about the best I have, and I don't think that fully covers all the cases I have now
either a typeddict, dataclass, or give up and use any
Yeah the Any is looking more and more attractive
I guess I could just do dict
Just hurts to do either
Feels like I'm not being thorough enough. But at the same time.... API JSON
have you tried typeddict?
Use a library like Pydantic or dataclass-factory to parse the response and turn it into something typed
otherwise, well, the response is any JSON
i think Json = bool | int | float | str | list["Json"] | dict[str, "Json"] | None covers all valid json, although mypy doesnt support recursive types
There is _typeshed.JSON FWIW
Why does mypy give the error that "Name {typename} is not defined" when you're importing types from other files?
Can somekne provide any resources to learn more about python type hinting. Also im not sure if this is typehinting but in lots of official python modules there are empty functions like def x(a) -> List[str | int]: ...
check the pins for some links
for your second question, you're probably looking at stub files. Those aren't the real code, just a condensed version that type checkers use
Alriht
Is type hinting making your code more idiomatic in any way
Yes, type hints makes code self-documenting
You don't need to guess the variable or argument type
Also type hints are important thing for auto-complete
Yeah
Uh more stupid question. In 3.10 the pipe was introduced to replace Union[..., ...] although since Im new to type hihting i dont know what these do. My silly guess is that List[str | int] is a list of strings and ints? Lmfao
Note there's a big difference between list[str | int] and list[str] | list[int] - the first is a list with any combination of strings/ints, the latter is a list with only one of the types.
It's technically not a list of strings and ints but instead a list of strings or ints. It's just with the way list typing works it could have both, but it might not.
(to be super explicit as to what it means)
But then I suppose that's what @pastel egret was referring to with the difference between them so maybe what I said is wrong
I think it could technically still be all strings/ints
It can be yes.
What I meant is that list[str | int] accepts ['a', 1, 'b', 2], but list[str] | list[int] does not.
Yeah, exactly
When using typing.overload should the final implementation be type hinted. The docs and pep 484 suggest that type checkers should ignore it:
The @overload-decorated definitions are for the benefit of the type checker only, since they will be overwritten by the non-@overload-decorated definition, while the latter is used at runtime but should be ignored by a type checker.
But mypy still complains about them when running under strict mode. Is there any advantage to adding type hints (which I guess would always be just the unions for each possible parameter/return)?
yes, the type hints can be used for type checking the implementation
they are not useful from the perspective of callers though
I see. Is there any reason a type checker couldn't/shouldn't infer that based on the type hints of the overloads?
Might be possible in simple cases, but it's harder if the overloads are very different (e.g., different number of parameters)
I'll add my two cents here and say that overloads in python are a detriment to your type safety; It's completely on you, the developer, to properly dispatch to the correct implementation of your overloaded function; If the type checker can't determine for sure what the types of the parameters of the final implementation are, then it's easy for you to forget to dispatch for one of the overloads, specially if you add more later
This is different from a language like Java, which will determine which version of your function should be called during compile time
Hello, I wanted to annotate an attribute which would be initialized later. But, I'm not sure as to what placeholder value I should use in a way that doesn't make the rest of the code scream.
If I do Optional[Data], initializing it with None will work, but accessing this in the methods gives errors
don't initialize it
put data: Data as a class attr
i.e. ```py
class Cls:
data: Data
def init(self, other: str) -> None:
self.other = other
def set_data(self, data: Data):
self.data = data
i think initialising it with None is the most type safe thing to do as annoying as it may be
what if i accidentally do self.data.foo before ive called set_data or w/e?
Then you'd get a TypeError instead of AttributeError
class Cls:
def __init__(self, other: str) -> None:
self.other = other
self._data: Data | None = None
@cached_property
def data(self) -> Data:
assert self._data is not None
return self._data
this is what id generally recommend
Thanks
not sure if this is a mypy bug or if I'm doing the toml config wrong?
[[tool.mypy.overrides]]
module = "tests.*"
ignore_missing_imports = true
disallow_untyped_defs = false
the ignore_missing_imports for tests doesn't work with this config
So it is like an or. list[bool] | list[str] accepts [True, False . . . ] and ["abc", "def" . . . ] but not ["abc", True]
yes exactly like an or
Yeah thx
I am trying to annotate type constraints for a class decorator, but pyright seems to be unable to enforce them for some reason... is this a bug, or am I just not doing this correctly? Also, I have no idea why dec0 behaves differently from dec1 in this respect
In case you want to copy and paste the code:
from typing import Protocol, TypeVar, Type
class MyProto(Protocol):
def foo(self, x: int) -> int: ...
TMyProtoMeta = TypeVar('TMyProtoMeta', bound=Type[MyProto])
def dec0(t: TMyProtoMeta) -> TMyProtoMeta:
return t
def dec1(t: Type[MyProto]):
return t
@dec0
class Good0:
def foo(self, x: int) -> int:
return x
dec0(Good0)
@dec1
class Good1:
def foo(self, x: int) -> int:
return x
dec1(Good1)
@dec0 # Failed to detect error
class Bad0:
pass
dec0(Bad0)
@dec1 # Failed to detect error
class Bad1:
pass
dec1(Bad1) # Failed to detect error
mypy is able to infer the incorrect usage of the decorator for the Bad classes, so this seems like a pyright issue
What are the semantics of type[Proto]? Perhaps you can substitute the typevar inside the type[] call?
If I rewrite dec1 as
TMyProto = TypeVar('TMyProto', bound=MyProto)
def dec1(t: Type[TMyProto]):
return t
the last case (dec1(Bad1)) errors out as expected, but the two cases that use the decorator syntax still pass
What if you add a return annotation to dec1?
Maybe mypy treats untyped/partially typed decorators as identity functions
No effect
with or without the TypeVar, it behaves the same as the corresponding case when I don't specify the return type
mypy basically ignores class decorators
Is typing a function's args/kwargs impossible without also passing the function around at runtime? I have a class that stores a function call's arguments, but it doesn't store the function itself
not sure exactly what you need, but sounds like a job for ParamSpec
I have a class
class A:
def __init__(self, args, kwargs):
self.args = args
self.kwargs = kwargs
and want to type args and kwargs to be the corresponding args and kwargs of a function. That function is known where A is instantiated, but A itself doesn't keep track of it
Hey uh stupid question butshould i typhint everything.
typehint as much or as little as you want, its the benefit of gradual typing
i personally try and typehint everything that i think i could come back to and stuff thats a one off i dont normally bother
but note that you mostly should not type hint local variables
You would like to typehint modules for client
Wdym
For example if you are doing library you would like to typehint its all functions, classes that would be used by someone other
I like to do x: list[str] = []
just for generics though
i prefer x = list[str]()
Well that doesn't work in python 3.8 (using annotations works if using from __future__ import annotations)
Why not?
So redundant
they can easily be inferred when you first assign them
So, it's not as much about local variables as it is about situations where type inference is obvious?
Guys, anyone uses pylint on vscode? Basically I'm doing this
def x() -> str:
return "hello"
def y(foo: int) -> str:
return str(foo)
y(x())
Shouldn't y mark an error? Because it's hinted it receives an int but gets a str
I believe you need to turn on pyright somehow in pylint for type checking
ah no that's pylance, curse the similar naming
pylint just doesn't do type checking afaik
vscode provides integration with pyright through pylance
is typehinting a float to specify range a good idea (e.g. float[0, 1] to mean a float between 0 and 1)
mypy and pyright both throw errors like error: "float" expects no type arguments, but 2 given with that
Can I typehint a float/int to be between a specific range (i.e. 0 and 1.0 and 0 and 100)
I tried googling it but I get something about number types
Not with the base type system, since it's intended to mainly constrain types not values.
Ok
There's libraries though that add the ability to do things like that - see phantom-types for instance.
You can use typing.Annotated and/or typing.NewType to create type-checker-only types with the constraints, and then have a converter function which checks the range, then casts it to that new type.
i'm using the websockets module and noticed that VSCode does not offer any type hints for it
is that a vscode issue or does the module not offer any kind of type hints ..
?
Looking at the repo, it should have type hints. You might need to change some Pyright configuration to get it to be able to find the module properly.
i honestly don't know how to do that
any guides available ?
Generally, there is only a need to provide type annotations when it could not be inferred (in such cases, an "Unknown" type is shown when you mouse over the variable in your IDE)
I came up with this a while ago which works for decorating instance methods: <#type-hinting message>
You can similarly come up with a decorator that works for static and class methods
Hi guys, I'm using vscode and pylint
It just show errors when I save the file, is this OK? I think it's kind of annoying, can it show it in real time?
Hmm, mine shows the errors in real time... are you referring to the errors only popping up when you open the file? (as opposed to saving the file)
No, errors just update when I save the file
I can fix an error, but as long as I don't save it if doesn't update the errors
Maybe you're using autosave?
I've pylance too, but I doubt it's affecting it
I have default Pylance settings, didn't really change any settings regarding autosave
just checked, my autosave is off
np
pylint probably isnt capable of running as you type
pyright has parse error recovery
There's this.
https://github.com/python-lsp/python-lsp-server
is this not a valid TypeVarTuple usage?
Ts = TypeVarTuple("Ts")
# ... mapping Tuple[*Ts] to Union[*Ts]```
Looks ok to me... Have you assigned anything else to Ts before that line?
yeah I'm basically implementing something funny like ```python
@final
@frozen()
class WrapResult(Generic[E]):
error_type: E
@classmethod
def create(cls, error_type: F) -> WrapResult[F]:
return cls(error_type) # type: ignore
def __getitem__(self, error_type: F) -> WrapResult[F]:
return self.create(error_type)``` and I'm thinking of allowing tuples of error types as well
unfortunately no
When does mypy look at stub files? It doesn't seem to be reading them if they're in the same directory, or even if they're in a directory pointed at by MYPYPATH. I have a very basic test.py and a test.pyi, but a type conflict that should be shown is not unless I directly annotate test.py
If I try mypy ., it tells me I have a duplicate module in stubs\test.pyi... I feel I'm missing something obvious!
I think .pyi files must be inside modules to work (at least in pyright) which means you'd need to have those __init__.py files to make your folders into modules
I just reread the docs, and it does say
Use the normal Python file name conventions for modules, e.g. csv.pyi for module csv.
I had misread that as "for csv.py" previously.
No idea. With or without __init__.py, changing the .pyi to the directory ("module") name, it just says "no issues found in 2/3 source files" :(
what's your file structure?
Flat directory structure. All files in single project dir.
pyi_test
|- __init__.py
|- pyi_test.pyi
|- test.py```
ah, rats. I guess I was thinking of the py.typed file, not .pyi files
So, if I understand correctly, though, you'd use .pyi when you don't control the source code for the library you're annotating. If you do, then you should put the type annotations directly into the code
I'm trying to emulate that situation.
Although keeping the typing in a separate file feels like a good way to avoid unnecessary bloat in the source file, too. (Especially to avoid confusing other devs who don't necessarily understand what the type hints are)
I'd argue the opposite. Having the types right there makes it easier to understand what your functions require and what they produce. Otherwise you have to read through the actual implementation to get a sense of what's going on
I think that depends on how complex they get.
When it's just def fn(a: int) -> None: ... that's one thing. When you start putting a load of optionals in... I guess you create a type (at which point, someone says "what's this weird variable at the top of the file?" ;) )
But anyway. No idea why the basic layout's not working.
Having a look at what numpy is doing, it seems that .pyi files match the names of other python files:
https://github.com/numpy/numpy/tree/main/numpy
So your tree structure there should probably have a test.pyi file instead of a pyi_test.pyi file. They also have a __init__.pyi, in which they seem to reference the other .pyi files.
So i guess you either put all your typing inside __init__.pyi or, if you split it off, maybe they have to be referenced inside __init__.pyi ?
I did initially have a test.pyi, but after reading the docs I quoted above I switched to <module>.pyi... It certainly would seem more natural to have a <file>.pyi
What do you mean?
Was this for me?
Yes
Why would you use a generic like this instead of just str | int or even a TypeAlias for it?
T = TypeVar("T", str, int)
this is not a generic, but a constrained TypeVar. You use it in other places where you'd use TypeVars, e.g. with a function whose return type depends on its parameter types
As in fn(a: list[T]) -> T iiuc, can't a TypeAlias do the same?
no
T: TypeAlias = str | int
fn there with a type alias is equivalent to ```py
@overload
def fn(a: list[str]) -> str: ...
@overload
def fn(a: list[int]) -> int: ...
def fn(a): ... # some actual implementation
with a type alias you have the same input types but your output will be different
cause youll widen the type to str | int rather than str if a is list[str] or int if a list[int]
Oh. So, with that type alias, a str input can have an int output
yep
And the constrained typevar doesn't allow that?
yep
Because T is constrained to resolve to one type at a time, hmm
Interesting, thank you
Also, I read about this typevar constraint notion in an article on python generics, why is it wrong to call it a generic?
a generic is generally a type that can be subscripted (ie you use []) with another type and acts as a container (in a very broad definition of the term)
type vars arent types and currently cant be subscripted
Union is not, it's its own special thing
Is there a way to annotate that a function accepts instances of some type, but not instances of subtypes of that type? I've got an API that requires a dict instance, not an instance of a subclass of dict, and I'm wondering if there's some way to explain that via typing
!d typing.Final
typing.Final```
A special typing construct to indicate to type checkers that a name cannot be re-assigned or overridden in a subclass. For example:
```py
MAX_SIZE: Final = 9000
MAX_SIZE += 1 # Error reported by type checker
class Connection:
TIMEOUT: Final[int] = 10
class FastConnector(Connection):
TIMEOUT = 1 # Error reported by type checker
```...
no, there is not
(Yes, it's probably not a good idea to have such an API in the first place; we're only accepting dict for good reasons, I swear)
wouldn't Final[dict] do the trick?
no
cool, thanks. Saves me the time of looking for something that's not there. ๐
that would mean the variable cannot be assigned to later
There are some stdlib APIs like that too. We could add a new type system feature for this, but not sure it's common enough to be worth the complication
indeed. The spot where I'm doing it is probably pretty close to why the stdlib is doing it - performance, and to avoid the possibility of reentrancy
(since I know that the accesses I'm performing on a dict can't call back into me, but I couldn't know that about arbitrary dict subclasses)
I think it's impossible to enforce. Consider this:
foo: MySuperDict[str, int] = ...
bar: dict[str, int] = foo
your_function(bar)
that's the same issue as "iterable of strings but not a string"
true enough, that example makes sense.
it wouldn't really be enforceable at compile time in any statically typed languages I can think of, either
its probably possible in languages which do not support subtyping
wait
thats obvious nvm
it shouldn't, at least not enough to make any difference
depends
if you're instantiating a lot of type hints in the runtime with function calls that are somehow expensive, it may ๐ค
personally i just use strings whereever i can. i still get syntax highlighting within them because my editor understands that those are type hints, and the typechecker also understands them
pyright will also complete inside type strings
Python should add a flag where it removes all typing stuff, annotations, etc
would this be a good solution?
atleast optimize out typing stuff
solution to what?
it's a really minor cost, and you only pay it on startup (in almost all cases)
eg: use of typing.cast()
That's not really possible to optimize out
why not
Python has a lot of dynamic behaviours, which break otherwise sensible optimizations. For example, you might think that this: ```py
module_a.py
foo = 1
def get_foo():
return foo
can be optimized topy
module_a.py
def get_foo():
return 1
But nothing is stopping some other module from doingpy
import module_a
module_a.foo = 42
```, in which case get_foo should return 42.
if i can remove a typing.cast call then even the optimizer can
For example, you can do this: ```py
import typing
def my_cast(a, b):
print("Hello, world!")
return b
typing.cast = my_cast
``` and a faithful Python implementation will allow this. In that case, removing typing.cast from some other module will not be an optimization.
An optimization, by definition, does not change the behaviour of a program.
If calling typing.cast is such a big performance penalty you can replace it with a # type: ignore
which is ugly
isn't typing.cast also ugly? 
why doesnt python support type hinting for more complex objects like coroutines by default?
or it does and i just havent discovered it ๐ ?
to clarify, i mean not importing any modules such as typing or collections
Mypy:
Item "None" of "Optional[Match[str]]" has no attribute "group" [union-attr]
if found := marker.search(paragraph):
current = int(found.group(1))
Python 3.10.5, mypy 0.961
So...from context, "Item" is clearly never None. How can I solve this mypy warning?
because it would pollute the builtin namespace with things that are not relevant in a lot of uses
you can do a cast if you can be sure the search won't return None
which module would you suggest to use for type hints because theres several that seem to overlap
that seems like a bug
maybe type narrowing for the walrus isn't implemented properly
ah sorry I misread that error
The search can return None, in which case the if body isn't accessed as intended
collections.abc has generics for python's abcs, typing has typing things like Literal, and other objects that need it should be generics on their own
Welp, guess I'll just # type: ignore[union-attr] on that line until they fix that
e.g. collections.abc.Iterable is any iterable, most objects don't actually subclass it but it's understood that anything that implements __iter__ is iterable and falls under Iterable. It's abstract and only tells you what should be implemented (and may give some useful defaults for methods)
https://mypy-play.net/?mypy=latest&python=3.10&gist=153d5af78532766fc59af0b2e11c15a2
Argument 1 to "map" has incompatible type "Callable[[T], T]"; expected "Callable[[object], T]"
T = TypeVar('T', str, int)
StrOrInt: TypeAlias = str | int
def fn(p: T) -> T:
if isinstance(p, int):
return p
return p * 2
def bn() -> Iterator[StrOrInt]:
res = map(fn, ['a', 1, 'b', 2])
print(res)
return res
I wanted fn to express that it returns the same type it receives (i.e. int -> int and str -> str) so I used a constrained TypeVar there.
But mypy isn't happy with the map line. Not sure I understand why ๐
try using typing.T?
I'm not familiar with that. from typing import T?
yes
main.py:1: error: Module "typing" has no attribute "T"
>>> def f(p: T) -> T:
... if isinstance(p, int):
... return p
... return p * 2
>>> def bn() -> Iterator[str | int]:
... return map(fn, ['a', 1, 'b', 2])
works
I believe it's because the typevar resolves to both with the map, while the typevar expects either of them but not both
Yeah...I came to suspect something like that but then didn't know how to fix it here
@rustic gull I don't see anything different from how I did it?
https://mypy-play.net/?mypy=latest&python=3.11&gist=da7cae6d502cfbbe6e10b6838d1cc6bd
from typing import T seems to fail even on 3.11
the site's issue
Ok...I'll try in my actual code. Also having a hard time finding doc on typing.T since T is kinda hard to search
any T in typing is going to be their own typevar, it's not supposed to be used externally
oh
Ok...how would you approach that problem?
fn outputs the same type it takes, but it's normal for bn to return an iterable of both types ๐
from typing import TypeVar, Sequence
T = TypeVar('T') # Declare type variable
def first(seq: Sequence[T]) -> T: # Generic function
return seq[0]
T = TypeVar('T') instead of what you had
Only bound on the union of the two came to mind but that also seems to be problematic
this is too hard for me to understand
a plain typevar would make their fn invalid
can we make it like C++ templates
Well, if you're having trouble with this, maybe I should think about changing the design instead
I'll try overloading fn instead of branching on types with if
can we do that?
Never used @overload, about to
wouldn't it just replace the whole function
ah
@overload would just do what you did but hide the if else right?
Yeah...no, it didn't help, same error
https://mypy-play.net/?mypy=latest&python=3.11&gist=bf48e789346d7c1a9c1a9ed7a8b75605
I mean...I'm not doing anything crazy, why is the typing system giving me a hard time here ๐
would you consider this a bug in mypy?
I don't think so, because TypeVars do expect to resolve a single type and none other (can't be int | str), but that's what fn is doing. There shouldn't be a problem with bn returning an iterator of both types
A function returns a map object over int | str, and the mapper returns an int given an int, and a str given a str. Nothing too crazy here ๐คทโโ๏ธ
It doesn't seem to me like TypeVars or mypy are broken, but I feel like there is a mechanism lacking for expressing something this simple
Argument 1 to "transform" of "Transformer" has incompatible type "Union[Any, Tree[Any], None]"; expected "Tree[Any]"mypy(error)
why
TypeVars seem to just not like playing nice with unions ๐คทโโ๏ธ
this is join-v-union territory: mypy infers the list as list[object] inappropriately
try explicitly declaring lst: list[str | int] = [...]
https://mypy-play.net/?mypy=latest&python=3.11&gist=d1c265772ffeacf00061d83fb24ade6a Like this? Same error ๐คทโโ๏ธ
def bn() -> Iterator[StrOrInt]:
lst: list[str | int] = ['a', 1, 'b', 2]
res = map(fn, lst)
print(res)
return res
Argument 1 to "map" has incompatible type "Callable[[T], T]"; expected "Callable[[Union[str, int]], T]"
I'm wrong, it's not the "same", it was Callable[object] before:
Argument 1 to "map" has incompatible type "Callable[[T], T]"; expected "Callable[[object], T]"
that one is correct. you cannot pass str | int to a function that is generic over a constrained TypeVar
it works if you use a bound TypeVar instead: https://mypy-play.net/?mypy=latest&python=3.11&gist=d1c265772ffeacf00061d83fb24ade6a
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
(except it starts complaining about fn instead)
(You gave me back my same link, you need to click on the Gist button right next to Run to generate your own link)
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
Interesting
So, as a TIL, just never pass a union type to a constrained TypeVar, that's not how they're supposed to be used
Hm. I think I'll just drop the TypeVar and only use TypeAlias here. I wanted to express that fn returns the same type it takes, but that seems too problematic here ๐คทโโ๏ธ
If someone has a nice .pytlintrc and is willing to share it, I would apreciarte it
yeah, this is https://github.com/python/mypy/issues/12190 . needs to have a constrained type var, from a quick pdb, looks related to the multiple pass logic
anyone know why mypy would say error: Variable "typing_extensions.Self" is not valid as a type?
I'm not sure whether the support is there yet
aw. :^(
at least pyright seems to understand it
mypy not understanding that causes a bunch of other failures to cascade
core/models.py:735: note: Revealed type is "Type[core.models.TranslationModelBase[Self?]]"
since it doesn't know what Self is, it doesn't let me use this type as an actual Type
You can try this style for the Self type:
MYSELF = TypeVar("MYSELF", bound="MyClass")
class MyClass:
def foo(self: MYSELF):
...
I think this style even prevents some of the variance unsoundness that you'd get by using Self
You get variance unsafeness using Self?
Self should be mainly used in classmethods to return an instance of the class.
i.e. ```py
class Foo:
@classmethod
def create(cls) -> Self:
return cls()
It can also be used for method chaining where each method returns self
i.e. x().y().z()
Hello!
Anyone knows what's the closest thing to
def get_const(img_type: type[Item], key: str) -> [WHATEVER TYPE tp[key] IS]:
tp = item[img_type.as_string()]
return tp[key]
?
where is the item mapping / dictionary coming from?
Same file as the function, as an example:
still: Consts = {
"RESULTS_DIR": "Stills",
"LIST_URL_TEMPLATE": "",
"URL_TEMPLATE": "",
"JSON_FILENAME": "stills.json",
"PARSER": html_parser.StillParser,
"ORGANIZER": organizer.StillOrganizer,
}
Consts is a TypedDict
I could return an union for sure.
there's nothing like that in the current type system
Ah, ok. I'll just return a cast to Any then.
However I'm having another issue
To solve a cyclic import, I put an if TYPE_CHECKING around the from classes import Item.
But now Python yells at me that NameError: name 'Item' is not defined. Did you mean: 'item'?
I'm not using Item in any way here other than type checking.
annotations are still evaluated at import time. Put "Item" in quotes or use from __future__ import annotations
Ah, thanks.
Argument 1 to "join" of "str" has incompatible type "List[_S]"; expected "Iterable[str]"
def tables_to_text(paragraph) -> str:
values_of = partial(map, itemgetter("VALUE"))
extracted_text = []
for row in values_of(paragraph):
for cell in values_of(row):
for cell_content in values_of(cell):
for text in values_of(cell_content):
extracted_text.append(text)
return ' '.join(extracted_text)
https://mypy-play.net/?mypy=0.961&python=3.10&gist=29175e885b7b464698564de8a5ebdb85
Not sure how to fix this error. What is this _S thing? It's supposed to just be a str ๐
you should complete the types in your function args
anyway, try annotating extracted_text with list[str]
Yeah I had tried that, it just moves the error to the list instead:
Argument 1 to "append" of "list" has incompatible type "_S"; expected "str"
https://mypy-play.net/?mypy=0.961&python=3.10&gist=ac619d2ff569f0a3d9c67999864050eb
Hello. What's the effective difference between @overload and @dispatch? I understand that the syntax between them in defining overloaded functions differs.
do they complement or substitute for one another?
the first is only for type checkers, the second has a runtime effect
like mypy, ok
Thanks. Can you please elaborate on that?
def tables_to_text(paragraph: list[dict[str, Any]]) -> str:
values_of = partial(map, itemgetter("VALUE"))
return ' '.join(
text
for row in values_of(paragraph)
for cell in values_of(row)
for content in values_of(cell)
for text in values_of(content))
Generator has incompatible item type "_S"; expected "str"
Weird enough, just marking values_of as a Callable fixes the problem.
https://mypy-play.net/?mypy=0.961&python=3.10&gist=69b44f41f5f8f7392cc1fcfbb6503a29
yeah the problem is likely that mypy can't solve all the typevars involved in partial() and map()
The _S it was complaining about was actually within partial's return type. I figured Callable with its implicit [[Any], Any] would subsume that
i had asked this before in a help channel but didn't really get anywhere. i want to get essentially the following type structure:
from typing import Self, TypeVar, Type, Generic, TYPE_CHECKING
from django.db.models import Model
from django.db import models
if TYPE_CHECKING:
from django.db.models.manager import RelatedManager
class Language(Model): ...
_TranslatedM = TypeVar("_TranslatedM", bound="TranslatedModel")
_TranslationM = TypeVar("_TranslationM", bound="TranslationModelBase")
class TranslationModelBase(Generic[_TranslatedM], Model):
language: "models.ForeignKey[Language, Language]"
parent: "models.ForeignKey[_TranslatedM, _TranslatedM]"
class TranslatedModelMeta(models.base.ModelBase):
"Metaclass that generates a TranslationModelBase subclass as the Translation property of its classes."
...
class TranslatedModel(Generic[_TranslationM], Model, metaclass=TranslatedModelMeta):
Translation: "Type[TranslationModelBase[Self]]"
translations: "RelatedManager[_TranslationM]"
@property
def translation(self) -> _TranslationM:
current_locale = get_current_locale_from_request()
return self.translations.get(language__code=current_locale)
this would allow me to write the following and have it fully type hinted (and automatically linked in django!) without having to manually specify things:
class Foo(TranslatedModel["FooTranslation"]):
... # fields, etc.
## Expected:
# Translation: Type[TranslationModelBase[Foo]]
# translation: FooTranslation
# translations: RelatedManager[FooTranslation]
class FooTranslation(Foo.Translation):
name = models.CharField[str, str](...)
description = models.CharField[str, str](...)
## Expected:
# parent: models.ForeignKey[Foo, Foo]
however currently mypy doesn't seem to understand Self, and pyright refuses the code stating "Class definition of Foo depends on itself". is there a way to properly type this?
use in actual code would be something like
foo = get_object_or_404(Foo, pk=pk) # foo: Foo
foo.translation # Expected: FooTranslation
foo.translation.name # Expected: str (via CharField.__get__)
right now the best compromise i have is specifying translation and translations's type manually in the Foo class definition and not giving it the FooTranslation type parameter, but i hope that there's a better way to do it.
im wondering - anyone aware of a painless way to tie lazy loading and type hinting so that i can declare certain descriptors on objects that will bascially act like cache properties
if TYPE_CHECKING:
from ._heavy import Helper
class Obj:
@cached_property
def helper(self) -> Helper:
from .heavy import Helper
return Helper.from_obj(self)
## "wanted"
with lazy_import():
from . import _heavy
class Obj:
helper: _heavy.Helper = magic_demand_create_needs_sane_name()
It might help to reduce your example a bit to make it easier for people to read; maybe get rid of all django stuff and produce a minimal example of what you're trying to accomplish =)
Are you having problems in particular with TranslatedModelMeta? Are you trying to come up with the code inside TranslatedModelMetat hat does the typing magic you're after?
At first glance, I don't think you could do what you want via metaclasses. It sounds more like what you want is what "code generation" or "monomorphization" would do, which would actually create new types in compile time, and which would make the type checker aware of them. Even if you achieve that in your code, I don't think any type check would ever be aware of the classes that get generated.
Still, I don't think you need this level of magic to do what you want
maybe get rid of all django stuff
sure, i just thought it'd give more context as to what i'm trying to do ;)
are you having problems in particular withTranslatedModelMeta?
the code works fine and does what i expect; it's only the type hinting i have trouble with.
i think what i need is for two types to be able to depend on each other; i think (not 100% sure) some other languages allow this. what i want is for TranslationModelBase to automatically know of the TranslatedModel type, and for the TranslatedModel to automatically know of the TranslationModelBase subclass.
the Foo.Translation model points to Foo via a foreign key (which is added in the metaclass). i want this foreign key's type to be automatically figured out. i also want to be able to tell Foo about the reverse relation via a type parameter.
i'll come up with a pure python version and post it to give an idea of what i want
Great =)
The thing is, there's a lot that works in runtime but that the type checkers can't be aware of, because they don't execute the code. Whatever you come up with has got to be declarative, so to speak, not imperative.
how can i get generic type in a method of generic class?
like if i have a = generic_type[int]() i want to call int() in a.create()
I don't think you can get that information, but you could pass it into the normal args and have the generic resolve to that through a typevar
That's kinda hard to do because you don't have runtime access to that generic. You can somewhat work around it with something like this:
from typing import TypeVar, Generic, Type
T = TypeVar("T")
class MyClass(Generic[T]):
def __init__(self, your_type: Type[T]):
self.your_type = your_type
super().__init__()
def create(self) -> T:
return self.your_type()
Notice that the create method isn't completely safe, though. If you pass in a type in your_type that can't be instantiated without arguments, things will break in runtime. You might want to consider having T have a bound to a Protocol that specifies how it can be called/instantiated
thank you!
how about creating generic function?
i have a func that has no params so i cant use typevar
but i want to have its return value
generic_func[int]() # returns int
Why would you have such a function?
can you provide more details maybe?
in this case i want to make the typechecker explicitly aware but it won't let me
that "Unknown" is the problem
I'm constructing a pgsql query here
I defined everything that starts with Query till the last fetch
fetch looks like this:
I want to substitute the "Any" with something that the user can specify
so that I get the exact type instead of "Unknown" or "Any"
records: Sequence[PaginationResult[YourThing]] = await ...
from typing import TypeVar, Generic, Sequence, Type
from typing_extensions import reveal_type, Self
_TranslatedM = TypeVar("_TranslatedM", bound="TranslatedModel")
_TranslationM = TypeVar("_TranslationM", bound="TranslationModelBase")
class Language: ...
class TranslationModelBase(Generic[_TranslatedM]):
language: "Language"
parent: "_TranslatedM"
class TranslatedModel(Generic[_TranslationM]):
Translation: "Type[TranslationModelBase[Self]]" # Generated by a metaclass
translation: "_TranslationM"
translations: "Sequence[_TranslationM]"
# Usage:
class Foo(TranslatedModel["FooTranslation"]):
pass
class FooTranslation(Foo.Translation):
name: str # CharField[str, str]
reveal_type(Foo.Translation) # Type[TranslationModelBase[Foo]]
reveal_type(Foo().translation) # FooTranslation
reveal_type(Foo().translations) # Sequence[FooTranslation]
reveal_type(Foo().translation.name) # str
reveal_type(FooTranslation().parent) # Foo
@fervent sierra ^
right now mypy doesn't understand typing_extensions.Self, so i get the following output:
typing_test.py:15: error: Variable "typing_extensions.Self" is not valid as a type
typing_test.py:15: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
typing_test.py:27: error: Variable "typing_test.TranslatedModel.Translation" is not valid as a type
typing_test.py:27: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
typing_test.py:27: error: Invalid base class "Foo.Translation"
typing_test.py:31: note: Revealed type is "Type[typing_test.TranslationModelBase[Self?]]"
typing_test.py:32: note: Revealed type is "typing_test.FooTranslation"
typing_test.py:33: note: Revealed type is "typing.Sequence[typing_test.FooTranslation]"
typing_test.py:34: note: Revealed type is "builtins.str"
typing_test.py:35: note: Revealed type is "Any"
welp, i had the type vars reversed
fixed above, but still the same errors
If you want you can try my branch for it
It's a little bit broken for things that aren't Self but it should work for this
thank you, i'll give it a try :^)
@soft matrix
typing_test.py:27: error: Variable "typing_test.TranslatedModel.Translation" is not valid as a type
typing_test.py:27: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
typing_test.py:27: error: Invalid base class "Foo.Translation"
typing_test.py:31: error: Access to generic instance variables via class is ambiguous
typing_test.py:31: note: Revealed type is "Type[typing_test.TranslationModelBase[Any]]"
typing_test.py:32: note: Revealed type is "typing_test.FooTranslation"
typing_test.py:33: note: Revealed type is "typing.Sequence[typing_test.FooTranslation]"
typing_test.py:34: note: Revealed type is "builtins.str"
typing_test.py:35: note: Revealed type is "Any"
the Self error went away but the other errors seem to persist
ah, one problem seems to be that Translation is seen as an instance variable
let me see how i can make it seen as a class variable
argh, hitting this weird limitation now: https://peps.python.org/pep-0526/#class-and-instance-variable-annotations
Note that a ClassVar parameter cannot include any type variables, regardless of the level of nesting: ClassVar[T] and ClassVar[List[Set[T]]] are both invalid if T is a type variable.
related discussion https://github.com/python/mypy/issues/5144
i wonder what a good way of approaching this would be... PEP amendment?
but it seems like had ClassVar accepted that, mypy would be okay with that code
which is what i was going for ;^) so i guess time to bug the typing people about it
but i think i should first PoC a generic ClassVar
So I guess you expect your code to "expand" into something like this:
class Foo(...):
class Translation(...):
...
But there is no class/type generation from the perspective of the type checker as far as I understand. Maybe you'll need to sacrifice some convenience during usage =/
sure, i don't expect it to generate a type for me. i just want it to understand that it's there
the type is generated by the metaclass and placed on that property
seems like some of the other mypy errors i've been getting is caused by me not making Translation a ClassVar, and if i do that i get the error that ClassVars can't be generic (which seems to be an oversight in PEP 526)
could you maybe get the Translation also as a generic arg?
hmm, good point... i suppose something like this could work:
class TranslatedModel(Generic[_TranslatedM, _TranslationM]):
Translation: "ClassVar[Type[TranslationModelBase[_TranslatedM]]]"
translation: "_TranslationM"
translations: "Sequence[_TranslationM]"
# ...
class Foo(TranslatedModel["Foo", "FooTranslation"]):
pass
i'll try that later
it seems like pyright just can't cope with circular types so i'll ignore it for now
but then again i'm hitting the issue of classvars not being able to be generic
even though subclasses can fully specify the type of the class var
I think pyright can deal with it just fine. I have types for JSON, which are circular by nature
well, circular in the sense of Class definition of "Foo" depends on itself
which i think should be fine
aha ๐ค
Is that a pyright error?
yes
