#type-hinting
1 messages Β· Page 18 of 1
almost certainly None
NoReturn is used in the rare case where a function is expected to never return
like sys.exit()
Or the only way it returns is via an exception
sys.exit() always raises an exception π
def main() -> NoReturn:
while True:
...
NoReturn is also the bottom type so it is valid to annotate variables as it which is weird
however, I would recommend using it only for cases where users expect that it never returns, not for e.g. abstract methods that raise NotImplementedError
Especially before Never was a thing
that's why we added Never
Never and NoReturn mean the exact same thing in the Python type system
Where is our Unknown type?
It's object ;)
Any?
Wdym?
Early drafts of pep 696 had Unknown
Unknown would be for when you don't know the type, but you don't want to be able to assign it to anything
sounds like object
I guess it makes sense that typescript needs an unknown. Not everything is an object
Is there a web-based sandbox for pyright? I'm almost positive someone linked to it a while ago, but every time I try to find it, I come up empty
just to type some code and see pyright's diagnostics, I don't need to run the code
https://pyright-playground.decorator-factory.su/ but I think the SSL cert needs fixing
probably, haven't tried it though
When you install the python plugin, it warns that some auto completion is limited
You also can't install python packages
Because no terminal
pyright is written in TypeScript
Just listing other limitations of vscode.dev
Is there a way to make a class variable which is an instance of the class while keeping mypy happy?
class X:
x: X
Thought that required from __future__ import annotations
It does:
>>> class X:
... x: X
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in X
NameError: name 'X' is not defined
Quote it.
or use python3.13
we haven't implemented PEP 649 yet π
that's more subtle than that, and has nothing to do with the any/unknown dichotomy;
unknown tells the type checker to be rigid. you basically say to it: "you do not have enough information to infer the type here"
any means pragmatically the opposite: "whatever you think it can be, consider it is"
definitly not related to who is an object and who isn't, it's types . and I see no reason why Python couldn't have one like this π€ (theoretically, I mean)
the typescript object is not related to being an object at runtime or not. according to the doc, it is the type for "everything that is not a primitive"; however, the situation is a bit confusing in javascript, as the notion of primitive is not the same as in langs like java
so i'm writing into a database with sql and one column has the date type, which is iirc YYYY-MM-DD.
what should the type hint be with pydantic? is it str?
ideally it should be parsed into datetime.date i guess?
How do I type a union based off of a boolean? I have a enabled field in my dictionary, if it is set to True all other fields will be included, otherwise that is the only one passed:
class Disabled(TypedDict):
enabled: False
class Enabled(TypedDict):
enabled: True
other_field: str
Alias = Enabled | Disabled
This of course fails at enabled: False, but is there anything I can do which would accomplish this?
Literal[False]?
Ah! Yes that seems correct, great!
btw, why pattern-matching on a boolean, if you already subtype ? why not just isinstance pattern-matching?
These are TypedDict's, so I am typing data I receive/produce from an API
hm, not sure I understand, but okay I guess you know
TypedDicts are just dicts at runtime
but also their subclasses?
I mean, still don't get why instance-pattern mathcing couldn't be applied π€ , instead of keeping a redundant boolean
"a TypedDict" means a subclass of TypedDict, yeah
yeah but you loose the subclassing?
to clarify my question: instead of:
def bla(d: Alias):
if bla['enabled']:
...
else:
...
do
def bla(d: Alias):
assert isinstance(d, Enabled) or isinstance(d, Disabled)
assert not (isinstance(d, Enabled) and isinstance(d, Disabled))
if isinstance(d, Enabled):
...
else:
...
but I guess the enabled comes from the API too? (I guess?)
!e
from typing import Literal, TypedDict
class MyDict1(TypedDict):
foo: Literal[True]
class MyDict2(TypedDict):
foo: Literal[False]
d1 = MyDict1(foo=True)
d2 = MyDict2(foo=False)
print(isinstance(d1, dict), isinstance(d1, MyDict1), isinstance(d1, MyDict2))
print(isinstance(d1, dict), isinstance(d2, MyDict1), isinstance(d2, MyDict2))
@dull lance :x: Your 3.11 eval job has completed with return code 1.
001 | Traceback (most recent call last):
002 | File "/home/main.py", line 11, in <module>
003 | print(isinstance(d1, dict), isinstance(d1, MyDict1), isinstance(d1, MyDict2))
004 | ^^^^^^^^^^^^^^^^^^^^^^^
005 | File "/usr/local/lib/python3.11/typing.py", line 2994, in __subclasscheck__
006 | raise TypeError('TypedDict does not support instance and class checks')
007 | TypeError: TypedDict does not support instance and class checks
Well, that answers your question
so the best thing you can do is a discriminated union
okay clear to me!
Is there a built in type hint that suggests Success/Failure instead of just using bool true/false?
Success: TypeAlias = Literal[True]
Failure: TypeAlias = Literal[False]
def f(a: int) -> Success | Failure: return bool(a)
x = f()
if x == Success:
...
else:
...
``` ?
I don't think that should exist
a well-named function makes it clear what True/False signifies on return
Success = True
Failure = False
yea? oh nvm, u'd need Literal
Eh. I understand that for somethings. But it also makes naming cleaner in languages like Rust or Go where it's clear Result etc.
applying patterns from one language to another, without taking the whole picture into account, might really well be one of the less fruitful exercise you could make ;D python and rust have very different exceptions philosophies, for example
I never said I'm trying to force it into python. It was a question so I could use it if it exists. I'm not trying to create it.
in that case, if it was me, I'd do a NewType('Success', Literal[True]) (if pytohn allows that), so that not every bool can accidently be mistaken with a success/error dichotomy
!e
code
!eval [python_version] <code, ...>
Can also use: e
Run Python code and get the results.
This command supports multiple lines of code, including formatted code blocks. Code can be re-evaluated by editing the original message within 10 seconds and clicking the reaction that subsequently appears.
The starting working directory /home, is a writeable temporary file system. Files created, excluding names with leading underscores, will be uploaded in the response.
If multiple codeblocks are in a message, all of them will be joined and evaluated, ignoring the text outside them.
By default, your code is run on Python 3.11. A python_version arg of 3.10 can also be specified.
We've done our best to make this sandboxed, but do let us know if you manage to find an issue with it!
!e 3.9 print("Hello There!")
consider using #bot-commands
Ugh, this is gross.
https://github.com/agronholm/anyio/blob/3.x/src/anyio/abc/_tasks.py#L89-L95
src/anyio/abc/_tasks.py lines 89 to 95
@abstractmethod
async def start(
self,
func: Callable[..., Awaitable[Any]],
*args: object,
name: object = None,
) -> Any:```
You can make an enum
!d enum.Enum
class enum.Enum```
*Enum* is the base class for all *enum* enumerations.
If you want a Rust enum aka discriminated union aka sum type, you can use a Union of dataclasses
class X:
def f(self, x: int) -> int: ...
class Y(X):
def f(self, x: int, y: int) -> int: ...
# Method "f" overrides class "X" in an incompatible manner
# Positional parameter count mismatch; base method has 2, but override has 3
``` why this is not correct?
i dont think it violates LSP
opensr.py:133: error: Signature of "f" incompatible with supertype "X" [override]
opensr.py:133: note: Superclass:
opensr.py:133: note: def f(self, x: int) -> int
opensr.py:133: note: Subclass:
opensr.py:133: note: def f(self, x: int, y: int) -> int
``` mypy is also not happy with it
it does violate LSP. Given an object x of type X, you can do x.f(1)
but if it's actually an instance of Y, that will break
I have a classmethod on a mixin that returns an instance of of the subclass class. I'm trying to figure out how to make mypy happy with the typings though.
def get_config(cls: type[_Config]) -> _Config:
But when I call it like this
config = Config.get_config()
the type I get back from that is not Config. reveal_type(config) is telling me
note: Revealed type is "_Config`-1"
Any suggestions?
works for me (pyright): ```py
from typing import TypeVar
T = TypeVar('T', bound='X')
class X:
@classmethod
def get(cls: type[T]) -> T: ...
class Y(X): ...
x = X.get()
reveal_type(x) # Type of "x" is "X"
y = Y.get()
reveal_type(y) # Type of "y" is "Y"
I think that's basically identical to what I'm doing but I'm using mypy
huh, something is different though, your code works for me too, going to dig more. really appreciate it π
maybe you didn't annotate cls?
oh interesting, I had added @lru_cache to my class method and it seems to have corrupted the typing
yeah, it is removing type information from function completely
seems like that decorator has some pain points, to say the least
you can do something like this: ```py
if TYPE_CHECKING:
def lru_cache(f: T) -> T: ...
else:
from functools import lru_cache
it looks like cachetools with the corresponding typeshed stub package doesn't have this issue
@oblique urchin I wanted to take a stab at making an experimental static analysis tools to explore some alternative typing things. Do you have any advice on building such a thing?
I think the biggest first hurdle will be stuff like resolving scopes, modules, etc; and doing control flow graph stuff. Is there some way I can avoid that maybe?
nice! Yes, you'll likely need to implement scoping rules. I suppose the only alternative is reusing the implementation of some existing type checker or linter. My pyanalyze has an ast_annotator mode (https://github.com/quora/pyanalyze/blob/master/pyanalyze/ast_annotator.py) that adds inferred types to an AST. I think pylint uses an underlying AST analysis module (astroid) that might do something similar, but I'm not familiar with it.
Hmm, astroid seems interesting
Honestly I'm not sure I have the energy to start such a project now. Maybe I'll stick to thought experiments for now
I wanted to take a stab at a sort of an alternative implementation of types in Python. LIke TypeScript for Python.
I kinda just want to fix the humongous complexity of the type system... There are a lot of rules, and you need to read a lot of PEPs to understand it.
Last idea I had was unifying TypeVarLikes into a single concept, kinda
Like they are in TypeScript
So in other words, try to make it more uniform
At least we have this. https://typing.readthedocs.io
yeah I suppose
that's published from python/typing/docs btw
I think there's a pip proposing a proper generics syntax to get rid of the TypeVar hack
PEP
Hah yeah my bad
Already added to 3.12
In your experiment, are you thinking of improving tuple types so they can type non-tuple sequences, like in TS?
That's just syntactic sugar though.
It doesn't unify them really
Yeah I get what you're saying. paramspec is limited to params, it's not unified with more general tuple type concepts like in TS
Do you mean like type Foo = (str, int)?
Yeah, e.g. in TypeScript there's no need for a separate TypeVarTuple, it's just a type variable holding a tuple type
TypeScript doesn't have keyword arguments, so it's a bit different
Still could work, the kwargs type would be a typed dictionary
there's hacks for it, but it isn't a builtin language feature
Well, ParamSpec has to account for the fact that some arguments can be passed positionally or by keyword
There's also the weirdness with typing rest params, where the type annotation is not a sequence type, so you don't get the option of typing it as either a sequence of T or a tuple type
You can do it with Unpack I think
can you do either * or ** on a TypedDict to substitute Unpack for kwargs in 3.12?
no, that syntactic change was rejected
you have to use Unpack
damn...
Did they ever decide on the dict[{"foo": str}] TypedDict shorthand?
I don't think it was ever in a formal pep
but pyright has experimental support for it
there's no PEP yet
Nikita is working on it
using a dictionary literal in a typehint could be useful for Callable
It's no different to me than tuple[x, y]
actually thinking about that lol
pass a tuple of types to the tuple class getitem to get a tuple type π
and everything to the left would be packed into a tuple?
actually, who says we can't use a slice? dict["x":str]
slice won't take non-int args I thought
!e ```py
class Foo:
def class_getitem(self, arg):
print(arg)
Foo['x': str]
@rare scarab :white_check_mark: Your 3.11 eval job has completed with return code 0.
slice('x', <class 'str'>, None)
hrm
it was rejected, and it didn't propose any specific uses for that feature
that's fair
slices accept anything:
!e ```py
class X(slice('hi', 123)): ...
print(X)
@tranquil turtle :white_check_mark: Your 3.11 eval job has completed with return code 0.
slice('X', (slice('hi', 123, None),), {'__module__': '__main__', '__qualname__': 'X'})
unalivejoy also showed yes
I don't like it
π
Is a slice a view onto part of a sequence?
can't believe I've never seen them before
ohhh I've only seen them via the [x:y] syntax
no, a slice is just a glorified tuple of (start, end, stop)
the [x:y] syntax creates a slice object internally, which is then passed to e.g. the implementation of list.__getitem__ which returns a new list
oh bummer. so a[x:y] is equivalent to a[slice(x, y)]?
@oblique urchin :white_check_mark: Your 3.11 eval job has completed with return code 0.
slice(1, 2, None)
(and slicing a list, note, copies that part, it's not any kind of view)
yeah i was hoping for a slick view onto an underlying sequence, like could do:
packed_rgb_colors = list()
red = slice(packed_rgb_colors, 0, len(packed_rgb_colors), 3)
green = slice(packed_rgb_colors, 1, len(packed_rgb_colors), 3)
blue = slice(packed_rgb_colors, 2, len(packed_rgb_colors), 3)
red[10] = 255
Slicing of numpy arrays works like that.
you can always make a custom class
I think I saw some memoryview tricks to do it with a list, but unsure.
native stuff's faster for tight loops. extra python fn calls and dictionary lookups add up
but yeah otherwise custom class works
Getting a weird issue with enum.Enum (https://github.com/t94j0/sddl_py/blob/master/sddl_parser/types.py#L6). I'm trying to make this function actually check with mypy. Rewrote it like this:
def rights_to_type(rights: int, access_map: IntEnum) -> Set[AllRightsT]:
rs = set()
for r in access_map:
if (rights & r) == r:
rs.add(access_map(r))
return rs
where AllRightsT is a sum type of all items in rights_enums.py. Mypy is claiming "IntEnum" has no attribute "__iter__" (not iterable) [attr-defined] while enum.IntEnum is a subclass of enum.EnumType which has __iter__ defined. Can't get the __members__ property either even though it's defined.
sddl_parser/types.py line 6
def rights_to_type(rights: int, access_map: IntEnum) -> List[int]:```
do you mean type[IntEnum]?
Oh maybe. I've never seen type[*] before. Where could I learn more about what that's doing?
Yep, this worked perfectly. Thanks so much
Ahh, that makes sense. That's exactly what I was looking for
Python now has Never as the bottom type... when will we get Unknown?
pyright thinks that Unknown is the same thing as Any
top type in python is object
Even in strict mode? Interesting
Not exactly the same. I believe it stores them differently.
mypy stores different types of Any differently
a Expression of type "None" cannot be assigned to parameter of type "(...) -> List[int]" Type "None" cannot be assigned to type "(...) -> List[int] I need to give default value of None to this callable while passing it in a function
can you show the code?
def func(
data: List[int],
func_: Callable[..., List[int]] =None,
) -> List[int]:
if func_ is None:
func_ = lambda x: x
...
a) func: Callable[..., List[int]] = lambda x: x
b) func: Callable[..., List[int]] | None = None
also, why do you accept ...? can it be called with any arguments whatsoever?
lambda x: x this can be any custom lambda but it will be fixed, thanks man, i will change ...
exactly, silly me
i kept myself thinking about (...) -> List[int] to None instead of revisiting my code
I am having problems using AbstractContextManager as a base class. Pylance always shows the warning that the class is missing generic type arguments. However, in contextlib, it isn't actually a generic class - the generics are only available through vscode's type stubs.
When I run pyright through the command line, no error is shown since it doesn't use vscode's type stubs
Is there any way to make the two consistent with each other?
it's actually generic at runtime (in 3.9+)
I see, thanks!
On another note, can I get vscode to only use the typeshed stubs instead of its own ones?
Because this sort of problem also occurs for external libraries such as numpy where vscode's stubs diverge from the ones on typeshed
I don't want devs to be surprised when they "fix" an error flagged in vscode, only for CI to fail because pyright runs on typeshed's stubs
I think Pylance uses typeshed as well. Just might be a different version
not sure why I'm getting this error on vscode
pyright is fine with it
and when I right-click on vscode to show the source, it can locate the file just fine
Is it okay to use _typeshed imports in normal code (not typeshed stubs)?
Yes
Make a conditional type alias. ```py
if TYPE_CHECKING:
MyASM = AbstractContextManager[Foo]
else:
MyASM = AbstractContextManager
class MyContext(MyASM): ...
Restart the pylance server.
class Foo:
def __init__(self):
self.arg: int | None = None
a: tuple[Foo, int, Foo] = (Foo(), 1, Foo())
if (var := a[0]).arg is None:
raise TypeError
reveal_type(var.arg)
``` This gives me
```py
information: Type of "var.arg" is "int | None"
``` It should be only `int`, right?
Yeah it should be
yep, what type chekcer are you using?
pyright
I think it's a bug from pyright's end? (I'm on the latest 1.1.316)
Does it have protocols for descriptors? Which ones?
Idk I don't think it has any
The source code is right here if you want to take a look at what we currently do and don't have π https://github.com/python/typeshed/tree/main/stdlib/_typeshed
Ik, but scrolling is an issue on github mobile π
eh nvm its only 10kb
I thought it would be much much bigger, since vscode always redirects to somewhere in typeshed stubs
Hello, is there a way to make a generic function in Python, such as: def generic_function[T](a: int) -> T
A repo or package containing just protocols for all the dunder methods would be nice, is there one already anyone knows of?
Hello! I posted my opinion on the chat but it was going so quickly. Before Python 3.12, from my experience I would say there are two options:
- By passing a "dummy" type:
T_ = TypeVar("T_")
def generic_function(generic_type: T_, ...) -> T_:
Problem of this approach is that you would need to create an instance of that type. So I dont like it at all
- Wrapping it into a class
class Generic_(Generic[T_])
@staticmethod
def func(args) -> T_:
...
So you would call it like
Generic_[int].func(args)
Maybe there are better ideas
Written from the phone, so there might be small typos
Not yet, but you can do
def generic_function(typ: type[T], a: int) -> T:
Right, that's much better and simpler π
typeshed is much bigger than this. that file is just a library of helpers used across typeshed
We're writing something like this but it's currently WIP and we haven't cut a release yet
(And when I see "we" I mean "mostly @hallow flint" π )
Is there a repo for this?
https://github.com/hauntsaninja/useful_types but as I say, WIP
type[T] < - this allows me to pass in a type instead of value ?
sorry if the question is silly, just making sure
type[T] is the object that you have used to construct instance T. For example:
MyClass() # This is type T
MyClass # Then this would be type[T]
yep
bool is an int subclass
yes, type[T] is covariant
π
Thank you soooo much. Thank you Thank you Thank you
I was getting desperate and hopeless
Sorry for not replying to you all this time. Like you said the messages were moving so fast and for some reason your message was not added to the unread messages counter
3.12 won't have function subscripting as of current tho
Sorry, what do you mean?
I wish they would add it because it would be nice to specialize generic functions like you can with classes
From PEP695:
Here is an example of a generic function today.
from typing import TypeVar
_T = TypeVar("_T")
def func(a: _T, b: _T) -> _T:
...
And the new syntax.
def func[T](a: T, b: T) -> T:
...
At least that's what I meant
def f[T](a: T) -> T: ...
f[int](1) # error
It is okay! π
The proposed solution made me wonder, because my IDE doesn't highlight the function argument as being used. This is what I have atm:
def get_resources(self, docs_type: type[T], url: str, **kwargs) -> PaginatedResources[T]:
but docs_type is greyed out by IDE (as in nothing is using it). Does Python take it into account somehow? Does it get passed into the PaginatedResources[T]?
Well yeah, if you don't use it in your function body it's unused
but does it really pass in the generic into the PaginatedResources[T] ?
it doesn't pass anything
π¦
that is handled by a 3rd party checker such as mypy
ah
they are hints to type checkers/readers of your code (including you)
In the end when you call that function, mypy will just look at your function signature and it will assume the return type based on the arguments you provided. But you are responsible for making that happen within the function body
I am such a scrub, I probably don't even have mypy installed
Are you using and IDE? They usually integrate type checkers
Yeah, Pycharm
I have never used PyCharm. But as far as I know, PyCharm uses its own LSP with type checker integrated. Not 100% sure tho
I have just gotten back to Python after having spent 1.5 years in Typescript
I see that Python allows most things that Typescript does, just trying to figure out where and how everything is
typescript is different in that it transpiles to JS
After checking, I have found already have mypy. But different from TS compiler, it seems very mild. I dont see any messages, nobody is screaming at me... Is this normal?
Usually the most used one is mypy as it is from python core developers. IDEs have the possibility to integrate this tool to display its feedback on the same line
You need to configure some things likely
How does it work in Python? I mean Python stays Python, so a tool like mypy basically does the surveillance of code during development?
Look at the PyCharm plugin store. I have seen that there is Mypy plugin that probably makes the job. Maybe you installed mypy using pip and the PyCharm IDE cannot linkt/find it
Mypy analyzes your source code to verify types
Does it do so in real time during the development?
only if you have a language server setup. Otherwise you can do something like run mypy every time you save your file
Yeah, I found it there, already enabled it shows
ah that might be the case
I was expecting some real time highlighting
that may be possible but idk the answer sorry
I am not sure if mypy is capable for real time. It is kinda slow as many other type checkers
This comes from Intellisense btw
For TS
so that is a language server
I didn't know that, thanks for telling me
Tbh I dont really know how it works under the hood in terms of TS. It just did the job automatically so I just rolled with it
Apart from having to setup the .tsconfig file
There is the TS compiler, then there is Intellisense. Intellisense runs as a language server for real time autocomplete and type feedback and docs etc
I see, thanks. You seem to be well versed in terms of types
Does Intellisense use the same TS compiler to accomplish this?
It sometimes got really slow, especially when I started working with React. Might be my computer though
Anyway, I found the article on how to configure mypy on Pycharm. Thanks for putting me on the right track @viscid spire @echo knot
π
Good luck with it!
thing with like VSCode is that it is pre-setup for TS
One thing about Python hasn't changed. It is really fun to write and to read
because both made by Microsoft
Yeah it's fun
typing add an extra layer to the fun π
in future you will want to look into collections.abc and typing modules for typing needs as well. They provide important functions and classes for more complex typing
And just in case you need to know. Mypy comes with some features/rules disabled by default, as they might be too annoying sometimes. But if you want to get better at it, it might be interesting to enable them
The most strict mypy settings are great to learn about typing, then relax them as you see fit
Yeah, I always try to configure the settings as strictly as possible
It is important to remember that typing is completely optional
So it is entirely up to you what you do and do not do with it
Ok glhf π
u2!
perhaps a decorator could achieve this for functions
although idk why you want multiline type ignoring
typically you don't want to ignore, but rather cast
theres talks about deprecating the typing.no_type_check decorator in the mailing lists
!d typing.no_type_check
@typing.no_type_check```
Decorator to indicate that annotations are not type hints.
This works as a class or function [decorator](https://docs.python.org/3/glossary.html#term-decorator). With a class, it applies recursively to all methods and classes defined in that class (but not to methods defined in its superclasses or subclasses). Type checkers will ignore all annotations in a function or class with this decorator.
`@no_type_check` mutates the decorated object in place.
it just seems like not a good idea to ignore stuff
Reading this doc, definitely looks like it should be deprecated
to clarify, no_type_check is not currently going anywhere. there is a separate symbol, no_type_check_decorator, that no one uses and no one supports, which is what weβre thinking of deprecating
haha fake news
Ok, Idk what it is but I am finding mypy to be extremely buggy. It reported 5 problems, I have fixed 1 which was unrelated to the other 4 and suddenly all other problems are no longer picked up, even though I know they are still there (such as incorrect return type)
Oh and now the problems reappeared
after 3 empty manual scans
If you can send a small snippet maybe I can try to see where is the issue
The issue is with the mypy, not the code itself
I have a function that returns a completely wrong type
it gets reported every 3-4 manual scans
then this issue. I have installed the required type, the error does not go away (well it does go away for 3-4 scans and then reappears)
The bad function simplified:
def get_resources(self, docs_type: type[T], url: str, **kwargs) -> PaginatedResources[T]:
docs = []
return docs
Did you do pip install types-requests? This error happens because not all packages provide the type information. To solve this, some developers decided to create separate type information for some packages
Yes, I did. the error stays
This is happening because "docs" is infered to be type "list" with. However, mypy expects that you return a specific type based on docs_type variable
Like, the return type does not depends on "docs_type". Therefore you will not be meeting the signature types
Is PaginatedResources a type or a TypeAlias?
class PaginatedResources(msgspec.Struct, Generic[T]):
"""A struct describing PaginatedResources"""
docs: list[T]
totalDocs: int
limit: int
page: int
totalPages: int
pagingCounter: int
hasPrevPage: bool
hasNextPage: bool
prevPage: None | int
nextPage: None | int
a dataclass?
Ok, but why does it provide self contradicting reporting? One scan it says it is fine, then during the next scan it reports it as a problem?
Believe not as there seems to be non-fields here
they just separated types out of the init likely, or something else is going on
It should not π
Mypy is pretty stable and deterministic
once every 4 scans..
What you need to do inside the function def is creating an object of type PaginatedResources where docs is a list containing types indicated by your input argument docs_type
Maybe my mistake is running it on windows..
Did you verify you have correctly targeted the version of python that you are using in pycharm?
Yes, I am returning the wrong type on purpose to test mypy
Because if you aren't targeting the same version you installed types to, it's not gonna work
Oh sorry π
Come to thing about it, my machine is a mess. I have several Python versions installed differently under different venvs
Uninstalling Python from Windows is a pain in the butt
Might want to do some tidying to get stuff in order
The installer also has an uninstaller
I know, but it leaves artifacts
what is the go to way to install and manage python versions nowadays?
pylauncher is an option at install
and not putting python or pip on path
at one point I used Anaconda, not sure if it is still popular/ go to
then all commands go through py
py accepts a version argument, and for pip you do py -m pip ...
If you do not have company based restrictions, I would pick 3.9/3.10 or later (stable releases only)
You can also enable pip to only install in venvs
that will add peace-of-mind in where your site packages are installed to and accessible from
Using 3.10
To be honest I have never had this issue. It was always stable. And I have worked with 2 different IDEs, from terminal and in 2 different OS, including Windows. So I do not think I can help you
I currently don't have time to address this till Monday evening to ship the first version
But i will try to reinstall everything and run it on Linux some time later
I have seen things either work or not work. But I probably have never seen things work selectively on some occasions
I think your problem is not the OS, but a messy environment probably
Neither do I. If you are struggling with deadlines, I would suggest to temporary forget about type hinting. Or use the one that is already integrated in PyCharm
Trying to make an abstract parent class that defines that there are two read-only classvars. The subclasses should implement them. In theory like this:
@dataclass
class Parent(ABC):
min: ClassVar[Final[int]]
max: ClassVar[Final[int]]
other_field: str
@dataclass
class Child1(Parent):
min: ClassVar[Final[int]] = 0
max: ClassVar[Final[int]] = 10
@dataclass
class Child2(Parent):
min: ClassVar[Final[int]] = 100
max: ClassVar[Final[int]] = 200
But apparently ClassVar and Final can't be combined.
Complicating things, this can be re-implemented as a property:
@dataclass
class Child3(Parent):
some_other_field: ClassVar[Final[int]] = 3
min: ClassVar[Final[int]] = 0
@property
def max(self) -> int:
return 1 << self.some_other_field
I could just remove the Final[]s, but then mypy wants me to implement a setter property for max as well... but max is supposed to be final
A bit lost on how to solve this, sorry for dumping here
you can simply omit the ClassVar
ClassVar doesn't mean that you can't access the attribute on an instance. It means that you can't redefine that attribute on an instance
I suppose the issues come in the ABC part
because typehinting something as Final needs to eventually have a value in the same namespace
Right, that's what mypy has an issue with now
$ mypy scratch_1.py
scratch_1.py:10: error: Final name must be initialized with a value [misc]
scratch_1.py:11: error: Final name must be initialized with a value [misc]
scratch_1.py:16: error: Cannot override final attribute "min" (previously declared in base class "Parent") [misc]
scratch_1.py:17: error: Cannot override final attribute "max" (previously declared in base class "Parent") [misc]
Found 4 errors in 1 file (checked 1 source file)
basically ClassVar and Final are conflicting types in specificity
becasue Final stops all redefinition, while ClassVar stops only redefinition on an instance
might want to use a property perhaps
wait sorry that's deprecated
@dataclass
class Parent(ABC):
other_field: str
@property
@abstractmethod
def min(self) -> int: ...
@property
@abstractmethod
def max(self) -> int: ...
this works
it's deprecated?
gotcha, thanks
I remember reading something else about some kind of decorator chaining becoming deprecated too
forget the details
now in your children, you can have these access a Final[int]
Perfect, that works!
@classmethod plus @property I believe
thought it was @abstractmethod
oh wait misread my bad
class Parent(ABC):
@property
@abstractmethod
def min(self) -> int: ...
class Child(Parent):
_min: Final[int] = 0
@property
def min(self) -> int:
return self._min
this should be what your code is gonna approximately look like @thick quiver
I have to use a metaclass as a type-friendly workaround, right?
This actually works fine: ```python
@dataclass
class Parent(ABC):
other_field: str
@property
@abstractmethod
def min(self) -> int: ...
@property
@abstractmethod
def max(self) -> int: ...
@dataclass
class Child1(Parent):
min: Final[int] = 0
max: Final[int] = 10
oh damn really?
cool
well, pyright doesn't like it
because it actually changes the type of min
What if you throw a @classmethod in there, for fun?
3 decorators on one function π₯΄
doesn't change anything
there is another issue tho...
from typing import Final
from dataclasses import dataclass
@dataclass
class Child:
min: Final[int] = 0
x = Child(1)
dataclasses don't respect the Final typehint
so min will be added in the init
wow
you need this
from dataclasses import dataclass, field
from typing import Final
@dataclass
class Child:
min: Final[int] = field(init=False)
but idk how to put a value
@dataclass
class Child1(Parent):
min: Final[int] = field(default=0, init=False)
max: Final[int] = field(default=10, init=False)
ah right, default
it wasn't showing up in my intellisense lol
I was looking at the args and simply wasn't there
You mainly have "default" and "default_factory" for those objects that are mutable
yeah ik I have used it before
slipped my tiny mind unfortunately
This looks like a bug, dataclasses should respect Final
it's just that specifying = some_value isn't really the super correct way for dataclasses
should be = field(...) if you need to do that
Yeah, but IMHO it should work the same as a ClassVar
π€·ββοΈ
would NoReturn be a valid typehint to give to a method that will never return, but can raise exceptions and can be stopped by Ctrl+C
f()
print("Hello!")
If the print will never run, then f() returns NoReturn
alr thanks
would this be valid for a coroutine?
Hey folks, might be a dumb question, but is there a way to add a generic to a specific method of a class? I have a worker pool whose role is to pass requests to a worder and I struggle to type it
Yes
official doc only shows how to make the whole class generic, not 1 method
Can you show the code?
class Foobar:
def send_stuff(request_name: str) -> T:
pass_request(request_name)
#and then
foobar.send_stuff[(number, number)]("request_that_would_return_some_tuples")
Here is some peudocode
coming from JS this how I think about it
in the official doc I only see the case where you pass T to the whole Foobar class
and instanciate "foobar" this way
That's the same as returning Any from the method
or examples of functions where the generic is derived from an arg (eg return a string if I call with a string)
Using a TypeVar only once generally makes no sense, because there's nowhere to "get" it from.
What the method is really doing is returning Any. You can then assert it to be a specific type later
any idea how to do so ? In TS I'd cast with "as" or "satisfies", didn't yet figure the equivalent in python
doing val : str = foobar.send_stuff() didn't seem to work
because send_stuff returns "Any" so the str type is ignored
Wdym is ignored?
What type checker are you using?
If it doesn't work you can use typing.cast
nvm it does work !
first time I run Python on this machine so I may have messed up something
thanks for the "cast" type
tip*
what a beauty ret: Tuple[List[Union[(bool, int, bool), Exception]],] = self.dsw_pool.pass_request_to_workers(
technically I should probably wrap that into another function to get the proper type
but each type of request is called roughly in one place only
like specific class -> calls works pool -> calls worker and type in "specific class"
Fwiw (bool, int, ...) isn't a valid type
Right changed for "Tuple"
somehow vscode cringed onthe first tuple but not the nested one
If you want to be more type-safe you can use this pattern:
import uuid
class TypedKey(Generic[T]):
def __init__(self, name: str) -> None:
self._key = f"{name}:{uuid.uuid4()}"
def key(self) -> str:
return self._key
my_key = TypedKey[int]("my_key")
Then you can do e.g.
def register_worker(pool: Pool, worker: Callable[[T], None], key: TypedKey[T]) -> None: ...
def submit_task(pool: Pool, key: TypedKey[T], value: T) -> None: ...
This way you can't get mismatched types
any idea to better define an enum with tuple values in a type friendly and enum impl friendly way?
@enum.unique
class ChannelID(EventEnum):
IsEnabled = (0, BoolEvent)
_VolByte = (2, U8Event)
Why is a tuple an enum?
Why will docs_type: type[T] result in: Mypy: Variable "docs_type" is not valid as a type [valid-type] when used?
I am trying to make it into a generic, so I am not sure if I can assign it to anything?
T = TypeVar("T")
class PaginatedResources(msgspec.Struct, Generic[T]):
"""A struct describing PaginatedResources"""
docs: list[T]
def get_paginated_resources(self, docs_type: type[T], url: str, path_params: Optional[list[str]] = None,
**kwargs: str) -> PaginatedResources[T]:
paginated_response = self.session.get(request_url).content
return msgspec.json.decode(paginated_response,type=PaginatedResources[docs_type])
I would just type-ignore in this case
I see. Could you tell me why mypy doesn't like this?
I think because it only lets you index generics with "type forms" (like type aliases or classes), not arbitrarily variables
Hmm. Well I'd like to pass a class, the problem is I don't know which (different api endpoint - different class)
I think what you're doing is fine, just ignore mypy π
ok, thanks!
I was curious about the issue you were getting but I do not have a laptop now. I copy pasted your code into "mypy playground" on the phone and after fixing some formatting and removing the msgspec thingy, mypy did not report any issue
Maybe I made a mistake on the phone, as it copy-pasted the code in a weird way
It could be because I am running --strict π€
I tried with this code (strict and no strict) and it was all fine. I am using (again, probably I broke something on the phone) https://mypy-play.net/?mypy=latest&python=3.10&flags=strict
from typing import TypeVar, Generic, Optional
T = TypeVar("T")
class PaginatedResources(Generic[T]):
"""A struct describing PaginatedResources"""
docs: list[T]
def get_paginated_resources(self, docs_type: type[T], url: str, path_params: Optional[list[str]] = None,
**kwargs: str) -> T:
return docs_type()
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
Oh my bad. I removed those lines thinking it was not important. I thought that the issue was in the method signature and not on those lines. I totally ignored them
Hey all ππ» , a niche question I can't find the answer to online.
If you are publishing a library that is 100% statically typed (py.typed marker included etc). But those types depend on 3rd party stub packages. Do those stub packages become primary dependencies of your library? Or do they stay as dev dependencies and downstream mypy etc can handle the missing stubs?
They stay as dev dependencies
Except if you use a type hint at a place other than an annotation, like as a base class
cool thanks! I'm guessing downstream mypy will go, hey you need stubs for this library and ask you to install the same stub packages as your own dev dependencies?
I don't think it will. You probably do need anything that gets used in a public API to be a full dependency
Or maybe you can add an extra like [types]?
yeah this is what I thought as well π€ I guess anything you import directly needs to be a main dependency, weird that I can't find discussion about this online
but I guess if the libraries just provide stubs then no point including
and they are dev dependencies
if a package contains of only a single file of code in the root directory (like bottle.py for e.g), where do I place py.typed?
does py.typed require a flat or src layout?
you cannot, py.typed only works for packages, not single-file distributions
doesn't matter what you do locally, it only matters that the file is there when the package is installed
alr
btw should tests be in their own directory even if its a single test_lib.py?
i'd rather prefer it in the root directory if its that simple
I have a base class that is subclassed by a few different subclasses, I want a function that takes the type of one of those subclasses and will return an instance of that subclass, how can I type hint that?
I think this is working:
T = TypeVar('T', bound=BaseClass)
def func(the_type: Type[T]) -> T: ...
Yes! It is like that
@haughty stag
from typing import overload
@overload
def process_input(data: str) -> str:
pass
@overload
def process_input(data: int) -> int:
pass
def process_input(data):
if isinstance(data, str):
# Realizar operaciones para cadenas
return "Β‘Procesando cadena: " + data + "!"
elif isinstance(data, int):
# Realizar operaciones para enteros
return data * 2
As you can see here, the method that is not decorated is the "broadest" one
(example generated with chatgpt. It mixes spanish with english hhaah)
AAAAAAAAAAAAAA
And then type checker will start looking at the signatures from top to bottom
:P
Therefore, it will go like: is data string? No? Is data a int? No? Then take the last method signature
Oh
I pinged the wrong one hahahahahahahahahaha
Lol
def stat_data_onlevel(self, level: int) -> Dict | str:
level_iterator = count(start = 20, step = 10)
level_list: List = list(next(level_iterator) for _ in range(7))
if level in level_list:
stat_dict: Dict = self.content["levelData"]
for data in stat_dict:
if data["maxLevel"] == level:
return data
else:
return QueryError.leveldata_outofrange()
This function, returns a dict when if condition is met and returns a str when it isn't .
mypy is saying i'm missing a return statement?
data_query\character_data.py:26: error: Missing return statement [return]
Found 1 error in 1 file (checked 1 source file)
not sure what I'm missing
It probably means that your function can return None in some cases
if stat_dict is empty, for example. Or if none if its items satisfy data["maxLevel"] == level
If you're sure that this will never happen, add an assert False after the loop
hmm thats true, this may be of concern as the data gets updated
stat_dict reads from multiple json files. Since these json files contains info of a playable character, stat_dict should typically never be empty, as all characters must have levelData to be playable.
I'm gonna add a few lines to check for that as well
Haver somebody ever used "PEP696: Type defaults for TypeVarLikes"? (https://peps.python.org/pep-0696/)
I am using it in my project and it is not working with mypy.
I am just copy pasting the examples into mypy playground from the phone. Same example provided is giving a different output (see image). Type is supposed to be Bot. Since this feature was added one year ago, I am searching in mypy repo if it is supported, but I did not find anything
Python Enhancement Proposals (PEPs)
The code in case somebody needs to copy and paste:
class Bot: ...
BotT = TypeVar("BotT", bound=Bot, default=Bot)
class Context(Generic[BotT]):
bot: BotT
class MyBot(Bot): ...
reveal_type(Context().bot) # type is Bot # notice this is not Any which is what it would be currently
reveal_type(Context[MyBot]().bot) # type is MyBot
it's not implemented by mypy yet
(though some experimental support has been added recently)
Ugh. I am really interested in this feature. I guess I will have to wait more
Do you have a thread or something I can follow? I cannot find it in mypy repo π€
Great! Thank you so much. Such a coincidence to see you not only in Pyanalyze repo but also here π
I'm having trouble getting this TypeAlias (in the example A.T) to work properly:
from __future__ import annotations
class A:
T = tuple[A | B, ...] # This TypeAlias
class B:
def __init__(self):
a = A()
https://mypy-play.net/ doesn't complain about anything, though Pylance complains about A and B not being defined in the TypeAlias. If I run it python also raises an exception saying A is not defined. If I do tuple['A' | 'B'], Pylance no longer complains, but when I run it I get an exception about no operator | between str types. What am I doing wrong, or how can I make this work?
Does T = tuple["A | B", ...] work?
from __future__ import annotation has no effect because it only applies to annotations
This seems to work π€ I'll give it a try in my full case and see, thank you!
Yeah makes sense thanks
Is there an immutable dict I can type something as? I cannot use Mapping because I use dictionary methods, but if I use dict I cannot passed TypedDict's to it.
what dictionary methods do you use? Mapping should have all of the methods you need other than those which actually mutate the dict
!e ```py
from collections.abc import Mapping
print(set(dir(dict)).difference(set(dir(Mapping))))
@brazen jolt :white_check_mark: Your 3.11 eval job has completed with return code 0.
{'__ior__', '__delitem__', '__ror__', '__or__', '__setitem__', 'copy', 'pop', 'popitem', 'setdefault', 'update', 'clear', 'fromkeys'}
__or__ π
wonder why that isn't on mapping actually
it's newer than the Mapping ABCs, and adding methods to an ABC breaks compatibility
oh, that seems annoying, so any additions to dicts/lists won't carry over to the collections ABCs? Why does it break compatibility though? Does it need to be abstract? Couldn't you just write a default implementation there, so anyone inheriting from Mapping will get __or__ for free?
you don't have to inherit from an ABC to implement it
oh right, hm, that's annoying
Guess what __or__ does with defaultdict π₯΄
I mean, whatever it is, it's not amazing
!e
from collections import defaultdict
a = defaultdict(int)
b = defaultdict(bool)
print(f"{a | b =}")
print(f"{a | dict() =}")
print(f"{dict() | b =}")
@trim tangle :white_check_mark: Your 3.11 eval job has completed with return code 0.
001 | a | b =defaultdict(<class 'int'>, {})
002 | a | dict() =defaultdict(<class 'int'>, {})
003 | dict() | b =defaultdict(<class 'bool'>, {})
yeah, it picks the function from the first dict
ugh
oh no
?
well, you don't have many options with defaultdict here
dict.setdefault usually does the trick
yeah, I'm not saying I don't understand why it's like this
just that it's kinda weird
and perhaps unexpected
yep
I only thought about it literally right now. never occurred to me
chaotic evil
def defaultdict.__or__(self, other):
if random() > 0.5:
return defaultdict(self.fn, {**self, **other})
else:
return defaultdict(other.fn, {**self, **other})
(not swapping the order of unpacking to be consistent with dict.__or__)
yall should be thankful I'm not participating in the design of the stdlib
And here we come to another pitfall of inheritance. When you subclass, even if right now you don't violate any invariants of your parent class, you don't know if the parent class will in the future add something you won't be able to reconcile with
let's just move to impls, inheritence sucks anyway
Hmm.... Does this mean that adding a public method to a class could be a breaking change? 
Actually if I had my own defaultdict, adding __or__ to dict would be a breaking change
isn't Iterable[str] and str basically the same thing?
do type checkers have some special casing for functions accepting just str to also accept an Iterable[str]?
Str is iterable, yes
no, because not every Iterable[str] is a str
most functions that take strs don't just iterate over them
Special casing for str and bytes
Yeah, no special casing
str satisfies Iterable[str] already
in fact it's really annoying
(because most of the time passing a str where an Iterable[str] is expected is a bug)
but I don't see a good way out
guess we need a char type for python 4 π
A character type.
(blazing fast)
C char must be faster than Rust char
(blazingly faster, touches the register (sometimes))
char would be odd in python bc of how you can use unicode in strings
well... in C, char means something completely different π
ik
In Rust, char means a unicode character
C char is just disguised u8
ah
a unicode character
well...
The char type represents a single character. More specifically, since βcharacterβ isnβt a well-defined concept in Unicode, char is a βUnicode scalar valueβ.
@trim tangle fyi pyright playground has an expired ssl cert (you might already know?)
yeah I know I am just really lazy π₯΄
(not always... portability moment)
Does somebody here have used numbers.Real commonly? How are those numbers supposed to work from the point of view of hierarchy/type hinting? I have never used them
Like, doing:
number: numbers.Real = 2
Is wrong according to Mypy, although it seems Python built-in integer implements the same methods stated by the abstract class numbers.Real. I thought the difference between numbers.Real and int would be the same as with Mapping and Dict for example. But there seems to be something else
My understanding is that numbers is an old and not very useful module. E.g. Fluent Python ed.2 says about it:
As I review this in July 2021, the numbers package is not supported by PEP 484 or the Mypy type checker. Since 2017 there is an open issue in the Mypy project titled βint is not a Number?β. This is not a Mypy bug; it reflects a shortcoming of the numbers package, which I explain below.
[...]
Sadly, the numeric tower was not designed for static type checking. The root ABCβnumbers.Numberβhas no methods, so if you declare x:
Number then type checkers will not let you do arithmetic or call any methods on x.
[...]
In conclusion, although numeric types should not be hard to type check, the current situation is this: the type hints PEP-484 eschews the numeric tower and implicitly recommends that type checkers hard code the subtype
relationships between built-in complex, float and int. Mypy does that, and also pragmatically accepts that int and float are consistent-
with SupportsComplex, even though they donβt implement__complex__.
This is great! It solves all my doubts. Thanks! I was already reading fluent python but did not reach that chapter yet π
Do you know a tool or package that works with typing.Annotated to provide runtime checks? Or any other interesting feature that makes use of them (either runtime or not). I think the potential of this type hint is great. I searched this months ago and I could only find some web development related tools
Beartype uses that to do custom runtime type checks
Pydantic allows using typing.Annotated to add constraints, validators, etc (instead of putting them in parameters of Field):
https://docs.pydantic.dev/latest/usage/validators/#annotated-validators
Msgspec too: https://jcristharif.com/msgspec/constraints.html
I will take a look at these! Thanks both π
what would be the correct type hint for accepting an iterable of chars but not an iterable of strings?
If by "char" you mean a 1-character string, that's not possible
you can accept an iterable of the union of all possible one-char strings π
>>> print(f"Character = Literal{list(string.printable)!r}")
Character = Literal['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~', ' ', '\t', '\n', '\r', '\x0b', '\x0c']
I almost typed all that out manually
This barely scratches the surface
π
want me to include the entire short range?
here you go. ```sh
python -c 'print(f"from typing import Literal\n\nCharacter = Literal{[chr(x) for x in range(0xffff)]!r}")' > char.py
Pyright will die on this file
Black seems to have parsed it ||eventually||
Now make typealias for nonegative integers
here you go. ```sh
python -c 'print(f"from typing import Literal\n\nWholeNumber = Literal{[*range(1, 0x0fffffff)]!r}")' > whole.py
Can black parse that?
Idk, I didn't run it
Is it even a valid syntax? Isnt there some parser stack overflow?
There's no functions. Why would it stack overflow?
If anything, it would MemoryError
!e ```py
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
while True:
print('hi')
@tranquil turtle :x: Your 3.11 eval job has completed with return code 1.
001 | File "/home/main.py", line 21
002 | while True:
003 | ^^^^^^^^^^^
004 | SyntaxError: too many statically nested blocks
20 indents - ok, 21 - syntax error
!e this works ```py
x=[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[0 for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()] for _ in ()]
print(x)
@tranquil turtle :white_check_mark: Your 3.11 eval job has completed with return code 0.
[]
f"from typing import Literal\n\nWholeNumber = Literal{[*range(1, 0x0fffffff)]!r}" - this string takes 2.8GB
parser allocated 30GB of memory, froze my pc, and i killed the process
why not smth different, since it's not possible (see above)
_Hidden = NewType('_Hidden', str)
Char = NewType('Char', _Hidden)
def as_char(x: str) -> Char:
assert len(x) == 1
return Char(_Hidden(x))
def as_chars(x: Iterable[str]) -> Iterable[Char]:
return (as_char(_) for _ in x)
# example
def f(x: list[Char]): ...
f(as_chars(["a", "5"]))
with assertions turned on while testing, I guess you'd see "incorrect" usages enough?
on another note, it feels wrong to accept any kind of chars, disregarding control ones and normalisation. maybe you actually dont want any kind of chars, but a very small subset
I think you can remove _Hidden there
it's to "disallow" direct subtyping from outside the module, without going through as_char (or just to make it quite explicit it shouldnt be called directly)
That makes sense
if one day they optimise an idemtity call over regular method ones, one could also do
def or_identity(f):
def id(x): return x
b = False
assert (b := True)
return f if b else id
@or_identity
def as_char(x: str) -> Char:
...```
b = False
assert (b := True)
return f if b else id
``` this is extremely weird
`return f if __debug__ else id` - this is a lot cleaner and a bit faster (compiler (or peepholer) can optimize `if __debug__` checks)
yeah I didnt remember the dunder π
This is really fun to scroll through on mobile
because an argument accepting a literal, doesn't really need a hardcoded literal, a variable can work as well
So its Literal only in terms of its declaration, not real usage
OneOf would be more confusing IMO
Yea I think Union could have been reused too
Or the | syntax
Would Union['X'] mean a forward reference to class X or the literal string 'X'?
Or, well, just 'X'
For | syntax you'd have to add __or__ to strings, integers, None, Enum values and so on.
Yea I completely forgot forward refs lul
TypeScript has much lighter-weight syntax, as you proposed
but that's TypeScript π
TypeScript was invented because the base language is shit af, probably unusable
Python's lucky to have type hinting syntax inbuilt
But besides that
Literal wasn't a very good name choice
What would you name it?
Choice?
That's still a synonym for Union
You can cry about union all day
π
But its not supposed to be used anyways now
I mean a better syntax is usable in most cases 3.7+
I think we would be better off without that tbh
Type hints having runtime semantics is a bit of a nightmare
I don't trust complex code without type hints
For one depending on the variable name entirely is hard
Not everyone is good in naming
What I mean is, instead we should've gotten something like TypeScript.
And code comments telling what code does is so C
mypy existed before annotation syntax
Yeah
It used comments!
# mypy pre type hinting
def main(): pass
```?
Annotations are really just comments in a different syntax
that was based af
pre-annotations
Initially annotations were added to account for many different use cases. But then it kinda died and it's more or less only used for static types
!pep 3107
So now we're in this awkward situation with runtime semantics, forward referencing issues etc.
so in hindsight^
Python stays a simpler language. And the type system can evolve independently of it
Lambdas are neutered anyway.
![]()
![]()
Still are
Will always be perhaps
And why doesn't vscode use type hints from stub files placed along side code?
Like isn't that the most basic thing
good question
i have no idea
Ffs look at C# tooling (π₯΅)
TypeScript tooling as well π
Both MS
Even pyright ms
MS has a legit type hint fetish
or it wants everything to look more like C#
I think Anders Hejlsberg was behind both TS and C#
wait what,
he was the original author of Turbo Pascal and the chief architect of Delphi.
is there a language this person hasn't created
In most cases you can slap from __future__ import annotations and dont worry about runtime behaviour of annotations
I have this import in literally every my file
- several years after pep649 stringized annotations will work as before
- if you remove
from futureimport, pep649 behaviour will work - after several years
from future import annotationswill be deprecated and will have no effect. New behaviour is almost equivalent to old behaviour, so nothing will be broken
I think nothing will be broken (except for some code that relyes on the fact that annotations are strings)
I dont like how that import looks like. I think because of that I am using the string baded type hints. Which might be even worse π₯Ή
I have a blind spot on this import line, so i dont care about how it looks π
This and the if TYPE_CHECKING line to avoid circular import issues
I never had circular import issues when i use from future import annotations
just do import x; a: x.X instead of from x import X; a: X
But if you have a.py and b.py, and you import a from b.py, and b from a.py (only for thpe hinting purposes), the from __future__ import annotations still does not fix the circular import issue, no?
Or yes? π€
My common sense tells me that unless future annotations are able to somehow detect that this import is only done by type hinting purposes, the circular import issue will still be happening
But I never tried. I directly go with if TYPE_CHECKING + string-based type hint with that specific type
# a.py:
print(f'a, before import')
import b
print(f'a, after import', b)
# b.py:
print(f'b, before import')
import a
print(f'b, after import', a)
``` and then i do `import a` in this directory
there is one problem: you can import a from b, but it will have no fields (it is not fully initialized)
so you can do import a but you cant do from a import X (because a has no X at this moment)
I am so confused now
Oh, I see. I did not note this difference
it is even more confusing if i do python a.py:
it import a twice π
because first a is actually a __main__ module
I tend to do the "from X import X", which typically triggers circular import issues if you have one. So I thought they would always happen regardless of how you based your imports
Is there some typing trickery to allow not getting a type error on the assignment line here?
class MyList(list):
def my(self) -> None:
pass
a: MyList = [1, 2, 3]
well that code is clearly incorrectly typed, so why do you want it?
the glib answer is # type: ignore but I don't understand why you would want to write that line
maybe you want a NewType instead, or use a = MyList([1, 2, 3])
Why is a good question π
We basically used PEP 3107 hints for other purpose (not PEP 484) and now I'm trying to reconcile things in a large codebase lol
So the a: MyList part comes from the requirement to be that in runtime
While the = [1, 2, 3] part comes from a desired to have better ergononomic. We do support a = MyList([1, 2, 3]) but it's very verobse to have around.
The longer I'm contemtplating what I can do with all this the more I'm leaning towards just
if typing.TYPE_CHECKING:
MyList = typing.Any
So the
a: MyListpart comes from the requirement to be that in runtime
What do you mean by that? Since according to
a: MyList = [1, 2, 3]
you already assigned something that is clearly not MyList to the a variable at runtime
seems like there's a good chance
if TYPE_CHECKING:
MyList = list
will work better for you than MyList = Any
Have you tried making some kind of type alias like MyList = Annotated[list[...], extra_data]? That doesn't interfere with type safety
Is there a way to specify that an abstract static method returns an instance of the non-abstract subclass?
-> Self does not work
and -> AbstractBaseClass does not work since that would be too general a type
what's wrong with -> Self? though maybe you need an abstract classmethod instead
I was thinking so too yeah
there isn't really a difference between an abstract classmethod and an abstract staticmethod anyway
yeah try a classmethod
ok that works, thanks!
I don't know if this has enough data to debug but:
def compare(self, target: Mapping[Variable, VAR_VALUE | set[VAR_VALUE]]):
for var in target:
reveal_type(target[var])
if isinstance(target[var], set):
raise TypeError()
reveal_type(target[var])
``` I have this code and both of the `reveal_type()`s prints the same:
```py
filepath:107:25 - information: Type of "target[var]" is "int | Fraction | list[Group | Operator | ParenthesizedGroup | Fraction] | set[int | Fraction | list[Group | Operator | ParenthesizedGroup | Fraction]]"
filepath:111:25 - information: Type of "target[var]" is "int | Fraction | list[Group | Operator | ParenthesizedGroup | Fraction] | set[int | Fraction | list[Group | Operator | ParenthesizedGroup | Fraction]]"
``` Why doesn't pyright narrow it (remove the set)?
I don't think it narrows x[y] where y is a variable in general
rather, where x is a variable
oh
no?
x[<literal>] would get narrowed
ah
e.g. if isinstance(target["x"], set): reveal_type(target["x"]) would output set
I would extract target[var] into a variable
like foo = target[var]
or just accept Mapping[Variable, VAR_VALUE] π
actually, in this case, do for var, foo in target.items()
(make it the caller's headache)
hm, why does it work tho?
it broke like 99% of my other code ._.
that's terrifying π
Almost, I also want it to know about .my()
Should I bother with using GenericAlias manually
SelfListType = cast(type[list[Self]], types.GenericAlias(list, cls))
Or should I just do it normally via type: ignore? ```py
SelfListType = list[cls] # type: ignore[mypy]
technically it is not sound to narrow here because your Mapping can do arbitrary things when you look up the key
Yes, I was thinking about it, but it would require some changes to the framework which I'm not sure are streighforward.
I'm looking at it, and I don't like what I did.
that's not the reason though, since type checkers are happy to narrow for literal keys where the same thing applies
it's just the cross-variable dependency
oh
If you have one class thatβs generic over something bound by another class, can you express that the bounding class is itself a subclass of the bounded class with whatever subclass of the bounding class it is as the generic parameter?
I ask because I want to represent linear algebra, where every linear space is related to some field, but every field is a linear space with itself as the field.
I would like to write
Scalar = TypeVar("Scalar", bound="Field")
class Space(ABC, Generic[Scalar]):
...
class Field(Space[Self])
...
but mypy complains
Maybe you want something like a TrivialSpace(Space[Scalar])?
I'm not sure such trickery with Self works
I think from the point of view of a real case it should not be possible. A superclass that depends on some of its subclass it is a problem. Because what would happen if you create another subclass from Space? This one would be linked to Field even if they should be completely independent. Maybe I am just wrong. But I have never seen something like that
Because in the end subclass should be aware about what its superclass implements. But not the other way around: a superclass that depends on its subclass
a subclass MySuperField would be a Space[MySuperField]
which, yes, would break LSP because Scalar is invariant
(π€)
Ah, I understand the case better now
But why would you care about which types are assumed for Space? If it is abstract and it is never going to be instantiated, you only need to care about which types are inferred for the subclasses like Field, no? This is confusing me even more π
Suppose you have something like this ```py
class Eq(ABC, Generic[T]):
@abstractmethod
def equals(self, other: T) -> bool:
...
If you have a type of `Eq[int]`, you can compare it to an int.
But if you have:
```py
class Foo(Eq[Self]):
...
if you have an instance of Foo, you can compare it to itself.
So, if the TypeVar is always going to take the value T = CurrentClass, why you just dont do:
class Eq(ABC):
@abstractmethod
def equals(self, other: Self) -> bool:
...
In the end Self is an alias for a TypeVar that always takes the value of the current instance type
This way any class you create from Eq, would have other being same type as its superclass
Because this is not the only way to use Eq
You can also be Eq[SomethingDifferent]
In OP's case you could have class IntVec3(Space[int])
I see π€
I've cloned this pypi library but it doesn't have any static typing and not using it basically gives me whiplash at this point. I'm not sure whether to add a directory for type stubs or just rewrite them inline however - the library seems to be 2 years dead, otherwise I'd go for type stubs
But if I'm likely going to be moving it into a fork I suppose it makes more sense to be writing it inline
Actually that's a module and not a space because integers are a ring and not a field
wdym
oh right
yeah I'm bad with maths, sorry
int doesn't have an inverse
I think I will do this (but with a class factory function)
I also think algebraic structures fit really well with Protocol types
it would be cool if you could somehow tell the type system that two classes will never overlap
that way, an isinstance check for one could rule out the other
That's possible if they have incompatible metaclasses
IIRC
oh cool
gah
I know why you canβt but it bothers me
I wish you could do issubclass with a TypeVar
Is there a pattern for dataclass typing to declare an abstract class attribute for a base class, and for the linter to not complain when children implement it? At runtime this works, but pylance complains:
class Base(ABC):
@property
@abstractmethod
def arg(self) -> Foo:
...
@dataclass
class Child(Base):
# pylance/pyright reports this as incompatibleMethodOverride
arg: ClassVar[Foo] = Foo.ENUMERATION_1
You are overriding a method as an attribute? Thats incorrect
Yes I know it's weird, but literally every SO thread I can find has "this is the pattern for declaring an abstract attribute"
It is not weird. Typecheckers should understand that attr satisfy requirements of @property, but they are not smart enough
pyright intentionally complains about this, the reasoning is that a property has more specific semantics than an attribute
If protocol has property (read only) and class that implements this protocol has attr (read and write), how that violates lsp?
if you do Base.arg you get a property object, if you do Child.arg you don't
(note that I disagree with this argument, I think it's too puristic, but it does make some sense)
discussed in https://github.com/microsoft/pyright/issues/2943 (though Eric makes an exception for Protocols)
If Child has slots then you get slot descriptor object, if it has no slot for this arg you get AttrError
In any case (slot, no slot or property) mypy will think that Child.attr is an int object (if you annotated it as int), not property or slot descriptor
Oh, it is fixed actually
hey @trim tangle: is there an ETA on when we should expect significant additions to Typing Tips? ParamSpec, maybe? π₯Ί
Is the PR button not working? π You should contact GitHub
||/lh||
I guess there's the typing docs, the typing tips are kinda redundant
fix, i'm not asking so i can add additions, i'm asking so i can finally use paramspec without a zillion errors and reverting to not using it
Yeah I understand π
There's something in here for example:
https://typing.readthedocs.io/en/latest/source/generics.html?highlight=ParamSpec#declaring-decorators
I personally never used ParamSpec and I have no idea about all the caveats. Every time I tried using it I kinda failed as well.
I am currently in the middle of a scorching burnout so don't expect any kind of magnum opus any time soon
If you're in immediate danger of type unsafety, my advice would be: replace ParamSpec with TypeVarTuple
The biggest problem with ParamSpec for me is the awkwardness of adding/removing parameters, especially on methods. It kinda looks like it was designed just for the cases of:
- Passing a function, args, and kwargs (like
Thread(...)) - Making a decorator with no/minimal transformation of arguments
I think 1. is silly anyways, just pass a lambda/partial
Though again, I haven't used it. So maybe I'm missing something
ive confronted paramspec not working by sending prs into mypy π
my use case being a function that you use to design your own function signature
... obviously this use case has been supplanted by typevartuple
Just curious . Do any of you type hint numpy-array shape? If do, how do you do it? The only possible way I found is by using external wrappers (nptyping) that provide a way of indicating shape for each array
The generic in this function appears to break in pylance...
def find_annotated(field: FieldInfo, annotation_typ: type[T]) -> T | None:
...
# Argument of type "type[InlineValue]" cannot be assigned to parameter "annotation_typ" of type "type[T@find_annotated]" in function "find_annotated"
# "type[InlineValue]" is incompatible with "type[bool]"
# Type "type[InlineValue]" cannot be assigned to type "type[bool]"
if not (foo() or find_annotated(f, InlineValue)): ...
works fine when I inverse the if. ```py
if not foo() and not find_annotated(f, InlineValue):
bug report time
it's possibly because foo() returns a bool
Ok, so the workaround is to be specific and check is not None
In pydantic v2, is there any way to add a field validator in __pydantic_init_subclass__?
Is there a way of annotating a generic function to being not None? I have a FastAPI application where I found myself writing
result = db.query(...) #Β some DB query
if result is None:
raise HTTPException(404)
quite frequently. I wanted to write an expect() function to try and shorten this construct. I came up with the following
def expect(
value: T | None,
error_msg: str | None = None,
status_code: int = 404,
) -> T:
"""
Unwrap a value or raise an HTTPException if the value is None.
"""
if value is None:
raise HTTPException(
status_code=status_code,
detail=error_msg,
)
return value
but this isn't really perfect, is it possible to have a TypeVar that is not None, or is there something I've missed that makes this possible?
Try using a TypeGuard
it's a function that returns a bool. If it's true the input is the typeguard's arg
e.g. ```py
def is_not_none(value: T | None) -> TypeGuard[T]:
return value is not None
if is_not_none(xxx):
xxx.yyy
though typeguard doesn't work with exceptions
The issue with this is that MyPy doesn't recognize the type since the Generic could be None itself right?
I'm working on this in https://github.com/hauntsaninja/useful_types/pull/7 but every variation I tried so far fails on some cases for either pyright or mypy
it was my understanding that you can't really have T | None since T could be None itself
I don't think TypeGuard really helps here, it doesn't add anything over just checking is not None
This looks really interesting, thanks. I haven't actually tried my example with Pyright, but I know MyPy complains about it
Custom Annotated like this probably won't be possible, right? ```py
Ts = TypeVarTuple("Ts")
Inline = Annotated[T, InlineValue(), Unpack[Ts]]
Just an annotated that acts as its own Annotated so you could do Inline[str, SomethingElse()]
Out of curiosity, do you know if this is a bug in one of the type checkers, or is this behaviour not really specified? I was just looking through the typing documentation/ PEPs and I couldn't really see any mention of overloads with generics
the latter, overload behavior isn't well specified
Is TypedDict special cased?
It looks like a normal class but instead of __getattr__ all the typing enhancements get applied to __getitem__. If I subclass TypedDict and also use the dataclass decorator, would that mean I get easydict like behavior for free?
is it possible to create a class that acts like a Union[str, bool, int, float] type hint, but allows me to add custom methods to the class? or would i need to subclass Union to do this?
alternatively, can i create a class inheriting from Generic[T] where T is bound to str, bool, int, float, and then have literal values be compatible with the class type hint?
e.g.
T = TypeVar("T", str, bool, int, float)
class Foo(Generic[T]):
pass
def bar() -> Foo[bool]:
return True # type checker failure
wouldnt that be unsafe? if i had an x: MyType and called x.my_method(), it would fail if x was a bool literal
import typing as t
import dataclasses
V = t.TypeVar('V')
class easydict(dict[str, V]):
def __new__(cls, *_, **__) -> t.Self:
self = super().__new__(cls, *_, **__)
self.__dict__ = self
return self
@dataclasses.dataclass
class X(easydict[int | str]):
a: int
b: str
x = X(42, 'foo')
reveal_type(x.a) # int
reveal_type(x.b) # str
reveal_type(x['a']) # int | str
reveal_type(x['b']) # int | str
``` is this good enough for you?
i dont think you can subclass TypeDict, because there are no TypeDict instances actually at runtime
i see the flaw in my first idea, but what about the second one?
doesnt it have the same problem 
second is also unsafe
because True is not an instance of foo
if literals were to be compatible with this typehint then it couldnt have custom methods
you cannot do Foo.bar on bool values
This reminds me of C++ template fuckery where you can "enable" a method if T satisfies some condition
makes sense. in practice, the value passed from the users function goes straight into json serialization. i need the type hint for metadata
Just about ok
basically what im trying to achieve is this:
the user writes a function, for example
@my_deco
def foo() -> RawResponse:
return True
then, my_deco knows that the user is intending to return a RawResponse and can record some metadata like some_object.response_type = "it returns a raw response"
alternatively, if the user does not intend to return a raw response, it would go something like this
@my_deco
def foo() -> AnotherResponse:
return AnotherResponse(
some_field="xyz",
some_other_field=42,
)
then my decorator would record as metadata that this function returns an AnotherResponse, and can then pass that metadata to an api
hm and whats the type hint needed for
telling the logic behind the decorator what the function intends to return, so it can map that return type to an id that the api understands
and also for type checking in general, so the user doesnt accidently return an object that isnt recognized by the api, like a list
!d typing.Annotated
typing.Annotated```
Special typing form to add context-specific metadata to an annotation.
Add metadata `x` to a given type `T` by using the annotation `Annotated[T, x]`. Metadata added using `Annotated` can be used by static analysis tools or at runtime. At runtime, the metadata is stored in a `__metadata__` attribute.
If a library or tool encounters an annotation `Annotated[T, x]` and has no special logic for the metadata, it should ignore the metadata and simply treat the annotation as `T`. As such, `Annotated` can be useful for code that wants to use annotations for purposes outside Pythonβs static typing system.
it was added in 3.9, im working with 3.8 π₯²
pass AnotherResponse to decorator (or make another decorator which will set metadata on function) and annotate function as -> bool
@my_deco(rettype=AnotherResponse)
def foo() -> bool:
return True
i think for now i'll go with making RawResponse an alias for Union[str, bool, int, float]
is there a way to make a parameterized union, so that the user could choose which of the Union's parameters they intend to return?
like
MyType = Union[str, bool]
def foo() -> MyType[bool]:
...
i think this is something typevars could be used for, but not sure how exactly
T = TypeVar("T", str, bool)
def foo() -> T[bool]: # error
...
i guess you are overriding this property with incompatible type
can you help me with this?
i = 1
while i < x:
print('candy')
i += 1
it gives me this :
wrong channel: #βο½how-to-get-help
no one answered
!pypi typing-extensions
def db_connect(choice: str) -> None:
if choice == "lc":
return sql.connect(key["LIGHTCONE"])
elif choice == "fsearch":
return sql.connect(key["FSEARCH_DB_LOCATION"])
else:
return None
how do I typehint the return of the sql.connect()?
what does it return
it returns a <sqlite3.Connection object at 0x000001A24D18DD50>
yes
its from sqlite3, im using that
-> sql.connection | None
I assume you did something like import sqlite3 as sql?
ah yes
also, the way im checking my type hints is through mypy, I did this and it complains that sql.connection is not defined
i mean i get it, but im not sure what to do
oooh
ok so for the sake of future type hint adventures, the way to know what typehint a return is just go print(type(func)) or var or whatever and just use that
sqlite3 is also in typeshed, so you can probably also use reveal_type(sql.connect) to get the return type of a function
also also also
oh there's a package for external typehints?
it's included with pyright and mypy, but you shouldn't import them. It takes care of it for you.
aah ok thats good to know
i already have mypy on
stdlib/sqlite3/dbapi2.pyi lines 205 to 214
def connect(
database: StrOrBytesPath,
timeout: float = ...,
detect_types: int = ...,
isolation_level: str | None = ...,
check_same_thread: bool = ...,
factory: type[Connection] | None = ...,
cached_statements: int = ...,
uri: bool = ...,
) -> Connection: ...```
def fsearch(entry, choices) -> str | None:
cutoff: float = 0.55
candidates = {}
for i in choices:
score: float = ratio(entry, i)
if score > cutoff:
candidates[i] = score
try:
return max(candidates, key=candidates.get)
except ValueError:
return None
mypy is complaining:
error: Argument "key" to "max" has incompatible type overloaded function; expected "Callable[[Any], SupportsDunderLT[Any] | SupportsDunderGT[Any]]"
so do i typehint the kwargs as well?
I think the problem is candidates.get may return None
(it can't in this case, but mypy doesn't know that)
now that you've mentioned it
return max(candidates, key=candidates.get(candidates.keys()))
its weird but now it shows the None complaint
error: Argument "key" to "max" has incompatible type "float | None"; expected "None"
that code doesn't really make sense, candidates.keys() can't be a good argument to candidates.get and you probably need a lambda
yeah, i was just wondering
i'll try the lambda
Wouldn't candidates.__getitem__ also be an option?
or even max(candidates.items(), key=itemgetter(1))[0]
yep lambda works and mypy stops complaining
return max(candidates, key=lambda x: x)
ill try these
that's an identity function, which is the default
oh wow yeah it works
TIL
You probably wanted lambda x: candidates[x]
that works and the x: x also works
doesn't both return the values all the same?
are you wanting to get the max key lexographically?
sorry what does lexographically mean
the max here is comparing the value of the keys, which is a levenshtein ratio for fuzzy logic (float)
and what i want to be returned is the keys
so the x: x make sense to me since it'll get the key
but x: candidate[x] also works
doesn't that mean it'll return the value (float) instead? since its indexing candidate["something"]
only one of these should work as you want. if you observe that they both work, you may need more test cases
It means by characters
also with the code you showed above, lambda x: candidates[x] should fail for some inputs (where the ratio is below the cutoff)
What's your opinion from other type checkers? Considering speed, how quick they implement new features, bugs... I typically use Mypy. I used pyright in the past but it had some bugs. What about pytype for example?
that portion of the test fails as intended, but for the case of x: x and x: candidates[x] both return the keys, is that the intended result of both lambdas, or should one of them return the value instead of the key?
aah got it
found out the problem, well it's not a problem with the code. i jsut forgot how lambdas work
so basically since the cutoff is quite high relatively to the choices, it discards most results and in the end sometimes there's only one key:value pair in the dictionary, when this is the case, max() will of course return only one thing regardless of the key=
and the correct argument is indeed either __getitem__ or x: candidates[x].
evaded a potential disaster there
remind me from what Python version does from __future__ import annotations work?
3.7
thanks bro
mypy used to annoy me like hell once upon a time i.e. pre 1.0, but its pyright that does so now. mypy has really improved
however pyright lsp is kinda instant as opposed to mypyd (or whatever vscode uses)
VSCode uses Pylance which is based on Pyright
mypyd doesn't provide general LSP features like go to reference
Because mypy is a type checker. Pylance uses pyright for type support
why no fix error btw?
am smol dukci
pyright also provides an LSP with some stuff
and e.g. Rename
microsoft built an separate mypy ext recently
Same with black and flake8
I've found pyright to be a bit ahead of mypy. E.g.: https://dogweather.dev/2022/10/03/i-discovered-that-python-now-can-do-true-match-exhaustiveness-checking/
I think mypy also supports that π€
I could only find a lot of articles showing faked-up exhaustion checking with mypy. Maybe it's been added since I wrote that blog
def func(a: int | str | datetime) -> None:
match a:
case int():
return
case str():
return
reveal_type(a)
$ mypy t.py
t.py:11: note: Revealed type is "datetime.datetime"
Success: no issues found in 1 source file
If you remove the reveal_type, do you get an error? That's the feature, IMO
Ah, probably not? As it shouldn't be an error?