#type-hinting
1 messages · Page 57 of 1
why? i mean, it works with type
from typing import Union
class X:
__or__ = Union.__or__ #mypy: error operator - Unsupported left operand type for | ("_SpecialForm")
bug?
not bug
Union is not type, it is instance of _SpecialForm
what are you trying to do?
i am implementing GenericAlias in python
Uhh why?
Read the code in typing_extensions
like typing._GenericAlias ?
Uhh actually this one https://pypi.org/project/typing/ maybe?
That’s an old backport for 3.4 and earlier.
You’ll want the stdlib typing module:
https://github.com/python/cpython/blob/main/Lib/typing.py
Note though, all the implementation here is private and has changed a lot between releases to be more efficient or accomodate new features.
that is actually a bit of a pain if you want to inspect type annotations
or types.GenericAlias
looks like that's what e.g. list[int] gives you
yea, the API is only __args__ right
or some such thing
def __or__(self, other):
return Union[self, other]
mine was actually no good
How would i typehint an anyio task created by TaskGroup.start or start_soon
i see its none
That's because start_soon() doesn't return anything
So it is None
Do you want to get the result of a task?
@tulip hearth ^
No, i just wanted to type hint the task but i figured out that anyio works a bit differently than normal asyncio code i.e in asyncio i can do sometask = asyncio.create(somecoro) which returns a future object, turns out anyio wraps everything
tasks can only have side effects
that's the trio magic
O_o seems pretty magical indeed, its gonna take some time to adjust to this
Yeah if you wanted the result await it directly
👍
It's also parameters and a getitem
ah, that's true
Hi, first time I'm using overloads and I can't figure out how to solve this: ```py
@overload
def init(self, pattern: ValidPatternType, repetitions: int, or_more: bool = False):
...
@overload
def __init__(self, pattern: ValidPatternType, minimum: int, maximum: int):
...
def __init__(self, pattern: ValidPatternType, *args, **kwargs):
pass
Basically, I don't know how to extract the attributes I need from `*args` or `**kwargs`, since the variables can be both positional and keyword, I'm struggling a bit with doing this, isn't there a better way to parse them out so that it matches the signatures from the overloaded functions?
I could probably figure out a way to parse it out manually, but I'd also like it to give out an exception if the *args or **kwargs can't be passed in the way the user provided them. i.e. if the function is called like with pattern="x", repetitions=12, random_variable="x") it should raise an exception that's at least similar to what python would give out if it were a real function taking those parameters, this just seems incredibly complex to do just to have some overloads
so, I found out I can do this: ```py
def init(self, pattern: ValidPatternType, *args, **kwargs):
s1 = inspect.Signature(
parameters=(
inspect.Parameter("repetitions", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
inspect.Parameter("or_more", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
)
)
s2 = inspect.Signature(
parameters=(
inspect.Parameter("minimum", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD),
inspect.Parameter("maximum", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD)
)
)
try:
bound = s1.bind(*args, **kwargs)
i = bound.arguments["repetitions"]
j = None
or_more = bound.arguments["or_more"]
except TypeError:
bound = s2.bind(*args, **kwargs)
i = bound.arguments["minimum"]
j = bound.arguments["maximum"]
or_more = False
...
Why are you trying to use these overloads? If I were you I'd probably just write two different classmethods as alternative constructors
hmm, I could do that, though it would be a bit weird, I mean, I'd be defining 2 class methods that act very similarely to just init calls: ```py
class Foo:
def init(i: int, j: Optional[int] = None, or_more: bool = False):
...
@classmethod
def from_range(cls, minimum: int, maximum: int) -> "Foo":
return cls(minimum, maximum, False)
@classmethod
def from_repetitions(cls, repetitions: int, or_more: bool = False) -> "Foo":
return cls(repetitions, None, or_more)
To show the similarity, here are some examples for using init vs the class methods: ```py
x = Foo(1, 15)
x = Foo.from_range(1, 15)
y = Foo(15, or_more=True)
y = Foo.from_repetitions(15, or_more=True)
``` At this point, I might aswell just use the `__init__` alone, but to have a more descriptive way show up when initializing it I defined those overloads
I find the classmethods a lot clearer and easier to reason about
they're certainly cleaner, but it just feels weird to have a class method that essentially does the same as __init__, also there was another purpose behind those overloads which was that they restricted what could actually be passed into __init__, they prevented things like setting both i and j along with or_more=True. While this could be handled explicitly in __init__ it's just more annoying to do
but I suppose it's better than the signature mess
Its is possible to overload method, using metaclasses. Calling functions of a class with the same name - but different arguments.
Yet doing so would slow down the program at run time.
I would suggest renaming the functions, to what they do OR have an additional argument - to change how the code is run.
Just use keyword arguments 😅
If your class has either range or repetitions, why not make two classes?
@hidden spoke list[int] is a list where each element is an integer. You can't append a list of integers to it.
If you want a list of lists of integers, that would be list[list[int]]
Sounds like you need a proper class 🙂
perhaps a dataclass
I've never seen someone learn about type annotations before classes 👀
I guess that's a good strategy
!d dataclasses
Source code: Lib/dataclasses.py
This module provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes. It was originally described in PEP 557.
The member variables to use in these generated methods are defined using PEP 526 type annotations. For example, this code...
from typing import TypeVar, Iterable, Union
T = TypeVar('T')
Tree = Iterable[Union[T, 'Tree']]
this is valid, right? no type checkers ive tried seem to be able to handle the recursive aspect of it. am i doing it wrong? or recursive types are just poorly supported?
pyright supports recursive type aliases I believe. But this sort of thing is hard to solve for the typevar solver
ah, just remembered, __origin__ is a part of the API too
oh i was thinking of UnionType
!d classes
Graph types
NetworkX provides data structures and methods for storing graphs.
All NetworkX graph classes allow (hashable) Python objects as nodes and any Python object can be assigned as an edge attribute.
The choice of graph class depends on the structure of the graph you want to represent.
!d metaclasses
By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace).
The class creation process can be customized by passing the metaclass keyword argument in the class definition line, or by inheriting from an existing class that included such an argument. In the following example, both MyClass and MySubclass are instances of Meta...
what do you mean by "how they work"?
like, how do they detect inconsistencies in type hinting?
I want to contribute to mypy but the code feels too complex and when I see the in code comments, they have too many technical terms
ah, I have no idea how it works 🙂
it is pretty arcane
I tried making a plugin for mypy, I didn't go very far
oh
!rule 5
5. Do not provide or request help on projects that may break laws, breach terms of services, or are malicious or inappropriate.
not the right channel, not the right server
AFAIK it runs semantic analysis (https://github.com/python/mypy/blob/master/mypy/semanal.py#L1-L49) then actual type analysis (https://github.com/python/mypy/blob/master/mypy/typeanal.py) then the checker is ran (https://github.com/python/mypy/blob/master/mypy/checker.py)
I don't blame you, I've written a mypy plugin and the whole process was painful
I'm too stupid to understand mypy plugins
to be honest I don't really understand it anymore
it was just loads of trial and error really
I have wanted to write probably 3 or 4 different plugins and always got scared away
Yeah I regret even writing it lol
This bug has been fixed in pyright 1.193
Here's a cool trick that's now possible with TypeVarTuple:
https://paste.pythondiscord.com/okehayubut.py
@tail_rec
def factorial(n: int, acc: int) -> TailRec[int, int, int]:
if n <= 0:
return Return(acc)
return Call(n - 1, acc * n)
reveal_type(factorial) # function (int, int) -> int
is this a mypy bug?
Incompatible types in assignment (expression has type "Sequence[Union[Literal['Parent'], Literal['Expert'], Literal['Educator'], Literal['Individual'], Literal['Other']]]", variable has type "List[str]") [assignment]
those Literals are all strs 
is this some variance issue with List?
ah yep that was it
i had this, more or less:
# 3rd-party code
get_some_text() -> List[str]: ...
# my code
parse_foo(parts: Sequence[str]) -> Sequence[Literal['a', 'b']]: ...
foo = get_some_text()
foo = parse_foo(foo)
i changed it to this and it worked:
# 3rd-party code
get_some_text() -> List[str]: ...
# my code
parse_foo(parts: Sequence[str]) -> Sequence[Literal['a', 'b']]: ...
foo_raw = get_some_text()
foo = parse_foo(foo_raw)
--allow-redefinition 😄
no, but why wouldn't you always want it?
because imo it shouldn't be default, "failing open" as it were
easy to assign the wrong thing to the wrong thing just because of a typo
Couldn't you use ParamSpec here?
No, because ```py
class Call(Generic[Unpack[P]]):
def init(self, *args: Unpack[P]) -> None:
self.args = args
But it would if you'd allow both args and kwargs?
what would you put in the return type here?
@tail_rec
def factorial(n: int, acc: int) -> TailRec[???, int]:
if n <= 0:
return Return(acc)
return Call(n - 1, acc * n)
there isn't really a way to construct a ParamSpec from thin air
btw there should also be a return type here: ```py
def tail_rec(fn: Callable[[Unpack[P]], TailRec[Unpack[P], T]]) -> Callable[[Unpack[P]], T]:
...
but I removed it because of a pyright bug 🙂
TailRec[[int, int], int]
Does that work?
I've never constructed a ParamSpec other than from a Callable
I'm not sure, but that certainly doesn't include kwargs
lol nice error
just checked that this doesn't work
I think there are no paramspec literals
i still want to be able to derive a ParamSpec from a function and a Protocol from a class
and I want a flat in Moscow
make a mypy plugin 😉
i wish!
and i bet if i post that on python-ideas that's exactly what people will say
Protocol from a class is already a thing on the python/typing repo
param spec from a function sounds like what ambv suggested for callable syntax or maybe with the protocol pep
actually no https://github.com/microsoft/pyright/issues/2667
hm.. i don't really need special syntax for it. just "a ParamSpec that has the same args, kwargs, and return annotations as some specific function"
so uh if we have a class let's call it MyClass and we need to annotate an attribute of another class as being an instance of MyClass, do we do like var: MyClass = MyClass() or var: 'MyClass' = MyClass() or something else
Both work but the former is preferred. You need the quoted form in some unusual situation, most frequently when you're annotating a method as returning an instance of its own class.
ok cool thanks
To the type checker (who doesn't care about definition order) it treats them the same, you just need the string in cases where the class wouldn't be defined yet.
On classes though, annotations are assumed to be instance attributes - you'd need typing.ClassVar[TheType] to indicate class attrs.
if I have to define a tuple of int of length 100, how would I type hint it?
because for tuple, I have to mention the type of each individual element
is there a shorter way to do it?
no, afaik
tuple[int, ...] would be a tuple of any number of ints, and i think thats the closest alternative
Hi, is it possible to set type hints for **kwargs?
For now, only if the type is homogeneous
thank you
TypedDict **kwargs when 😳
Once I get PEP 464 into typing-extensions I'm planning to make **kwargs: Unpack[TypedDict] mean what you want it to mean in pyanalyze
Would still require a PEP for standardization though
wait, that's a thing?
that basically solves the nasty-kwargs problem
its not a thing yet
no I mean, in pyanalyze
Not officially. PEP 646 supports the corresponding thing for *args so it seems logical to make **kwargs work the same way
Not yet but it should be easy to add because I already have internal support for heterogeneous **kwargs
So just needs surface syntactic support
i personally dont really see the point in using a TypedDict for kwargs myself unless you are stubbing out a library. what other use cases are there that cant already be done now?
libraries which use pass-through kwargs, like matplotlib or aiohttp
You could have multiple wrapper functions for a function that takes a lot of kwargs
shouldnt you just hope for this #type-hinting message?
Like, every time I see this https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client.py#L312-L314
aiohttp/client.py lines 312 to 314
def request(
self, method: str, url: StrOrURL, **kwargs: Any
) -> "_RequestContextManager":```
it just breaks my heart
personally i dont see how moving the definition to somewhere else is particularly useful just seems like another situation of inline vs stubbed types
what was the point of carefully type hinting everything in _request if request is basically "you want to add an argument? well friend, you're up to a journey"
but what if?py def request( self, method: str, url: StrOrURL, **kwargs: ParametersOf[_request] ) -> "_RequestContextManager":
or something
i swear this reads so much better
instead of ```py
def request(
self, method: str, url: StrOrURL, **kwargs: Unpack[RequestFnKwargs]
) -> "_RequestContextManager":
that adds a new concept to the type system -- the ability to reference the type of a symbol
which is arguably more stuff to add to the already complex system
It's also not necessarily strong enough because you may not support all parameters of _request
as i said i feel like this should have come with 612
if we are only talking about pass through methods surely thats not something to be considered, theoretically you could do some gross stuff with Concatenate and add a Deconcatenate special form to do the opposite
this certainly could be a lot nicer if you could be named arguments in getitem aswell
Any considered harmful: ```py
return await response.json()
I think I made this mistake 3 times because I'm using httpx and not aiohttp
There is an argument to be made about what other languages do, and TypeScript does this for their equivalebt to kwargs
What's wrong?
response.json() is a blocking call that actually reads the whole response
for some reason httpx decided to cram an async response and a sync response into one class 🤷
so I had to do json.loads(await response.aread())
but because .json() returns Any, type checking passed
Why did you expect something else I am confused?
Oooooh
I expected it to be async or something
just like in aiohttp
But you should've gotten an error then trying to await a dictionary?
yes
Also, I don't understand this. Didn't you use AsyncClient?
I did.
Ah...
But it produces the same response object as the blocking client
list[list[int]] means a list of lists of int
if you mean a numpy array you'd do something different
I think mypy will recognize the type from later .append operations
I remember in mypy it's called "partial types" and it causes quite a bit of headaches
is there a type checking feature that doesn't cause quite a bit of headaches? 🙂
Any. It only solves problems.
x: list[Obj] = []
or
x = list[Obj]()
I am just looking at types in plain python3 (as opposed to mypy) and I noticed that type({}) returns <class 'dict'> but you can also use {} to create a set class, by doing myset = {1,2,5} then type(myset) returns <class 'set'>
is mypy a bit more "strongly" typed than generic python?
oh i showed up 5 hrs after that conversation. oh well.
The thing is, {} always becomes a dictionary. That's no special MyPy thing or special behaviour
!e print(type({,}))
@blazing nest :x: Your eval job has completed with return code 1.
001 | File "<string>", line 1
002 | print(type({,}))
003 | ^
004 | SyntaxError: invalid syntax
Basically, dicts were added to Python well before sets, so it had to stay that way for backwards compatibility.
There's no way to create an empty set with literals
Unless you count {*()}.
Not really, just unpacking a tuple. No colon, so it's a set. And the tuple is empty, so it's an empty set. Not too efficient though, it builds an empty set, then unpacks a tuple for no reason.
you almost never need to typehint self
Type checkers will deal with it correctly. However sometimes it's useful to specify it as a type var, so you can indicate the method returns/requires others of the same type (if the class is being subclassed).
Yeah.
@hidden spoke 
as you can see, Foo is hinted correctly, but BadFoo is not
so when you subclass BadFoo, everything kind breaks
is it because the self in BadFoo defaults to an invariant type?
Yeah, since it's not a typevar/generic at all.
No, it's because BadFoo.do_something always returns BadFoo
and this type doesn't automatically get changed when you make a subclass
because you could have something like this: ```py
class BadFoo:
def do_something(self) -> "BadFoo":
return BadFoo()
oh I see it's fixed to BadFoo that's not great
whats type hinting
see the second pin
thanks
typeHintingChannel.on(/does mypy not support (.+?) yet/, message => message.reply(":pensive:"))
😔
!ban @acoustic thicket self-bot 
:x: @trim tangle, you may not ban someone with an equal or higher top role.
Is there a way to type hint an instance of a class which inherits from some metaclass?
Like, I have ```py
class FooMeta(type):
...
class Foo(metaclass=FooMeta):
...
class Bar(metaclass=FooMeta):
...
and I want a type hint for an instance of Foo or an instance of Bar (or any other class based on FooMeta)
If I use FooMeta as the hint, it's for the class, not the instance
I don't think so
You could theoretically do it with a protocol
T = TypeVar("T", bound=type)
class InstanceOf(Protocol[T]):
class: T
Although it needs type ignores
mypy is just a type checker, a tool which checks all your types, not related to any cpython interpreter
in reply to it
mypy does also ship a Python compiler (mypyc)
I didn't know that
need to check
mypyc supports only what mypy supports
and mypyc isnt very fast, cython is better
mypyc classes cant be subclassed in pure python and patched
mypyc's benefit is that it should just directly speed up anything that currently can be typed checked by mypy
mypy has a lot of bugs and not implemented features
and mypyc build is very slow
cython build is a lot faster
You always need cython where heavy computations need to be run in a single thread and don't want to spawn processes, I know it's a lot of pain but we can never escape that GIL thing....
Until the nogil branch gets merged 🙂
😌
I wonder to create a multithreaded desktop application with Python and C++ specifically for running cpu bound operation in a separate thread
but I need to learn language binding for that
pybind11 is nice so is cython
what about swig?
never heard of it
SWIG supports many languages and by SWIG you can bind language by writing inline code, not sure about it. You can Google it.
can you set type of a variable that was created using __setattr__? Say for an example object.__setattr__(obj, 'var', 5).
how do I set type of var?
In the way I could have done var: int = 5, now if I use __setattr__, I can't define that var should be int
You just do var: int in the class block.
class Programme(object):
all_: dict[int, Programme] = {}
direct_conn: set
def __new__(cls: Type[Programme], p_id: str):
p_id = int(p_id)
if p_id not in cls.all_.keys():
obj = object.__new__(cls)
obj.direct_conn = set()
cls.all_[p_id] = obj
return cls.all_[p_id]
Found this work around. Let me know if there is anything wrong in this code
think this is what you suggested
Yep.
its probably a pycharm bug if you are using it
Which type can't you setL
if I do obj.direct_conn: set[Programme] in __new__, I get this error from pylance Type annotation not supported for this type of expressionPylance
I am using vscode
if you change obj to self does it work?
You don't need to do that there, that's what the annotation is doing in the class block.
but if I remove it from the class block, it still shows the same error
also you should probably use super().__new__(cls)
Change that to direct_conn: set[Programme].
yeah did that
And don't annotate in new or init.
what is the difference? i see the same thing. actually I was trying different things to figure out what would work, and then used object instead of super
why not?
Because it's redundant if you annotate at the class level.
You can do it, but they'd have to match.
I understand, but what if I want to annotate only in __new__? Pylance then shows that error. I don't understand what is wrong. But I guess I have found the solution
Because annotating self is a special case, normally annotationing attributes is invalid.
when passing in a FrozenSet to secrets.choice mypy complains:
Argument 1 to "choice" has incompatible type "FrozenSet[str]"; expected "Sequence[<nothing>]"
Isn't a FrozenSet a sequence? 🤔
No it doesn't support getitem
Ah
Atleast I don't think it does
checking
>>> dir(frozenset("abcd"))
['__and__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'copy', 'difference', 'intersection', 'isdisjoint', 'issubset', 'issuperset', 'symmetric_difference', 'union']
Alright, in this case I can coerce it to a string first
Sequence is actually a bit too strict, proposed https://github.com/python/typeshed/pull/6562 to widen it
Awesome, thank you!
While you're at it does random.choice have the same issue?
yeah probably but I don't know how to edit multiple files in the web editor 😄
but now there's a branch so I can just put it on the same branch
secrets.choice actually is just random._SystemRandom.choice
I was making my first abstract base class in Python and I thought "why not add type hints too?"
How do I annotate an abstract base class with properties?
With only a getter
!d abc.abstractproperty
@abc.abstractproperty```
Deprecated since version 3.3: It is now possible to use [`property`](https://docs.python.org/3/library/functions.html#property "property"), `property.getter()`, `property.setter()` and `property.deleter()` with [`abstractmethod()`](https://docs.python.org/3/library/abc.html#abc.abstractmethod "abc.abstractmethod"), making this decorator redundant.
A subclass of the built-in [`property()`](https://docs.python.org/3/library/functions.html#property "property"), indicating an abstract property.
This special case is deprecated, as the [`property()`](https://docs.python.org/3/library/functions.html#property "property") decorator is now correctly identified as abstract when applied to an abstract method:
```py
class C(ABC):
@property
@abstractmethod
def my_abstract_property(self):
...
```...
Yeah I've researched
it's deprecated but there's an explanation as to what to do 🙂
You can open it in GitHub.dev
And you can do that with comma or period from GitHub
huh?
Umm
The prefix is !. You can try !help in #bot-commands. This, however, is not the correct channel for testing the bot.
Show your code?
Pasting large amounts of code
If your code is too long to fit in a codeblock in discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the floppy disk icon in the top right, or by typing ctrl + S. After doing that, the URL should change. Copy the URL and post it here so others can see it.
And also what your mypy version is
0.910
try running this using the master version of mypy
(also you shouldnt use mysql here as it will block the bots event loop)
File "'mypy /main.py" line 165, in run_build WHERE fisher_id = %s
Seems odd
ya i noticed last night
Do you have a file called mypy/main.py in your cwd?
i feel like they must have just edited the file as they were running it right?
the file i posted is called main.py but it's not in a mypy folder
there's just a mypy.ini
Show the output from tree?
master branch worked thanks
hello, i have a sqlmodel base class that is the parent of all of my model classes. i added a get_or_create helper that accepts the same kwargs as __init__ and would like to type them, preferably generically on the base class. is that possible in python 3.10?
this is the class ```class BaseModel(SQLModel):
class Config:
arbitrary_types_allowed = True
uuid: Optional[UUID] = Field(default_factory=uuid4, primary_key=True)
timestamp: Optional[datetime] = Field(default_factory=datetime.utcnow)
@classmethod
def get_or_create(cls, session: Session, **kwargs):
instance = session.exec(select(cls).filter_by(**kwargs)).first()
if instance:
return instance
else:
instance = cls(**kwargs)
session.add(instance)
session.flush()
return instance```
You'd have to do it manually.
is there a way to do it without specifying every single class var?
and is there any pep that works towards this?
maybe an extension to paramspec?
you should use ParamSpec
mypy doesnt support it, but pyright does
how can ParamSpec be used for kwargs?
yeah im familiar with paramspec but i dont understand how to use it to type the kwargs of my get_or_create method
could you please demonstrate it with the BaseModle i posted above
P = ParamSpec('P')
T = TypeVar('T', bound=SQLModel)
class BaseModel(SQLModel):
...
def get_or_create(cls: type[T], session: Session, **kwargs: P.kwargs) -> T:
...
instance = cls(**kwargs) # it should be checked
...
i hope this should work
it first complains about missing args: "args" and "kwargs" members of ParamSpec must both appear within a function signature Pylance
and then after adding it ParamSpec "P" has no meaning in this context Pylance reportGeneralTypeIssues
and when get_or_create is used it says Param spec "P@get_or_create" has no bound value Pylance reportGeneralTypeIssues on the first kwarg
try this
import typing
P = typing.ParamSpec("P")
class SQLModel:
pass
class Session:
pass
T = typing.TypeVar("T", bound=SQLModel)
class BaseModel(SQLModel):
@classmethod
def get_or_create(
cls: typing.Callable[P, T],
session: Session,
/,
*args: P.args,
**kwargs: P.kwargs,
) -> T:
instance = cls(*args, **kwargs)
return instance
it works, thank you!
class BaseModel(SQLModel):
class Config:
arbitrary_types_allowed = True
uuid: Optional[UUID] = Field(default_factory=uuid4, primary_key=True)
timestamp: Optional[datetime] = Field(default_factory=datetime.utcnow)
@classmethod
def get_or_create(
cls: Callable[P, T],
session: Session,
/,
*args: P.args,
**kwargs: P.kwargs,
) -> T:
statement = select(cast(type[T], cls)).filter_by(**kwargs)
instance = session.exec(statement).first()
if instance:
return instance
else:
instance = cls(**kwargs)
session.add(instance)
session.flush()
return instance```
type hints in cpython are really coming along
is there a resource that gives a good overview of all type hint related stuff?
check the pins
neat, thanks!
POOL: Optional[aiomysql.pool.Pool] = None
do you need the optional for a global variable with a default of None?
yes
you don't if you type ignore 😉
that is, if you know you'll initialise it before you ever use it, not using Optional + using type ignore can be useful, so you don't have to do assert POOL is not None everywhere you use it
(of course, then the type checker won't help you catch places where you might not have initialised POOL)
How would you type hint getattr functions?
You mean the __getattr__ method?
yeah, basically I have a util function which I want to typehint
def _recursive_getattr(obj: typing.Any, attribute: str) -> typing.Any:
"""Get an attribute recursively. All `.` in attribute will be accessed recursively."""
for name in attribute.split("."):
obj = getattr(obj, name)
return obj
is that mine? lol
Yeah you're not going to get much other than Any for that
yes lol
with pyanalyze you'd be able to write a custom plugin that handles this function
but wouldn't obj always be a class object and the return type be a property/class var, so there may be a better way
good because I was starting to think I implemented it much less efficient than the above
The function you posted isn't limited to class objects
^ iirc I'm using it on class instances as well as types
but only classes have attrs 
every object in Python has attributes
and every attribute is an object, fun times
any reason asyncio.sleep uses ... instead of None which is the default?
https://github.com/python/typeshed/blob/master/stdlib/asyncio/tasks.pyi#L233
https://docs.python.org/3/library/asyncio-task.html#asyncio.sleep
stdlib/asyncio/tasks.pyi line 233
def sleep(delay: float, result: _T = ...) -> Future[_T]: ...```
defaults arent put in stubs
why not?
in this case knowing the return type is None when no argument is provided would be useful
idk check pep 484
well you can tell from the type var that thats the case can you not?
🤔
This particular rule isn't in PEP 484, it's a convention we adopted in typeshed
this is annoying: Type of "await asyncio.sleep(1)" is "_T@sleep"
because we can clearly know it should be None
We might actually change it soon because it's useful for language servers to know the actual defaults
out of curiosity when do you use the result argument with asyncio.sleep?
I dont, which is actually the problem 🙃
I have the strictest options on when using pyrigth, soo .....
Result of call expression is of type "_T@sleep" and is not used; assign to variable "_" if this is intentional
I can of course just slap on a # type: ignore (or set the argument explicitly), but it feels like this should not be a issue to begin with
that seems to be way more of an annoyance than a benefit
you could probably do
_ = sleep()
I can, and that I was I opted to do
yhe it is a strange one, like wont you already have that value, why have sleep return it
from what i read on a not particularly convincing stack overflow post, they just used it for mocking a database call
after a quick google, one use case is passing it as a coroutine to another function that expects a result
So it's for the benefit of type checking?
no, imagine you have a function that takes a coroutine, awaits it and then uses the result ```py
import asyncio
async def foo(cor):
result = await cor
do_something(result)
async def bar():
I want to make foo sleep 2 seconds, and use this result
await foo(asyncio.sleep(2, result="abc"))``` that is one usecase I can think of
I cant think of a real world example of this, maybe for testing stuff
right
ofc if you dont want it to sleep you would use 0, or just another func like py async def returns_abc(): return "abc"
ok gotcha
What's the best way to type around black-box functions like pickle? i.e.
import pickle
def deserialize() -> int:
return pickle.load(open("my_local_file", 'rb'))
➜ pyexample python3 -m mypy main.py --strict
main.py:4: error: Returning Any from function declared to return "int"
Found 1 error in 1 file (checked 1 source file)****
It feels counter-intuitive to have to define a local variable when it's being immediately returned, and the type is signified by the return signature.
I think you can turn off this rule in mypy.
Generally, this signature is a lie. You never check that it's really an int.
or you could cast it if you know it's right
yes
!d typing.cast
typing.cast(typ, val)```
Cast a value to a type.
This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).
if you dont want to do that
I think typing cast might be the best option, thanks! Ultimately this is a toy example, and some cases cannot be cast in a real way, so typing.cast seems like an okay alternative
wdym 'cast in a real way'?
If I'm loading in a Torch model, I wouldn't want to then attempt to re-initialize that object.
you could still insert a runtime assert isinstance(..., Torch model)
id just # type: ignore cause this isnt worth casting over imo
typing.cast does not do anything at runtime
Yes, I'm agreeing with you - the difference between casting to the toy example, i.e. int(x) and cast(x, int
it's just a signal to the typechecker to narrow the type
it would be nice now that typing has a c module if this like NewType.call was just a no-op
it does?
you can't really do that kind of optimization in Python
at least without a JIT or something
Mark Shannon claims that at some point calling a Python function will be faster than calling a C function 🙂
🙂

Quick question, what's better for a (big) project - typing or __future__ module for #type-hinting .
what do you mean with the future module? annotations?
What Python versions do you support?
Thinking typing is the way to go, for a project at such scale, yet saw that __future__ (annotations) is very popular as of late.
Only 3.X, or more specifically 3.6, 3.6.5, 3.7, 3.8, 3.9, 3.10 .
Then from __future__ import annotations isn't an option because it's not in 3.6
You'll need to use things from typing no matter what you do, the future import just (may) allows you to use certain things even if they're not valid in that python version
Those two aren't really exclusive alternatives though
it'll also break the moment something tries to inspect the annotations that were postponed and aren't valid
Just as I thought, this confirms it. Many thanks for the tip.

is there something I am missing here? to me this looks like it should work ```py
from typing import ParamSpec, Concatenate, Generic, Callable, Any
P = ParamSpec("P")
class Foo(Generic[P]):
def init(self, func: Callable[P, Any]) -> None: ...
def bar(baz: Foo[Concatenate[int, P]]) -> Foo[P]: ...
def test(a: int, b: str) -> str: ...
abc = Foo(test)
reveal_type(abc)
bar(abc)but pyright is not happy
Type of "abc" is "Foo[(a: int, b: str)]"
Argument of type "Foo[(a: int, b: str)]" cannot be assigned to parameter "baz" of type "Foo[(Concatenate[int, P@bar])]" in function "bar"
TypeVar "P@Foo" is invariant
Type "(a: int, b: str)" cannot be assigned to type "(Concatenate[int, P@bar])"
Parameter 1: type "Concatenate[int, P@bar]" cannot be assigned to type "int"
Function accepts too few positional parameters; expected 2 but received 1
Keyword parameter "b" is missing in destination```
sounds like a bug\
it does feel like a bug, I was just not sure as it was my first time using Concatenate
it do be like that sometimes lol
I am gonna create a issue then and hope for the best
6 hours later: "as designed, just read the PEP"
I did have a quick look in the pep, and a example really similar to this is in it, so......
I tried to play around with it a little bit, it wasn't very fruitful
from the pep ```py
T = TypeVar("T")
P_2 = ParamSpec("P_2")
class X(Generic[T, P]):
f: Callable[P, int]
x: T
def f(x: X[int, P_2]) -> str: ... # Accepted
def f(x: X[int, Concatenate[int, P_2]]) -> str: ... # Accepted
def f(x: X[int, [int, bool]]) -> str: ... # Accepted
def f(x: X[int, ...]) -> str: ... # Accepted
def f(x: X[int, int]) -> str: ... # Rejected``` so I feel like it should be supported
Thats cool
the code I wrote that lead me to find this was py def with_argument( option: traits.CommandOption[O] ) -> Callable[[_SlashCommand[Concatenate[O, P]]],_SlashCommand[P]]: def decorator(command: _SlashCommand[Concatenate[O, P]]) -> _SlashCommand[P]: command.options.append(option) return command # type: ignore return decorator
well issue created, now I sleep 🙃
I think I would use TypeVar for this
could make it a little more readable
Let's say I have a generic type, class B(Generic[T]): ..., what do I call something like B[int]? A B <foo>, what is <foo> here? (it's not exactly a subclass)
instantiation of the generic type is the name I hear often
!e
print(list[int].__origin__)
print(type(list[int]))
print(type(list[int].__origin__))
@tranquil turtle :white_check_mark: Your eval job has completed with return code 0.
001 | <class 'list'>
002 | <class 'types.GenericAlias'>
003 | <class 'type'>
int is the type argument if that's what you're asking
Like with function parameters and arguments.
You can think of B as a function taking a type and returning a type
why std library isnt annotated?
It is
But FR there are concerns about speed also making things slower, maintainers not wanting to update the code and it really slowing down typeshed stuff
what is "FR"?
for real
Does anyone know what the two type parameters for numpy.ndarray are?
_ShapeType, _DType_co
Is there any way I can find some examples? I tried searching for the Numpy typing docs, but they are very sparse
shape type is any and _DType_co is numpy.dtype
the first param doesnt seem useful atm but im not an expert
Okay thanks a lot!
i think pep 646 will bring support for it
Hello! Can someone explain me why the type of custom_id is still str | None even after that if?
The type checker doesn't know that self.custom_id and custom_id are the same
if you want it to work just set custom_id to custom_id or os.urandom(16).hex()
I'd generally recommend only assigning the instance attribute once
Some of the annotations would still have to use .pyi files for the C extensions yeah?
for builtins and _asyncio etc yeah
is there a way for a generic to only be considered a subclass of a Abstract class for one of its instances, to be specific, I have ```py
class ApplicationCommand(ABC):
@abstractmethod
def convert_to_dict(self) -> internal.CommandStructure:
...
class _SlashCommand(ApplicationCommand, Generic[P]):
def init(
self,
...j
func: CommandCallback[P],
) -> None:
...
def convert_to_dict(self: _SlashCommand[[]]) -> internal.CommandStructure:
...``` So if a function takes a `command: ApplicationCommand` I only want a `_SlashCommand[[]]` to be a valid type to pass, I know I could do a union in the function `command: ApplicationCommand | _SlashCommand[[]]` , but I would like to keep `_SlashCommand` a subclass of `ApplicationCommand` as that makes sense
because _SlashCommand is a ApplicationCommand, but it is only valid to be passed to functions taking ApplicationCommand when P is []
(because functions taking ApplicationCommand are going to call convert_to_dict, which is only valid for _SlashCommand[[]])
Sounds like you want to use Protocols
oh right, that would work
completely forgot about those
ah, but, other classes also have convert_to_dict methods that are not subclasses of ApplicationCommand
oh wait, that is not a issue, because those other methods dont return a CommandStructure 🤦
@boreal ingot I would expect convert_to_dict to return a dict 🙂
CommandStructure is a TypedDict
its probably a typed dict
Hey guys, do any of you know what is wrong with this?
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
I have been breaking my head for quite a while about this x)
Trying to add stubs to an external ORM library that doesnt have them and it is proving itself to be quite challenging
The first error about The erased type... might just be a mypy limitation, but if that is the case that is fine since I can just add a @classmethod decorator and mypy is fine with it then and the fact that these are just stubs
The second error I don't really know how to solve, and finally the type is revealed to be ItemSet*[T'-1] but is should in theory be ItemSet*[FooItem*]
any suggestions for a linter to check only for real errors? No styling or any of that nonsense.
it should catch things like missing or misnamed function parameters
If you are in vsc there is also Pylance
what is actually a language server?
Like, is there some remote server from which my editor gets autocompletion?
this explains it pretty well https://microsoft.github.io/language-server-protocol/
basically, it's like an editor plugin, but editor-agnostic
Looks like a mypy issue
Just tried the same with Pyright x)
Is there a way to subclass pathlib.Path that Mypy accepts? I'm at a loss.
adding extra methods
Well, can you give an example?
Just subclassing and adding random unrelated methods wont make it complain
well the issue is you must do it like:
# https://github.com/python/cpython/blob/7b78e7f9fd77bb3280ee39fb74b86772a7d46a70/Lib/pathlib.py#L1067
class Path(pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath):
Lib/pathlib.py line 1067
cls = WindowsPath if os.name == 'nt' else PosixPath```
mypy will probably like it better if you do something like if os.name == "nt": _PathBase = pathlib.WindowsPath else: _PathBase = pathlib.PosixPath; class MyPath(_PathBase): ...
oh you need to use sys.platform
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
I really thought it would evolve making your own small type factory xD
ty! why does that work? 😮
tyvm!!!
There's a few specific conditions type checkers look for, sys.version_info is another (in case different Python versions change the definition).
Actually those are the only ones: https://www.python.org/dev/peps/pep-0484/#version-and-platform-checking
Oof, just learned Pylance doesn't support the Numpy typing stuff yet --- until numpy 1.22.x gets out of rc. :'[ Also, is there a place for users to give samples of their code for others to check their style/linting/etc.? I'd like to get a bit better at my readability.
!paste ?
Pasting large amounts of code
If your code is too long to fit in a codeblock in discord, you can paste your code here:
https://paste.pythondiscord.com/
After pasting your code, save it by clicking the floppy disk icon in the top right, or by typing ctrl + S. After doing that, the URL should change. Copy the URL and post it here so others can see it.
Is this a valid use of ParamSpec? ```py
from typing_extensions import ParamSpec
from typing import Dict, Generic, Optional
P = ParamSpec('P')
class HTTP:
def init(self, headers: Optional[Dict[str, str]] = None) -> None:
...
class _Base(Generic[P]):
def init(self, foo: str, *args: P.args, **kwargs: P.kwargs) -> None:
...
Base = _Base[HTTP]
class Sub(Base):
def init(self) -> None:
super().init(foo='foo', headers={'foo': 'bar'})
Pyright complains: ```
foo.py:23:37 - error: No parameter named "headers" (reportGeneralTypeIssues)
foo.py:23:9 - error: Argument missing for parameter "__p0" (reportGeneralTypeIssues)
It's not in the pep but it would be a nice to have feature
Oooh
That's so cool though
What if you "convert" HTTP into Callable[P, HTTP]?
Since Type[HTTP] is an instance of type() that when called returns an instance of HTTP.
That doesn't work because pyright then complains that class Sub(Base): is missing a type variable
you can use github.com/python/typing/discussions
No it's not?
Yeah nevermind, I don't know how I got that error originally but retried it and the same original error occurs
(probably)
Oh :(
I guess you can use TypeVarTuple
If you need just the positional args, it's like 42069 times better
I need the keyword arguments unfortunately
though maybe a annotating with a decorator that takes a callable of param to a callable of a callable that concas both would work
what do you want to do, generally?
I'm using a wrapper over httpx clients, e.g. https://github.com/RobertCraigie/prisma-client-py/blob/main/src/prisma/_async_http.py
I actually probably could refactor it so I don't need the wrapper, I only originally wrote the wrapper as I needed to have a common interface spanning multiple different http client libraries but as I just support httpx now it could be refactored
I gave up trying to strictly type the kwargs as it's just used internally anyway
hello, i have a that might as well end up in #esoteric-python or #internals-and-peps but i'll check here first 🙂
in https://github.com/amogorkon/justuse we're working on a way to programmatically decorate everything in a loaded module that matches some given filter
currently we only have a way to filter by a checking type (function, class, method etc) and name (via regex)
but we're pondering ways to filter by signature
so, what i'm wondering whether there is a way to check if a given signature is a subset of another signature
like list would be a match to list|np.array
No standardized way. Obviously every type checker will have some logic that works kind of like what you need, but it's not necessarily easy to extract.
i need it during runtime
something other then typing everything by hand with regex
My typechecker https://github.com/quora/pyanalyze would allow this, you'd call pyanalyze.annotations.type_from_runtime and then use the .can_assign method. I'm guessing runtime typecheckers like pydantic would also support this, but no idea what the API would be.
like individual functions or "collections" of functions to be filtered by signature
how would that work with subsets of signatures?
Not entirely clear on what you need there, but it may be doable by fiddling with the direction of the can_assign
Like you want to match every function that takes a parameter typed as a list?
yes
yeah
I guess then you could use inspect.signature and iterate over the parameters?
does that work with complex signatures like list|np.array or containers?
yeah, you'd still need something (like pyanalyze) that knows to deal with that
Can you clarify why you need this though? I read over the README of your repo and it's not clear to me what you'd want this for
if you look at https://github.com/amogorkon/justuse/blob/unstable/docs/Showcase.ipynb, fairly at the end
you can decorate everything in one go, for instance to log, debug, time..
and we want to be to filter not only everything
but multiple functions aswell, not just everything
Right, I don't see why you'd want to decorate "all functions that take a list as an argument" though
it's a first step to get an overview, there's very little to go on property-wise
actually gives me an idea
we already build an html to get an overview for a dry-run "what-if" decoration.. we could build on that and provide a neat interface with different filters so the user only needs to copy&paste the line into their code
then it's easier to combine different filters
like "all functions that return a np.array, name starting with "sig"
and the user could directly see which functions would get hit
great
we got the idea of what we can try
I'm gonna do some stuff with decorators in vscode and then think about how to implement it
excellent
is there a reason types.GenericAlias doesnt set __orig_class__?
There doesn't seem to be a reference to that attribute in the python docs
there are a lot of typing things that are completely undocumented
__org_bases__ is another thing
not even sure why this is the case
they are incredibly useful for introspection
What's supposed to be in orig class?
its the subscripted generic when subclassing something
mro_entries overwrites it in bases
In the end GenericAlias just seems to be type(list[int]) with no changes to it anyway
In [11]: class this(list[int]):
...: ...
...:
In [12]: this.__bases__
Out[12]: (list,)
In [13]: this.__orig_bases__
Out[13]: (list[int],)```
this is what i mean
I don't think type is a subclass like that though
Ah, I thought you were using it on GenericAlias itself
Looks like this is impossible to do without the Self type
That is interesting as I thought Self would just be for convenience and clarity
it should but the way things are special cased for it in type checkers makes it possible for things where its descriptors and it might be hard for them to normally understand whats going on there
jelle said this was something they wanted to see improve in the issue you made on the typing repo i think
Oh wow, small world, I was just testing around with your fork 😂
Yeah I ended up opening an issue on Pyright as they are currently the only ones supporting pep 673 and they said it was just a limitation of the checkers
anyways im gonna try and fix whatevers up my fork
can sm1 explain to me the need for mypy etc. when using annotations along with an IDE? i don't really understand what else i get from using it.
a dedicated type checker will be stricter and helps you catch more errors
depending on what ide you use there might not be a point using mypy
if you use something that uses pyright like vsc or maybe sublime its pointless imo
!remind 24h
Sorry, you can't do that here!
i use pycharm. they have type hinting, but idk what they use for it. it says
PyCharm supports type hinting in function annotations and type comments using the typing module and the format defined by PEP 484
can you explain how type hinting can be more or less strict? i thought this is a fairly simple task -> either the type matches or not
pycharms type checking is very very lenient and flat out wrong sometimes, using mypy with it will definitely catch bugs that pycharm doesnt
Yeah usually pycharm will just let things through it it's not something simple
It's also not very good in figuring out types in certain situations, e.g. it couldn't figure out the types from an iterable that had a typehinted iter
if you decide to use it I would recommend adding mypy as a precommit script on pycharm or bind the command to a hotkey using plain mypy. The pycharm extension is unbearably slow and buggy imo
you can't put PyCharm into your CI 🙂
also, not everyone on your team will use PyCharm
so there needs to be some reusable program that checks types.
Is there a standard/good way to type hint mapping keys?
mappings not dicts?
Is there a difference between the keys you can use for mappings and dicts? If there is, then I mean dicts
I'm not sure I understand your question. You can use annotations like Mapping[int, str] or dict[int, str] to indicate that you want a Mapping (or concretely a dict) with int keys
There is also TypedDict for dicts with specific string keys
class typing.TypedDict(dict)```
Special construct to add type hints to a dictionary. At runtime it is a plain [`dict`](https://docs.python.org/3/library/stdtypes.html#dict "dict").
`TypedDict` declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers. Usage...
No I mean how can I type hint "anything that can be used as a dict key"
oh Hashable
Ah thanks
Though historically that hasn't necessarily worked too well because hashability is inherited in weird ways. Haven't tried using it recently though.
That sounds like an uncommon use case anyway so I won't worry about it too much
The problem is that object.__hash__ exists, and it's only subclasses of that that then remove hashability.
In typeshed right now, mappings and sets accept any object as a key.
If multiple users are working on a project you would want to setup mypy in your CI pipeline 🙂
PyCharm doesn't detect all the errors that mypy does when using stricter configuration e.g. no-untyped-defs ...
Yep, plus different people use different ide's
And sometimes you can forget to run mypy before committing 😵💫
hmm, does anyone have any idea why usage/integration of signature objects/changes has not been considered for pep 612 (parameter specification variables) and pep 677 (function type specs)
You want to change a function signature or do you mean an inspect.Signature?
@soft matrix in a way both - Callable[somesignature] would be a nice addition, in particular of parameter specs worked in a similar way (stuff like declared wrappers/changing decorators automatically fixing up type declarations, overrides and signatures in one go would be neat)
I think the authors were just concerned it would be too much to implement in one step
At the typing meetups there was actually a lot of discussion of providing a more powerful syntax for callables, but the authors of PEP 677 decided to stick with only positional-only + ParamSpec because their data showed little usage of more complicated signatures
i'd be curious about that data, because my subjective experience is the opposite, that people use extremely complicated / multi-function interfaces all the time without even thinking that they're complicated, because python is dynamically typed so it just doesn't matter
or maybe it's just a handful of libraries like django, pandas, etc
I sometimes see people complain that static types make everything "bloated" and "verbose". But very often the issue is that the API is really complicated, and has pretty complex invariants that you have to hold in your head.
this from 10:48: https://youtu.be/VUThGgxOf28?t=648 (from the creator of teal, a gradual typing thing for Lua)
I think it's linked in the PEP. Pradeep analyzed typeshed and a few other projects
Yeah, with untyped APIs it's often really hard to figure out what types are actually expected
Sometimes though, it is kind of the fault of the type checker not to allow to express common idioms.
For example, mypy doesn't have narrowing of TypedDicts based on a key existing or not. That's the sort of thing that I mean.
Wait... what the hell? mypy disallows @final on a TypedDict?
what's the reasoning behind that?
mypy tends to be very restrictive in disallowing things it doesn't understand
probably it just disallows all decorators on TypedDiict?
pyright made a nice addition where you can do type narrowing on final TypedDicts:
@final
class A(TypedDict):
a: int
b: str
@final
class B(TypedDict):
b: int
c: str
def f(ab: A | B):
if "a" in ab:
# ab s A here
else:
# ab is B here
so it's pretty disappointing to see that code using this feature will now be totally incompatible with mypy
well, I guess you can put a # type: ignore on the final
It's pretty strange that you can't @final a TypedDict, given that you can subclass them...
i wish you could decorate a class (including TypedDict) with @frozen that prevents mutation at the type-checker level (other than in __init__), or maybe just prevents mutation by things other than instance methods
then you could have things like subclassing a TypedDict with a more-specific type
theres an issue open on the typing github about this
@frozen
class CMSEntry:
id: str
content_type: str
class Article(CMSEntry):
content_type: Literal['article']
what's the repo/issue?
oh it got closed
hm i guess they decided to go with typing.Property
no i think they are going with builtins.property but that wouldnt solve the issue
no, it wouldn't. should someone comment and request that it be reopened, since the special case proposed does not actually cover the original issue?
Im struggling with how to type hint a Proxy object, that exposes the underlying objects attributes dynamically. Specifically, the Proxy object should match the Protocol that matches the underlying object. Heres a toy example:
from typing import Protocol
class TestMethod(Protocol):
def test(self):
...
class A:
def test(self):
print("works!")
class Proxy:
def __init__(self, _obj):
self._obj = _obj
def __getattr__(self, name: str):
return getattr(self._obj, name)
a = A()
p = Proxy(a)
c: TestMethod = p
c.test() # works!
As you can see the Proxy works and allows me to call the method of the underlying object. I just have no clue what i would need to do to typehint this.
This is the error in case its relevant, on the second to last line.
Expression of type "Proxy" cannot be assigned to declared type "TestMethod"
"Proxy" is incompatible with protocol "TestMethod"
"test" is not present
my current line of thought is that i would need to somehow typehint the return type of getattr to the underlying objects attribute type for the name, though i have no clue how to accomplish this.
@dim trail that's impossible
your best option is p: A = Proxy(a) # type: ignore
or perhaps p: TestMethod = Proxy(a) # type: ignore
You could add a classmethod to the proxy class that is typed to return the original object
E.g. ```py
T = TypeVar('T', bound=TestMethod)
class Proxy:
@classmethod
def proxy(cls, obj: T) -> T:
# type ignore required as we are tricking the type checker
return cls(obj) # type: ignore
p = Proxy.proxy(a)
c = p.test()
The disadvantage of this approach is that you will then have to type ignore any attribute access for the actual proxy class
Another solution could be subclassing the protocol, however you would then need to create a new proxy class if you want to proxy other protocols
I've never heard of that in terms of typing, is there a pep?
Not currently some people are working on it
Not sure when it will be out
proxy = classmethod[[type[Self], T], Self & T]
Would be my thinking
that's also something I've seen as well..
@trim tangle @hasty hull thank you for the responses. i think i will try to figure out a way to do what im trying to do without the proxy object, as none of the possible solutions make sense in my case (deciding what instance of the proxied class to use when the attribute is accessed)
I am totally lost in typehinting this:
def validate_sent_data(validation_form) -> Callable:
def decorated(f) -> Callable:
@wraps(f)
def inner(*args, **kwargs) -> tuple[Response, int]:
if request.method == "GET":
form = validation_form(request.args)
else:
valid_json_data, error_http_message = check_valid_json_data(
request
)
if error_http_message or not valid_json_data:
return jsonify(error_http_message), 400
multi_dict = MyMultiDict(valid_json_data)
form = validation_form(multi_dict)
if not form.validate():
form_errors = {}
field_code_name: str
for field_code_name in form.data:
# match fields errors keys names from a request
form_field = getattr(form, field_code_name)
field_error = form.errors.get(field_code_name)
if field_error:
form_errors[form_field.name] = field_error
return make_error_response_bad_params(form_errors), 400
return f(form, *args, **kwargs)
return inner
return decorated
Could you help me?
Details:
validation_formis a subclass of Flask-WTFForm(so e.g.MyCustomForm)- The presented decorator is used to validate Flask's requests arguments (both
GETand the ones with body) - see the photo
tuple[Response, int]is correct becausemake_error_response_bad_paramsis a wrapper around (Flask's)jsonify
What should *args, **kwargs be?
Especially - what kind of Callable should I use and how to - generally - improve this code
Oh wait, I seee
It's rather empty by the way
Unfortunately you can't do this with mypy yet
So what type-checker can do this?
Probably pyright
yep
https://pyright-playground.decorator-factory.su/?gist_id=d5880aa4f761ab37372dedb98170cc70&filename=example.py @deep pendant
(there's a type_reveal at the bottom)
Okey, I will see.
If you want to use mypy, there's probably something in mypy-extensions https://pypi.org/project/mypy-extensions/
nice page, been looking for a online pyright one for a while. tho i instantly found a visual bug https://img.laundmo.com/u/jeP59Q.png
Yeah, it's annoying
Maybe someone can send a PR 🙂 https://github.com/decorator-factory/pyright-playground
quite old 😦
No, wrong package
but theoretically it should be possible with a mypy extension
wait, I forgot that the code is shit
but hopefully that will not be a major obstacle
https://github.com/python/mypy/issues/8645
There are changes lately 😉
yes, mypy does have some basic ParamSpec support now
There is a comparison of type checkers? Especially pytype, pyre-check, pyright and mypy...
There is a lot of them
I'm not aware of any general comparison doc. I'll say that mypy and pyright are probably the only ones with widespread usage outside the companies they derive from.
I follow the github trackers for all of them, and pyre and especially pytype rarely get outside bug reports
Isn't a Google's pytype a tool to get better, still bad (codebase)?
pyright also looks more mature regarding PEP implementation - but yea, I will not judge 😄
pyright mypy
has a plugin NO YES
system?
fast? YES NO
up to date with
the latest PEPS, YES NO
including experi-
mental ones?
So what should I write in requirements.dev.txt in order to use pyright? NPM directly? pyright from PyPi is weird
pyright from PyPi is weird
You're welcome lol
What do you mean by weird?
It's just a wrapper over the npm CLI that downloads node for you if you don't already have it
I personally wouldn't want to install pyright via pip 👀
how dare you
at least it doesn't make remote calls to the pyright playground API 🙂
don't give people ideas
I only have $3.38 on my cloud account 😦
although that's probably going to be a few million calls
I already got billed 0.04 roubles for storing docker images
Holy xD
Callable[[Callable[Concatenate[F, P], None]], Callable[P, tuple[Response, int]]]
pep 677 when?
😂
it is a bit cringe indeed
there isn't a good way to extract this to a type alias, unfortunately
I mean with 677 that would become
((F, **P) -> None) -> ((P) -> tuple[Response, int])
The prospects for PEP 677 aren't looking good on the Python mailing list, unfortunately
Humm, I hand't seen the mailing list, interested to see their reasons
I tend to just use Protocol
I'd rather have a nicer lambda syntax
i wish they had used something other than Concatenate
or given us an infix operator + as shorthand
what's against it?
what do os.PathLike[str] and os.PathLike[bytes] indicate (the generic argument)?
The __path__ method returns either str or bytes
People think it complicates Python syntax too much for little gain
thanks
I just put up a simple tool on pypi to add obvious type annotations: https://github.com/JelleZijlstra/autotyping. I added thousands of annotations with it today, so I hope it's useful for others too!
.bm that looks amazing!
React with 📌 to be sent your very own bookmark to [this message](#type-hinting message).
pyright --verifytypes gives me an error for the provided code: ```py
class SnowflakeList(array.array):
...
discord.utils.SnowflakeList
error: Type of base class "array.array" is partially unknown
Type argument 1 for class "array" has unknown type
When I do `class SnowflakeList(array.array[int])` the error goes away, but subscripting `array.array` like this fails at runtime. Is this an invalid error or is there a proper way to type this?
Yeah this is a known awkward case: the class is conceptually generic but is not marked generic in the stub. Normally the workaround is to quote it, like "array.array[int]", but obviously that won't work for a base class.
So the only solution I'm aware of is something like if TYPE_CHECKING: _Base = arrray.array[int] else: _Base = array.array class SnowflakeList(_Base):
alright, thanks
Just up the base version to 3.9 :)
__fspath__
Hello. I have a class, whose init which looks like this
def __init__(
self,
author: Union[discord.User, discord.Member],
options: List[SelectOption],
content: List[str] = [],
embeds: List[Optional[discord.Embed]] = [],
clear_on_timeout=False,
timeout: int = 60,
) -> None:
And I initialise the class like
ClassName(self.interaction.user, options, embeds=embeds)
But pylance gives this error
Argument of type "List[Embed]" cannot be assigned to parameter "embeds" of type "List[Embed | None]" in function "__init__"
TypeVar "_T@list" is invariant
Type "Embed" cannot be assigned to type "Embed | None"
Type cannot be assigned to type "None"
What am I doing wrong here?
That's because lists are invariant. Consider this:
def f(xs: list[int | None]):
f.append(1)
f.append(None)
ys: list[int] = [4, 3, 2]
f(ys) # oops
You can read more here https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types
What you should do is accept Sequence[Embed | None].
And don't use mutable defaults. But that's a separate issue
That worked, but I am not sure how it solved the issue
Sequence is covariant because the snippet above cannot happen with it
hmm I see
and also, can you elaborate? Might as well fix some more issues while at it
content: List[str] = [],
lists is mutable, so you shouldnt set empty list as default value
!e
def f(l = []):
l.append(0)
return l
print(f())
print(f())
print(f())
print(f([]))
print(f([]))
@tranquil turtle :white_check_mark: Your eval job has completed with return code 0.
001 | [0]
002 | [0, 0]
003 | [0, 0, 0]
004 | [0]
005 | [0]
I see
what should I be doing then?
Is it better to pass in the list rather than using default?
The normal workaround is to set the default to None and then inside the function do if content is None: content = []
yes
Because I do not do any assignment to the lists
That's actually what I usually try to do. Mutating your arguments is a risky thing to do anyway.
Ah
Yep. For mappings there's mappingproxy
Oh, so like default=types.MappingProxyType({})? Hadn't thought of that
Yep
What would be the type of object instance of a class? i.e count
import timeit
import typing
class Static:
counter = 0
def __setattr__(self, name, value):
setattr(self, name, value)
def __delattr__(self, name):
del self.name
@staticmethod
def myfunc():
Static.counter += 1
return Static.counter
if __name__ == '__main__':
def main1():
count = Static() #with instance of class and slower
print(f"First call : {count.myfunc()}")
print(f"Second call : {count.myfunc()}")
print(f"Third call : {count.myfunc()}")
return count
Static?
The return of main1() function I'm talking about.
-> Static
Yes, just like int means an integer
No need to import typing module for that?
Isn't there any build-in types there in the typing module for this?
no you dont need to use the typing module here
okay.
https://mypy-play.net/?mypy=master&python=3.10&gist=20f66b99e2a121b83dca27eadffcab9c&flags=strict I'm trying out the new ParamSpec - but it looks like it doesn't support Generics?
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
It's limited and I think the focus was on getting typeshed ParamSpec'ed functions to work
the reveal_type seems to show the correct annotation
__main__.SyncToAsync[def [T] (delay: builtins.float, value: T`-1), T`-1]
is there documentation on the known limits?
the blog post http://mypy-lang.blogspot.com/2021/12/mypy-0930-released.html has a little
We’ve just uploaded mypy 0.930 to the Python Package Index ( PyPI ). Mypy is a static type checker for Python. This release includes new ...
Note that typing.Concatenate and some other use cases are still not supported.
i didn't even know about NotRequired
very happy to see ParamSpec and TypeAlias support
i also really need to start using TypeGuard... we have one application at work with a lot of TypedDicts and there are currently casts all over the code to deal with it
I asked on gitter https://matrix.to/#/!FjKyUUyKpKFUvCNnMz:gitter.im/$CVfQ_UH6zkYJI7rlWULLK6Nb9caF4rSCcORdnV6nW00?via=gitter.im&via=matrix.org&via=one.ems.host re the missing use-cases
You're invited to talk on Matrix
?
def to_foodict(x: Mapping[str, Any]) -> SpecialDict:
if 'specialKey' not in x:
raise ValueError('x is not a SpecialDict')
return cast(SpecialDict, x)
this is what we have now in several places
i'd rather have
def is_foodict(x: Mapping[str, Any]) -> TypeGuard[SpecialDict]:
return 'specialKey' in x
oh I see
yeah I think you want something safer like this?
def is_foodict(x: Mapping[str, object]) -> SpecialDict | None:
try:
v = x["specialKey"]
except KeyError:
return None
if isinstance(v, SomeType):
return SpecialDict(specialKey=v)
return None
like a mapping might not be a dict
it's annoying you can't do a Mapping-based TypedDict
very much so 🙂
and that you can't match on a TypeDict
this is a good point... i think in our actual code i use Dict and not Mapping, but i'll check
also I avoid TypeDict in new code in favour of dataclasses (attr.s) and or pydantic
then parse it from the json.loads' Any at the "edges" of the codebase that does all the deep/recursive checks
then shallow isinstance will work for the rest of the code
indeed, refactoring to use attrs is on my todo list. need to flesh out the test suite first to make sure it doesn't break in the process, which is what i happen to be doing this week
do you use mypy or pyright?
mypy
if your loading these into classes it would be a really good idea to use pydantic as graingert suggested as i think it supports from/to_dict in a typed way
it does, and also a good point
in this case the application is mostly marshalling data back and forth between a CMS and MongoDB
so really i need two layers of transformations, python <-> mongo and python <-> cms
i want somewhat precise manual control over that process anyway, so the convenience of pydantic isn't worth much in that case
if i wrote the whole thing from scratch using fastapi i probably would use pydantic though
TIL you can't make a generic TypedDict 🥴
Can't do anything fun ;)
I want a Generic NamedTuple or the ability to add typed unpack to custom types
there is a proposal to add it
its in my github stars
but i havent seen anything happen with it in a while
def func(arg:int):
...
"""how can I see what `arg` is typehinted to?"""```
I think so, ty
inspect.get_annotations(obj, *, globals=None, locals=None, eval_str=False)```
Compute the annotations dict for an object.
`obj` may be a callable, class, or module. Passing in an object of any other type raises [`TypeError`](https://docs.python.org/3/library/exceptions.html#TypeError "TypeError").
Returns a dict. `get_annotations()` returns a new dict every time it’s called; calling it twice on the same object will return two different but equivalent dicts.
This function handles several details for you:
cause this will handle string-ized annotations
what would you use if you wanted to get the typehint from the actual typehinted variable itself>
like inside func
i could see what arg itself is typehinted to
nah cant use thar
im making a check to make sure arg is of the typehinted value
without manually entering typehinted valeu
func.__annotations__["arg"]
or just make a decorator?
That would not get you very far though. You can't really "verify" more complex types, like a function or a generator or even a list.
If we have a dictionary that stores class instances, would following be accurate in terms of type hinting:
class Info:
def __init__(self):
pass
if __name__ == "__main__":
data:dict[Info] = {}
yes
ok thank you (:
how would i get the current func tho?
you can't really, without extra hacks
i dont wanna manually write the func name, is there a builtin method or property that tells me the current func its in
oh ok
you can make a decorator
@dire bobcat
This should work for functions that only take keyword-only arguments.
from functools import wraps
from typing import TypeVar, Callable, cast
F = TypeVar("F", bound=Callablle)
def check_types(fn: F) -> F:
@wraps(fn)
def new_fn(**kwargs):
with_defaults = {**fn.__kwdefaults__, **kwargs}
for name, arg in with_defaults.items():
if name not in fn.__annotations__:
continue
ann = fn.__annotations__[name]
if not isinstance(arg, ann):
raise TypeError(f"Argument {name!r} ({arg!r}) is not of type ({ann!r})")
return fn(**with_defaults)
return cast(F, new_fn)
For others you'll have to do a bit more work (with inspect), but the idea is the same
(this will obviously only work for very simple types, won't even work for None)
just use beartype
https://bpa.st/WWSQ my validate decorator.... bugs?
this breaks if you have defaults i think
I believe I tried it with defaults and it worked. Must be annotated
I could be wrong lol
I'm using wraps from functools btw
oh yeah actually that probably fixes it
TypeError: isinstance() arg 2 must be a type or tuple of types
generics dont work
If I am appending different class instances to a list/array, how should it be type hinted.
Does below look about right?
from typing import List, Union
class Foo:
pass
class Bar:
pass
class FooBar:
pass
if __name__ == "__main__":
updates:List[Union[Foo, Bar, FooBar]] = []
it doesnt work with stringified types
Yeah I didn't account for generics. I haven't gotten into them yet..
thats fine yes
Yeah it's basic lol
are you planning on actually using this in production?
me? OF course not lol
I know it's full of holes. It was me just dabbling with decorators and seeing if I could validate some func params...
I generally think that this is a lost cause
well that's kind of insulting to inzane
Not saying it's not useful. It's pretty useful if it's in some library code that all kinds of people use
but:
- there's a runtime cost (you can use some switch flag, but there's a good chance that few people will remember to turn it on/off). It will actually change the asymptotic complexity (big O) of some algorithms. Would you check every element of a list in a binary search function?
- it will not work 100% even for simple things like
list[int]orSequence[str] - it will completely not work for more complex types (like functions or generators)
that is what I meant by this
this looks right. remember that in 3.9+ you can use list instead of typing.List
So what it can do is provide better error messages in some cases where you use a completely different type.
What I would do "in production" is:
- use a static type checker to catch most of the stuff
- if something does break, hopefully your tests will catch that
- take a look at https://pypi.org/project/deal/
Thanks for mentioning that. I will look into transitioning to list.
How do we format it if classnames are long and we have several of them.
Does the following work by creating an alias of Classes in this case:
# create an alias of classes
Classes = Union[FooPerformsFoo, BarPerformsBar, FooBarPerformsFooBar]
updates:list[Classes] = []
Is that what aliases are for?
yes
yep
or if you repeat the same type over and over
or if you just want a type-level named constant
like YesNo = Literal["yes", "no"]
And do we declare aliases globally?
Usually yes
ok
Support for aliases at class or function scope is inconsistent
How do you mean?
Some type checkers don't support it
Oh cool..... deal....
Don't remember the details, it was discussed on the mailing lists a few times
Do you think it's possible to write code that works equally well with all the major typecheckers?
or... practical
I'm using pyright/pylance for development, but of course nobody has pyright in their CI
yes only use generics :)
Usually yes, it tends to be just more obscure cases where you get incompatibility
we do it all the time in typeshed 🙂
Callable attribute is the worst so far
probably
oh yeah that's a really long-standing mypy bug
yeah, 6 years or so
Pyright has the opposite issue - try writing a method decorator
that does something non-trivial
oh ok I see
that would be solved with something like an intersection type: And[Callable[[T, Unpack[P]], int], Descriptor[Callable[[Unpack[P]], int]]]...
yikes
soon™️
How should I type hint something that can be a subclass, let's say of class A, but not A itself?
And I'm talking specifically about classes, not class instances
Aside from specifying a union of all the subclasses, it's not possible I think.