#type-hinting
1 messages ยท Page 63 of 1
https://github.com/microsoft/pyright/issues/2916#issuecomment-1025200662 i dont understand how having msg.body here being Any and what cases get broken by this if you change the inference?
sry @soft matrix I think I made that merge conflict for you in Self with my ClassVar change >.<
luckily it was trivial
its fine
Is main blocked for a alpha release? I'm not familiar with dev cycle
its blocked cause buildbots are failing iirc
o
theres something in #mailing-lists about it
Ah I found it it's in python-dev
Thanks
Oh nice there's a PR clarifying GenericAlias and some other things
typing.py was a bit confusing in a few spots
i just spent like 12 hours trying to setup type hints + vscode + mypy, and its still not working totally. this is exhausting.
i feel like the juice was not worth the squeeze at all. i could've worked on features instead ๐ญ
If you have the Pylance extension installed in VSCode, you need to go to "Settings" -> search for "type checking" -> switch from "off" to "basic"
Pylance (which uses pyright) is a bit different from mypy, but IMO it is better polished, and is more intuitive. It is also used to provide types when you hover over something.
If for some reason you need mypy specifically, we can move to #editors-ides ๐
don't need mypy specifically, it just seemed like the most recommended after some quick googling. will take a break and try out pylance, thx
mypy is what most people use, because it came first (somewhere during Python 2). It also supports plugins for things that aren't representable in type hints.
whoa black changes failed my CI
we changed a lot this release haha ๐
that's why you should pin it ๐
I prefer the failing to just annoy me into fixing
if it's due to spaces around ** you can blame @leaden oak by the way
https://github.com/GBeauregard/pyffstream/commit/00f3226faa45d7dca5543aa7235a0d7789184426 this was the only thing that affected me
๐
I primarily use black so I don't have to have an opinion on formatting
I don't know if I should be proud or afraid ๐ค
is it recommended for __slots__ to be annotated (as Tuple[str, ...])?
also, py class A: x: int should x: int be in an if TYPE_CHECKING: block?
I wouldn't bother, because you wont get much from it, __slots__ only defines the names to reserve, not the types
no
it shouldn't be necessary (and may hurt some type checkers if they'd otherwise use the exact type)
alright, thanks
would that still be the same if there are a lot of annotations in the class meant for type checking? (performance wise)
Maybe if you profile it and see a significant amount of time spent building the __annotations__ dict. It can only affect import time anyway. It's not something I'd worry about.
I don't think I need worry about it then, thanks again
class A:
__slots__ = {
'a': int,
'b': str,
'c': dict[int, list[float]],
}
I dont think that works
!e
class A:
__slots__ = {
'a': int,
'b': str,
'c': dict[int, list[float]],
}
print(A.__annotations__)
print(A.a.__annotations__)
@hearty shell :x: Your eval job has completed with return code 1.
001 | {}
002 | Traceback (most recent call last):
003 | File "<string>", line 9, in <module>
004 | AttributeError: 'member_descriptor' object has no attribute '__annotations__'
from heapq import nlargest
from typing import *
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
count = Counter(nums)
return nlargest(k, count.keys(), key=count.get)
# main.py:1600: error: Argument "key" to "nlargest" has incompatible type
# overloaded function; expected "Optional[Callable[[int], SupportsLessThan]]"
# return nlargest(k, count.keys(), key=count.get)
# ^
count.get takes an int and returns an int, wouldn't that support the less than operator, what's wrong?
there's actually a convention that the values are docstrings
It is complaining because of the possible default value that could be passed I think
count.get may return None, that's the problem
righttt, key=count.__getitem__ makes more sense
Hey! How can I do something like this
def func(item) -> item.value:
...
What is item?
an object which has the an instance attribute value
Yes, it's an instance of Item
And what type is the value of the item?
That depends on what the argument, Item was created with, for example Item(value=0)
here, value attribute will be int
are you familiar with typing.Generic and typing.TypeVar ?
only TypeVar
okay so you know how a TypeVar scope is specific to a function?
a Generic allows you to extend that scope to a hole class ```python
from typing import Generic, TypeVar
T = TypeVar("T")
class Item(Generic[T]):
def init(self, value: T) -> None:
self.value = value
def func(item: Item[T]) -> T:
return item.value```
np ๐
I don't understand what this message is saying:
Incompatible types in assignment (expression has type "Dict[str, Dict[Union[float, int], str]]", target has type "Dict[Union[float, int], str]") [assignment]mypy
I have this from something such as :
_dict[x] = computed_dict
where _dict[x] = Dict[str, Dict[Union[float, int], str]]
and computed_dict is similar to {'something' : {2 : 'a', 4 : 'b'}}.
It doesn't make sense to me as the _dict has a union for the key to the subdict, so why does it care?
because you are saying the keys should be Union[float, int] in other words either float or int, and 'something' is not a float or int
But something is the str key of the first dict - the subdict contains float/int keys
Dict[str, Dict[Union[float, int], str]] -> {'something' : { 1 : 'a' }}
but _dict[x] = ... means you are trying to overwrite the subdict
_dict = computed_dict
``` should work
oh sorry - what i'm doing is:
_dict['something'] = {2 : 'a'}
got those mixed up, but that's what's happening - and i have that error
in that case, hmmm
i tried casting and moving on with my life but that didn't work either
this works when I test it ```py
from typing import *
_dict: Dict[str, Dict[Union[float, int], str]] = {}
_dict['something'] = {2 : 'a'}``` are you doing anything differently?
going to try and create a mwe - just tried in playground and it didnt' seem to work out ๐ค
ok i can get it to work by casting to Dict[Union[float, int], str]
might just live with that as i don't really understand why it's happening lol
@boreal ingot actually i guess this represents it (https://mypy-play.net/?mypy=latest&python=3.10&gist=0d054b6a0c5b7108585c2cd35ae7f4bb):
from typing import Dict, Union
TypeAlias = Dict[str, Dict[Union[int, float] , str]]
x : TypeAlias = {}
y : Dict[int, str] = {2 : 'this'}
x['something'] = y
So here I'm getting the previous error, but I've explicitly stated x to be more permissive type-wise than y is, and am assigning y to x, so intuitively (for me) i'd have thought the type x would take precedence here
The mypy Playground is a web service that receives a Python program with type hints, runs mypy inside a sandbox, then returns the output.
!e oh that makes sense, and it has to do with the fact that dicts are mutable, take this example ```py
from typing import Dict, Union
TypeAlias = Dict[str, Dict[Union[int, float] , str]]
x : TypeAlias = {}
y : Dict[int, str] = {2 : 'this'}
x['something'] = y
all fine so far, but if we do
x["something"][2.3] = 4
print("x:", x) # still fine
BUT look at this
print("y:", y) # now y is no longer Dict[int, str], OH NO!```
@boreal ingot :white_check_mark: Your eval job has completed with return code 0.
001 | x: {'something': {2: 'this', 2.3: 4}}
002 | y: {2: 'this', 2.3: 4}
I really like generic aliases
thanks - so i'm not sure what's typical - i've cast( ) to avoid this now, but not sure if there's a more typical approach
I would typehint y to be allowed to contain both float and int
ah ok, even if it's always int
just to make it conform to x, makes sense
yes, because in theory something working with x could change y to contain a float
any reason that's preferable to cast( )?
right - makes sense ๐
I just think y: something = {} is nicer than a cast
cool - just wasn't sure if it was more idiomatic or more personal, all good tho
How can I share my codes in color like this?
!code
Here's how to format Python code on Discord:
```py
print('Hello world!')
```
These are backticks, not quotes. Check this out if you can't find the backtick key.
thanks dude
btw your example would work if you were working with Mapping instead of Dict
since they're read only
so if the original code, you want to be able to pass something as an argument to a function and it's not working when you expect, annotating the function with Mapping instead of Dict might help
don't think so, Mapping is still invariant in the key type
ah yeah, if it's the key type then no luck
my mistake
you want to be really careful with float as a key to a Dict though, anyhow
hello, anyone can clarify what's up with typing.Generator being "deprecated" since 3.9? Not sure i understand what should be used as a return type of my yielding functions instead of Generator, as mypy still reports error when i try to replace it with List[yieldType]
it just means you should use collections.abc.Generator instead
nothing else changes
ooh, right, i was fixated on typing module and didnt realize other parts of "std" can be typed ๐
also consider using Iterator if that's all you care about
yes, simplifies it a bit, i just didnt understand deprecation notice, thus collections.abc.Iterator is my thing...
thanks Jell & Numerlor
Hi all, often you have some m: Mapping[KT, VT] and you need to call m.get(obj[, default]) with some obj that may not be of type KT. This is the whole reason you're using m.get(obj) and not m[obj] in the first place -- the very next thing you're going to do is to check if you got back None (or whatever default value you passed). But currently, mypy gives a type error for this: error: No overload variant of "get" of "Mapping" matches argument type "...". It looks like this is because Mapping.get(..) currently only accepts __key: KT .) What is the right way to handle this? It seems like typeshed's type annotations here are thwarting this very common idiom. The same goes for MutableMapping.pop(obj, default) too.
stdlib/typing.pyi line 456
def get(self, __key: _KT, __default: _VT_co | _T) -> _VT_co | _T: ...```
Hello guys, I've been once again distracted by python and instead of actually working on my project i decided to implement a useless rust feature for more than 3 hours but now I have two major issues which keep me a way from using/running my code how it is expected to be.
So, Result should have the Ok and Err params type annotated to be Ok/Err while also allowing type annotation so when Ok gets created with the value of a string then on doing ok.value it gets shown as a string but when I pass it into the Result with res = Result(Ok(True, "my value"), Err(False, None)) i should see that res.Ok.value is a string but i dont.
My code:
@dataclass()
class Ok(Generic[_TOK]):
status: bool = field(default=False) # on true this got returned
value: _TOK = field(default=None)
@dataclass()
class Err(Generic[_TERR]):
status: bool = field(default=False) # on true this got returned
value: _TERR = field(default=None)
_T = TypeVar("_T")
_TOK = TypeVar("_TOK", Ok, covariant=True)
_TERR = TypeVar("_TERR", bound="Err")
@dataclass()
class Result(Generic[_T, _TOK, _TERR]):
Ok: _TOK = field(default=Ok(False, None))
Err: _TERR = field(default=Err(False, None))
...
I know that this is a very advanced python problem but please give it a try. Thank you!
Hello, I hope you are well, can you help me with this, can you do me a favor, please
in this
This is why we must choose to make the best possible code. Therefore:
Research good programming practices in Python.
Create a summary document, where these good practices are consolidated.
Create a software program that can analyze the Python code of other programs and identify if best practices are being followed.
A summary should be identified and presented at the end indicating which good practices were identified and which were not.
It should give a suggestion on how to apply the good practice.
I already have the document of good practice and bad practice, but I don't know how to do it so that I can identify the practice
I already know how to make him read it line by line
!pep 8
Some 3rd-party compilers (and linters) can already identify that
(Has this question been answered before? I'd expect so, but couldn't find the answer anywhere I looked. Wondering if I wasn't searching correctly and where this already is (or should be) documented authoritatively.)
https://github.com/python/typeshed/issues/6597 is where we discussed it
I got a new regression from the latest release on a real project: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/logic/arguments/function_args.py#L...
okay, i fix it on my way.
Thanks @oblique urchin, hadn't seen that! Before I comment there, just out of curiosity, do you have any thoughts on what I said above (which covers more than special casing None)? (@Srittau doesn't seem to be in favor of making any changes, and could only possibly be convinced of adding a special case for None, which I don't think is enough.)
I tend to agree with Sebastian. Making sure the key type matches helps catch type errors.
In an ideal world we'd have some check that the key type overlaps, so you could use a partially overlapping type, but still get an error if you do d.get(1) on a dict with str keys
@oblique urchin, I think that there are a lot of legitimate uses of d.get(obj) where you have no idea what type obj is, and the code is still correctly handling the case where obj is missing. Do you think that all uses of d.get(obj) should have to use some kind of look-before-you-leap type narrowing to avoid a mypy error, even though it's common and often more idiomatic to check the result after the call? (I'd agree with this for d[obj] , but not d.get(obj) which should explicitly support the case where you don't know obj's type, and same goes for d.pop(obj, None) )
I already have it but I don't know how to implement it so that it identifies everything
it's not common in code I have seen ๐ But yes, if that's common, that's a good reason to change our mind
Interesting, I've seen this all the time (and can give examples if you like), but good to know you're open to reconsidering! (Put another way, I agree it can help catch type errors, but it can also flag perfectly idiomatic code that is already handling all cases correctly, which becomes less clear when rewritten to appease mypy. Would definitely be a victory for typing in Python when it can avoid causing cases like this.)
How come?
Is this just BC hashing floats in general is problematic?
They're floats atm as that's what was returned by the file format, there's no reason for then to be floats really tho
because comparing floats for equality is tricky in general
Ok fair, in this case they're never more than one decimal place
floats don't have decimal places. they're binary
Ok, Idk how to express then - my point is I'm not representing transcendental numbers or whatever, they're just 1.0, 2.0 and so on
You can run into precision issues with something as simple as 0.1+0.2
pretty much
I forget the exact examples but you can have thinks like, hash at 1.0, and then try to retrieve at 0.7 + 0.3 and it fails
that particular one actually works ```In [111]: 0.7 + 0.3 == 1.0
Out[111]: True
In [112]: 0.1 + 0.2 == 0.3
Out[112]: False
but yeah, it's not reliable in general
Are there any common workarounds for hashing floats? Rounding to some decimal places comes to mind but that still has sharp edges like 0.00049999 and 0.0005
simplest workaround which is obviously a pain in python is to use a BST instead
and then if you wanted you could use approximate equality, or even access all keys within epsilon of your access, check if there's only one, etc
but it really depends what you're doing
what would the use case be? I'm not sure I've ever felt the need for a set or dict containing floats
Dont have any specific use case in mind heh
you could imagine a situation where you wanted to form a "set" of measurements, to count how many of something you thought there was. Suppose you get readings from multiple sources, and they are floats, and two "identical" readings mean that it's the same object and shouldn't be double counted
but in a situation like that, you'd obviously want to have some kind of tolerance
so a more practical thing to do is to sort it and then do some kind of group_by operation
or if this group was being modified continually, a BST
but either way you need an appraoch that is robust to using tolerances, which sorting and BST's are, but hash structures are not
Oh yeah, ive definitely done that before, rounded to two places and collections.Counter'd a list of floats
The problem is that rounding runs into the edge cases discussed above
But it that's good enough then sure
You could just multiply it by 10**X to get X decimals and then turn the float into an integer.
almost, only a couple more failing buildbots?
So part of the discussion on ClassVar/Final in dataclasses was basically that the PEP should be amended to allow both of these in one annotation in dataclasses specifically. It's easy to implement this at runtime by adding the allow_special_forms flag to ClassVar and Final, but at the cost of allowing this everywhere at runtime and also allowing stuff like ClassVar[ClassVar[int]] (at runtime only)
If I have the following type alias
T = TypeVar("T")
Missing = Union[_MISSING, T]
Why does the type of a variable annotated with Missing[Type[Enum]] end up being _MISSING | Enum?
is that in pyright?
I think that's a bug. I noticed pyright has some occasional odd behavior around type[] in type aliases
Ack
The really odd thing is that hovering over the variable has VSCode show the right type, then when I use reveal_type or pyright shows type checking errors it's _MISSING | Enum
Cannot assign member "choices" for type "Option"
Expression of type "Type[Enum]" cannot be assigned to member "choices" of class "Option"
Type "Type[Enum]" cannot be assigned to type "Missing[Type[Enum]]"
"Type[EnumMeta]" is incompatible with "Type[_MISSING]"
"Type[EnumMeta]" is incompatible with "Type[Enum]"PylancereportGeneralTypeIssues
if TYPE_CHECKING:
self.choices = cast(Type[Enum], self.choices)
Can't even cast it 
Yeah it is a bug with pyright, the same works on mypy
Does the same issue happen if you just use the EnumMeta directly?
JINX
Could not bind method "__iter__" because "EnumMeta" is not assignable to parameter "self"
Type "EnumMeta" cannot be assigned to type "Type[_T@__iter__]"PylancereportGeneralTypeIssues
It complains about a different error
Humm
from typing import TypeVar, Union, NewType, Type
from enum import EnumMeta
_MISSING = NewType("_MISSING", object)
T = TypeVar("T")
Missing = Union[_MISSING, T]
def factory() -> EnumMeta: ...
a: Missing[EnumMeta] = factory()
reveal_type(a)
here it does reveal as being EnumMeta
_MISSING is an Enum is part of the problem
how is that different from round(n, X)? it'll still have the same edge cases
That's what PEP 484 recommends for singletons after I spent two hours trying my own metaclasses and things
so is _MISSING an Enum or enum instance
_MISSING is the enum, MISSING is an enum instance
T = TypeVar("T")
class _MISSING(Enum):
MISSING = "MISSING"
def __bool__(self) -> Literal[False]:
return False
def __eq__(self, other: Any) -> Literal[False]:
return False
def __repr__(self) -> str:
return "MISSING"
MISSING = _MISSING.MISSING
Missing = Union[_MISSING, T]
you made it unequal to itself? that's going to be fun

It's purpose is for when a value is missing it's MISSING
The __eq__ is just there, I'm debating whether to keep it or not since the goal is for it to be used in an is check
I never used one but dont people use sentinels in those instances?
That is exactly what I'm trying to do, just in a type safe manner
MISSING: Any causes me way to many headaches when I forget to check if the value is MISSING
The sentinels PEP isn't around yet, so I'm trying my best to follow the guidelines for type checking singletons
Humm, so with the Missing why would you need to pass it Type
Like what is the usage
The variable is meant to be an Enum class, not an instance of Enum
It's for defining a set of choices, so I need Enum.__iter__ functionality
Ohhh, I got what you are trying to do now
Yay!
The issue is stupid pyright is turning my type into an instance
And my if self.choices is not MISSING: check is saying yeet to the type checking as well
Typehinting self.choices as Missing[EnumMeta] makes it pass the check correctly, then pyright proceeds to complain that it's incompatible with _T@__iter__, which is just wrong
An if TYPE_CHECKING: block to cast self.choices to Type[Enum] after my MISSING check makes it work again though
Why are you using the class as the singleton though?
and not the instances
the instances are singletons
I tried to use a metaclass and a class and instances and all kinds of stuff to get it to work, it just won't deconstruct the Union correctly is the problem
The way PEP 484 makes it for deconstructing a Union with singletons is with an Enum, and it works exactly the way I want it to except in this one case where the other part of the Union is also an Enum
Either way, I need to run to bed now
What am I asking is that from the example you showed, it seems you are doing
class _MISSING(Enum):
MISSING = "MISSING"
class SOMETHING(Enum):
CHOICW = ...
Yes, _MISSING is a primarily internal value for when something is just not present (missing) and SOMETHING is a user-provided Enum
(I'll be back tomorrow, thank you for the help so far!)
aight, and I see now, I was wondering why would couldnt put both enums together
Yes, you still have the initial rounding issues but wouldn't that help later on when it's store in the dictionary?
how so 
if you always remember to round before hashing then storing floats and ints should be the same
PEP 563, basically just makes so all type hints are treated as strings. Will become default behaviour in 3.11
It's really disappointing to me that this hack is going to be in Python
What different does it make to the interpreter?
Were they not already stored as strings in the __annotations__ dict?
They were not nvm
Originally (circa 2008) annotations were created with the purpose of being inspected. And this is what libraries such as pydantic are leveraging
There are non-hacky solutions to the problem of circular references, such as https://www.python.org/dev/peps/pep-0649/
Humm are there workarounds though? I had heard that the reason for the delay from 3.10 to 3.11 was pydantic
There is a workaround with typing.get_type_hints which runs eval on these annotations, giving the context of the class they are defined in. And it does seem to work in most cases.
I see, just finished reading 649, so basically better or equal in almost every instance except when your code relies on circular imports
Given that the other pep is already accepted, why is this was as a draft? They wouldn't change behaviour on 3.11 just to then go back on some next version right?
The runtime consequences were brought up after the fact, and after some discussion the Steering Council basically deferred making it default behaviour for 3.10 and then 3.11, so there's time to try and figure out a solution that works for everyone.
If I have a load of TypedDict classes (e.g. AccountJson), how can I typehint that a tuple is of the NamedTuple equivalent without having to manually redo all the classes as a NamedTuple?
I found a way to automatically generate the NamedTuple variants, but since it uses globals().__setitem__ they're not recognised when I try to import them
can you show an example?
from typing import NamedTuple, TypedDict
class AccountJson(TypedDict):
id: int
name: str
email: str
phone_no: str
permissions: int
class AccountTuple(NamedTuple):
id: int
name: str
email: str
phone_no: str
permissions: int
```like this, but I want to automatically generate the second one since it's the same.
The script I came up with is:
```py
[
globals().__setitem__(
tuple_class_name := class_name.replace('Json', 'Tuple'),
NamedTuple(tuple_class_name, list(class_obj.__annotations__.items()))
)
for class_name, class_obj in globals().copy().items()
if class_name.endswith('Json')
]
```but as I said this causes issues with importing
Why do you need both?
or rather, why do you want to autogenerate them?
If you autogenerate them, you definitely won't get any type hinting support
I use both. Fetches to my database will return the NamedTuple version, and then I convert it to the TypedDict version in certain areas for easier use
I suppose I can just convert straight away and then I don't need the NamedTuple versions though
Why not just use the NamedTuple version?
I don't see how it's less convenient than TypedDict
Hmm
I just realised my reason is mute
I was using TypedDict since I can access by name instead of index
But NamedTuple literally does that too
I guess there's still the fact that the API has to return a dict though, which is where I'd use TypedDict I guess?
So you want to couple the public API to the database structure?
It already is
I think
If I'm understanding correctly
The API returns stuff from the database, so return values match the structure of the database
do you know what "coupling" is?
Nope
The database structure is an implementation detail of how you store the data. You might want to change it if you want to change the database system you're using, or change the database structure (e.g. denormalize some data), or split the data across two different databases, or introduce a cache, or do something as trivial as rename a column.
But the public API is not supposed to change with the database structure, or even to match it. The API should make sense to the user of your service
The data returned by the API is the same structure as the database
Why does the API exist then?
Because I need to allow others to access the database
Can you explain what the project is supposed to do?
The overall project purpose?
yeah
It's a sports center
So p.e. I have an endpoint in my API for viewing all of your bookings
And the format it returns is the same structure as the database, just as a json (dict) instead of a tuple
It's basically```py
def get_bookings(account_id: int) -> BookingJson:
data = database.execute("SELECT * FROM bookings WHERE account_id=%s", (account_id,))
return {
"id": data[0],
"facility_id": data[1],
...
}
!e
If you really want to return the dict representing a tuple,
import collections
Foo = collections.namedtuple("Foo", "a b c")
foo = Foo(a=1, b=2, c=[3, 4, 5])
print(foo._asdict())
@trim tangle :white_check_mark: Your eval job has completed with return code 0.
{'a': 1, 'b': 2, 'c': [3, 4, 5]}
I assume this is not a real system that you'll have to change and expand, so it's fine
Hmm
Yeah, it's not
I'll have to think about whether this does what I'm after
If you don't want to have to manually create the types you could use https://github.com/RobertCraigie/prisma-client-py ๐
Wait, so it is also not coming to 3.11?
It got postponed again x)
Can I typehint like -> foo._asdict()?
The API returns the dict
def some_http_handler(request):
foo = database.find_foo(request["id"])
if foo is None:
return 404, "Not Found"
return foo._asdict()
then make it a namedtuple
Yeah, exactly
like, database.find_foo() can return a NamedTuple
I'm not sure how well this will work
what DBMS are you using?
PostgreSQL & psycopg2 as the driver
Psycopg2 will return list[tuple] or just tuple
Hmm
This doesn't really work, so I can't really find how to make database.find_foo() return a NamedTuple
Like it's currently```py
def get_bookings_of_user(account_id: int) -> list[BookingJson]: # my API endpoint function
bookings = database.execute_sql("SELECT * FROM bookings WHERE account_id=%s", (account_id,), fetchall=True) # will be a list[tuple]
return [booking_to_json(tup) for tup in bookings]
return [Booking(*tup) for tup in bookings]
Hmm
It says that's not valid though smfh
Or are you saying to make it return tuple instead of dict?
I need get_bookings_of_user to return the BookingJson TypedDict
why?
Because that's how I use it
why do you need named tuples then??
lol
I think I don't need a tuple thing
So, new question instead
Do TypedDicts not have a constructor?
I've tried doing MyTypedDict(*tup) and it gives an error saying it doesn't take arguments
Should I instead be using a dataclass or something?
you can't do that unless the tuple comes with the names of the fields
because you can only start it via kwargs
Why not a dataclass
Can be constructed from both positional and keyword args
I don't see much use for named tuples these days tbh
I need it to be a dict-like format. Can that be done with dataclasses?
What exactly does "dict like" mean?
Yes, they can be converted to and from dicta
it is a function in the module
Just what I would use, but yeah dataclasses are perfectly fine, specially now in 3.10 where you can set slots to true
i like having slots in principle but never actually needed it. kw_only on the other hand
not having that is painful. can't wait to upgrade.
I do regret choosing dataclasses overall (over attrs)
As in, I need a way to easily convert it to a dictionary
Like a .todict() or whatever
!docs dataclasses.asdict
dataclasses.asdict(obj, *, dict_factory=dict)```
Converts the dataclass `obj` to a dict (by using the factory function `dict_factory`). Each dataclass is converted to a dict of its fields, as `name: value` pairs. dataclasses, dicts, lists, and tuples are recursed into. Other objects are copied with [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html#copy.deepcopy "copy.deepcopy").
Example of using [`asdict()`](https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict "dataclasses.asdict") on nested dataclasses...
If I have a class AccountDataClass, with an instance called account, and I do account.asdict() will I still get the auto-suggest for the keys?
Or does it lose that?
yeah you lose that
In fact, thinking about it, I think I don't really need it to keep it. I can just typehint that it's a AccountDataClass or whatever
dont need to keep what?
if the fields have different types then you basically want to avoid using it as a dict, generally speaking
you'll get auto completion for the fields by just typing .
and you don't have to quote anything
@dataclass
class AccountDataClass:
id: int
name: str
def print_name(account: AccountDataClass):
print(account.name)
account = AccountDataClass(1, 'Bob')
print_name(account.asdict()))```would this work?
did you mean print_name there
you have to do dataclasses.asdict(account) I think
and not print(name(...
Yeah, thanks @terse sky
In my actual use-case I need a dictionary
I have an API which will return this, and it needs to return a dict cause that's what the schema says it returns
yes, that's fine, but usually what you want to do when you have dicts (and other json-like things) that have schemas is use a dataclass for them throughout your code
and do to/from dict conversions at the boundaries
Hmm
you want to work with a raw dict as little as possible
but yeah it's common that you eventually serialize your dataclass back to json for example and return that
I'd also consider looking at pydantic early on if you're doing this kind of stuff
asdict is nice but it doesn't work recursively
so if you start having schemas that are nested it won't get the job done on its own
pydantic lets you define schemas, and they can be nested and it will still provide automatic to/from dict/json
So if I'm understanding this correctly, I want to changepy class AccountJson(TypedDict): id: int # id of the account name: str # account holder's name email: str # account holder's email phone_no: str # account holder's phone number permissions: int # account holder's permissions valueto be a dataclass?
yeah
attrs asdict will work recursively though
And then I use .asdict() and .fromdict() as needed?
interesting, I didn't know that
What do you mean by "recursively" by the way? The nested thing?
Yes, that's basically the idea. I wouldn't use TypedDict in any new code
attrs ftw x)
@dataclass
class Foo:
id: int
@dataclass
class Bar:
foo: Foo
In this example, if you have a Bar and you asdict() it, the dictionary will simply contain a Foo
instead what you probably want is for the asdict() call to also get applied to the Foo member
so you end up with something that can actually go straight into json (i.e. just dict, list, str, numbers, None)
Ah right, I see, yes. I don't have any nested types so it's not actually an issue for me, but that's good to know, thanks
Yeah, if you do have nesting later it can be annoying to change is the only thing. But if you're confident you won't you can just stick with dataclass
The main purpose of TypedDict is if you already have existing code, that passes around raw dictionaries everywhere.
It's not the ideal way to write it, but it works, and you don't want to mess with it, you just want to add type annotations to help you a bit, e.g. with minor modifications/additions
If you're writing code from scratch it's better to ignore TypedDict and just passes around dataclasses and do conversions at the last second when you need them
Apparently fromdict isn't a thing. Do I instead do Account(**{...})?
yeah
well, no
There's no point doing **{}
you can just use keyword arguments
looks nicer and the IDE will flag mistakes
Or I suppose I can just do Account(*account)? Since it allows positional args?
Yes, that was the next thing I was going to say
Right
obviously that's a little more fragile
But then I don't get this validation
Correct. Life is full of trade-offs ๐
If you use pydantic, and probably there are ways with attrs too, it will automatically validate that the type is correct
I'd like to avoid going too deep
yeah this is fine to start with
I don't really need validation anyway since I know 100% it will be valid since it's just what's returned from the database, which doesn't change
I suppose if I change the schema and forget it could be helpful, but not gonna worry about that
Thanks for the help ๐
class DictConverter:
def as_dict(self: dataclass) -> dict:
if is_dataclass(self):
return asdict(self)
@dataclass
class Account(DictConverter):
id: int # id of the account
name: str # account holder's name
email: str # account holder's email
phone_no: str # account holder's phone number
permissions: int # account holder's permissions value```I'm getting a warning on the `return asdict(self)` saying it has to be done on dataclasses. How do I get around that?
I'm essentially trying to make it so that I can do .as_dict() for each of my dataclasses (e.g. Account(*account_tup).as_dict())
that would be account.as_dict()
not Account.as_dict()
what's wrong with as_dict(account)
Yeah it should be working
Yeah, that's what I want
I edited my message to correct that
yeah, but why?
To save me having to import asdict in all the files I use dataclasses
It should be working, not sure how you care calling it
eh tbh that's not a good reason. you're better off not adding this complexity for something like that.
I haven't called it, but I get a warning by PyCharm's linter
you can just ignore it
pycharm's linter isn't perfect
more important to see if mypy complains
(or something comparable to mypy)
ahhh
yeah, i mean pycharm's linter is a guide
but this base class is kind of a headache, you can already see it violates its contract for non-datalcass derived classes
Is dataclass a real type?
no
that contract is also wrong since you would have to return an optional
Yeah
yeah
you have to individually import a lot of things, in basically every programming language
if you write asdict(account) and you ahven't imported it yet, pycharm will offer to automatically import asdict
Yeah, true
In fact, since I from datatypes import * I can just add it to my __all__, like this
yeah, you can do that
def _account_to_json(account: Union[list, tuple]) -> Account:
"""
Convert account data into a dictionary.
:param account: account data
:return: account data as a dictionary
"""
return asdict(Account(*account))```this seems to work
I use to also mind my imports, then I discovered isort and now I just put whatever import anywhere and pretend they aren't there
yeah, you're returning a dict
not an Account
also, for the annotation if you're just doing * you can just annotate it as Iterable
rather than list/tuple
def _account_to_json(account: Iterable[Any]) -> dict[str, Any]:
...
I don't like using Iterable for some reason
So avoid it where possible
Have update to be a dict though
Now I'm more confused
I think I do actually want to be using TypedDict
Since I want to know what the keys of the dict are
Stop overthinking xD
The whole point of me adding this typing, is that I know what the keys are
I can't get that with dataclasses(?)
It has to return a dict though
if it has to return a dict, then it's because somebody else is calling it
maybe a step back would help
why does it have to return a dict?
Unless I do like return {'response': Account(*account)} or something
Because it's the response from an API request, which has to be a dict since that's the return schema
Yeah but in that case, you dont need the types
it is no longer your concern
I don't understand what either of you are saying there
I want the types to make my programming easier
when you say "response"
I've coded an API, and a program which uses that API. I want to know the types so that I can make development of the program easier
it sounds like this is going over the network
Yes, it is
Okay. then you don't need the types once it goes over the network?
you're doing this conversion at the very last moment
(you should be)
Hmm
somewhere you have a function that actually sends a dict as a response yeah
Ohh I think they are making a an api and are themselves using it
def respond(data: Dict[str, Any])
But in that case you know it is correct
yes, but they still aren't calling that function correctly, they're still sending a request
and receiving a response
Yes. I'm using the API myself
if you're using the API yourself, then what you can do is on the client side, have a function that receives the data, and turns it back into an account
Like, there's only two possibilities here: the client code can see the API code, or it cannot.
in the first case, you can just reuse the dataclass on the client side and immediately re-create it. And now you're using a dataclass again, which is much better than a typed dict.
In the second case, the client code is not benefitting from the type hints of the API code anyway.
but usually if you're writing code just for yourself, and the code is shared, there's not that much reason to convert things to json and back in the first place
you could just pickle stuff
# The code for endpoint
def get_account(account_identifier: str) -> Response:
"""
Returns the data of provided account.
...
"""
account = _convert_to_account(account_identifier)
if account[0] == -1: # account not found
abort(404, account[1])
return make_response(asdict(Account(*account)), 200)
# Send request to the endpoint
def get_account(account_identifier: Union[str, int], *, auth_token: str) -> Account:
resp: requests.Response = requests.get(
f"{BASE_URL}/accounts/{account_identifier}",
headers={
"Authorization": f"Bearer {auth_token}",
"Content-Type": "application/json"
}
)
if resp.status_code == 200:
return Account(**resp.json())```does this seem right?
I think that's what you were saying to do?
@terse sky
These two functions seem to have identical names
But yes this seems like the idea
Really you want convert_to_account to already return an Account
Or an optional Account
They're in two different files, so that's why they have the same name
Yeah, that seems smart actually
I'll make a note and change that
Then I just do```py
account = _convert_to_account(account_identifier)
if account is None:
abort(404, 'Account not Found')
return make_response(asdict(account))```
It means I lose some precision as to why it failed to convert (unknown email / username / id) but that's not a big deal, and if I really want I can make _convert_to_account return like Union[Account, Error] where error has a .status_code and .reason or something
Or even make _convert_to_account abort (I think I can do that)
Suppose this is the best option
I probably wouldn't ever make things abort
no reason to do that
you can throw
and if it's not caught that will result in an abort
but more likely you want to at least catch at top level and log an error before exiting
yeah, that's fine, just saying you should throw and not abort
maybe that's what you meant
flask.abort(status, *args, **kwargs)
Raises anHTTPExceptionfor the given status code or WSGI application
well, it is throwing an exception
awful naming by flask
os.abort()
Generate a SIGABRT signal to the current process. On Unix, the default behavior is to produce a core dump; on Windows, the process immediately returns an exit code of 3. Be aware that calling this function will not call the Python signal handler registered for SIGABRT with signal.signal().
this is what people who know about computers expect from a function named abort
I'm not sure I like the whole idea of "returning HTTP codes with exceptions"
from my (limited, granted) experience, it makes it tempting to put too much business logic into HTTP handles
I can't comment as to whether the function should behave that way or not, it's not my wheelhouse.
But according to the docs, all the function is doing is throwing.
So it should be called raise or throw, or something similar
I would just raise HTTPException(your_code) and not add an extra abstaction
abort is a super duper loaded term which to many people is going to mean specifically not throwing an exception, but simply ending everything immediately
I don't really like the name either
abort(Response('Hello World'))
Excuse me... wtf
But it's what flask uses
yeah I understand
anyway "calling abort" (i.e. throwing) is fine
or it could be fine, I don't reallyknow ๐ but in that case you can return an Account rather than Optional[Account]
This is an HTTP goto
Nice lol
well, lots of things are <something> goto's
returns are gotos
etc
all exceptions are gotos
well
it's an easy way to completely exit the current request I suppose, even if you're nested
and then the framework I suppose catches that exception?
would be my guess
yeah, and sends a response
Yeah. I don't think there's anything wrong with that function, it's arguably a little less error prone than telling peopl.e what exception type to throw
if they need to exit the current request
just the name is troublesome
if it was at least called abort_request,or better end_request_with_error
etc
should a function that doesn't return anything be typed as -> None ?
Yes
Unless it is guaranteed to raise an exception, in which case if you wanna be fancy you can annotate it as typing.NoReturn
oh now that pytype implements Final without crashing it's easier to check it for bugs on more codebases
already found one: https://github.com/google/pytype/issues/1114
The following is valid for type hinting (taken directly from https://www.python.org/dev/peps/pep-0585/ ), but fails to type check. l = liststr actual behavior: [1/1] check testnew FAILED: /home...
I think there's more I'll look later
most of the other errors are hard to find since pytype doesn't support | syntax yet outside of .pyi files
I think I'll wait for that before looking again
@oblique urchin gratz on reveal_type
that was always going to be the least controversial, I think
assert_never() is the most interesting "new" addition, and reveal_locals() is the next least controversial probably
well, I don't think assert_never is controversial, just less established
reveal_locals() might not make it, see the last comments on https://github.com/python/cpython/pull/30839
I see
personally I'm most excited about assert_type(), that actually unlocks new behavior
what new behavior are you interested in? the thing that came to mind immediately is type checkers unifying their unit tests
yes, typeshed unit tests
not that they should literally share unit tests, but just everybody now has an obvious way to write them
assert_never() can be implemented in user code and everybody implemented reveal_type() already
has assert_type feedback has been positive?
oh I thought Guido had suggested this originally
ah yes you mentioned this ๐
Guido committed my change to ClassVar (it was an incredibly trivial change, after all)
except for one interesting interaction with a unit test
have you come across the python-dev thread about PR "spam"?
yes
I don't know what to make of that thread; weirdness all around
it seems non-explicitly directed at typing changes, but as far as I can tell nobody is really doing anything wrong
there are indeed some people who just come and approve random PRs without saying anything
but the OP was clearly talking about Nikita's changes, which seemed quite unfair to him
Wikipedia has this rule https://en.wikipedia.org/wiki/Wikipedia:Assume_good_faith, I feel like that's a good rule to follow in online interactions in general
So not great for that poster to just assume all of these changes were being made for GitHub points or something
that sounds like something I would have done in middle school too if in the right position (approve random PRs for GH stats), and then be embarrassed about it 8 years later
it seems like pytype often implements features separately for stub files and normal files
I wonder what about their codebase makes that necessary
yes, they have a wholly separate parser for stub files
not sure why. Perhaps because originally they were for Python 2?
and stub file syntax was always Python 3
Maybe
I suspect there's a lot of people who have no idea you can do this https://github.com/google/pytype/issues/1114
The following is valid for type hinting (taken directly from https://www.python.org/dev/peps/pep-0585/ ), but fails to type check. l = liststr actual behavior: [1/1] check testnew FAILED: /home...
I use it sometimes with sets
Rarely
I don't recall seeing it in code much
Even for generic classes that aren't bultin
I'm not so sure about the Strict name for conveying what the difference is, but I also don't have a better suggestion
How come reveal_type() is the only one I knew about ๐คฏ
Something I've wondered, what if the function returns if it doesn't error? I.e. something like -> Union[NoReturn, int]. Would you just do -> int or do you include the NoReturn?
just int
Exceptions aren't part of the typing system in general.
NoReturn is for the special case of a function that always raises an exception or otherwise quits - because then the type checker can understand that functions like sys.exit operate like a return or raise.
I have been working on a pytest confest.py (not made it a plugin yet) that runs pyright on files ending in types.py (so like test_foo_types.py), still a few quirks to work out, but happy with the result so far
you didn't read the mypy documentation carefully enough? ๐
I've only read the typing PEP in sections, isn't reveal_type() the only one stated there?
reveal_type() isn't mentioned in PEP 484, but a bunch of other PEPs use it in examples without explanation
You might be interested in: https://github.com/RobertCraigie/pytest-pyright
Just a warning its DX is not very good at the moment though lol
class Field:
"""
Represents a database field.
Parameters
----------
table: Table
The parent table class that
the field is inside.
"""
table: Table
class Table:
"""
Represents a database table.
Parameters
----------
name: str
The name of the table.
id: int
The id of the table.
fields: List[Field]
The fields within the table.
"""
name: str
id: int
fields: List[Field]
how do I typehint table: Table within the Field class?
bearing in mind ive defined Table afterwards..
put it in quotes (or on 3.7+, use from __future__ import annotations)
You mean the former?
im saying im using from __future__ import annotations but i dont like using it
im doing ```py
bot.db = Database(
database_server=938485141616070696 # discord server to use for the database
)
where ```py
class Database:
"""
Represents a database connection
This class is used to interact with the Discord Database server
Parameters
----------
database_server: `int`
The discord server to read, write and edit data from.
.. versionadded:: 0.1
"""
def __init__(self, *, database_server: int):
self._database_server = database_server
self._tables = []
but bot.db isn't getting typehinted
like when i use methods with it
my ide isnt recognising it as a class of Database
how do I typehint it to Database?
I cant do bot.db: Database for some reason
yes, type checkers won't recognize it if you add attributes outside the class definition
how do i make it recognize it?
ok
I just upgraded my numpy and I'm seeing some type errors, some of which are surprising
it no longer considers ndarray to be a List[float] which probably makes sense
but a call to np.any is now complaining that it returns type bool_, not bool?
seems like just casting/constructing to bool from bool_ works. Kind of an odd situation but I assume numpy has a reason
it what?..

Yeah, it basically turns all your annotations into strings
interesting
As if you wrapped all of them in quotes
I think someone complained about this somewhere. Logically the error makes sense because np.bool_ is an independent type, but they want to be able to treat it like a bool because that's generally safe.
Before the update, there was code assigning ndarray into a variable annotated as list of float
And it was passing
probably it was just Any
huh all github repos 500 for me
or I'd try to find the issue I alluded to above ๐
Yeah, gh is (partially) down atm https://www.githubstatus.com/
Welcome to GitHub's home for real-time and historical data on system performance.
using VS Code with Python and Pylance extensions, I would expect the editor to show an error when trying to deconstruct the call to the function
how can I enable that?
the return type is "incompatible" with the actual implementation
if you wanted an error here this should be -> typing.NoReturn/Never
still no error
yeah it should error
that should error
you need to enable basic type checking
exactly, do I need some pip package? VS Code settings?
done!
That would indeed be great
Yeah sometimes error messages get very big
also, fyi Tuple[int] means strictly a one-element tuple. It's very rarely a useful type
Tuple[int, ...] means a tuple of any number of ints
In your case you want tuple[int, int, int]
Just a matter of someone wrapping the mypyplayground repo into snekbox
I know, I only used that signature to illustrate my question for type checking
๐
it doesn't do anything special so far, it just imports the code
at runtime TYPE_CHECKING is False so that's what it sees
it's something I need to support better
Does it not support cyclical imports then?
as much as Python supports them
basically it imports everything, then checks every module independently
and just looks at the runtime type/function objects from other modules as needed
the main area where that causes problems is attributes, because you can't tell from a class definition what attributes actually exist
so for that it keeps some cross-module information around
and defers errors about non-existent attributes until the whole program has been checked
hello, i am observing strange issue in some old code i am trying to annoatte, but fail to identify the cause... i have tried to isolate short example, but that one works no problem:
# writeutil.py
ItemType = TypeVar('ItemType')
def write_strings(writer: Callable[[str], Any]) -> Generator[None, ItemType, None]:
while True:
item: ItemType = yield
if isinstance(item, str):
writer(item + "\n")
from writeutil import write_strings
def some_usage(output: TextIO) -> None:
sink: Generator[None, str, None] = write_strings(output.write)
# other stuff to do...
sink.send("ahoy")
Above seems to work fine, i get no error in mypy...
But in my code where there is a bit more complex module structure, i get error for line:
sink: Generator[None, str, None] = write_strings(output.write)
stating:
Incomatible types in assignment (expression has type"Generator[None, ItemType, None]", variable has type "Generator[None, str, None]")
Do i have some invalid annotation or need to annotate function call somehow?
ItemType has no meaning in that function
sure i understand that, but why is it not unpacked/interpreted before importing write_strings... or do i need to import it to make it work?
i mean above code excerpt does not report mypy error, but due to some possibly other code/relations in my codebase error is reported, which is a bit confusing to me
why are you making this generic in the first place?
maybe i didnt get some concept right, am not sure how to specifiy when using the "consumer/generator", what type of variable it should provide
it's some legacy code (different author) -> write_strings is supposed to be (in my understanding), generic sink that logs str only from any received items
but wont it prevent other items to be sent to that "generator"?
no but thats why you use a type checker, it will tell you if you send something to this that you shouldnt
i mean legacy code is dumping/sending all the different types of items into that fn, that/s why i assumed it has to stay "typevar"
but now i realize i can use "Any" instead of typevar and functionality will stay basically same ๐คฆ
you shouldnt use Any, that defeats the point of adding type checking to this function
i realize that, but the functionality of that write_strings was "prescribed" as "accepts all types of items, does internally something to strings only"
that's good question no one can possibly answer at this point ๐ it's rather old chaotic codebase ๐ฆ
to clarify - i am annotating some small/mid size app created in the past to make it a bit easier to orient-in and possibly allow code cleanup at later stage
one difference in target codebase is, that the write_strings is wrapped in some functools.wraps and coroutine (not the python async, but manually annotated fn), which adds possibly some extra fluff ๐ฆ
thanks for directing questions though, worst case i stay with Any instead of ItemType = Typevar as a workaround until code is reworked to more reasonable state
If I have a list of types, is there a way to typehint "one of this list"? Something like```py
API_TYPES = [Account, Booking, Facility, Login, Sport, Token]
API_TYPE = Union[*API_TYPES]
no
Why do you want this?
Gimme a sec
Basically I have a parameter which will be either ClassType or 'ClassType[]'
And I need to be able to typehint the parameter as Union[ValidClassTypes, str]
API_TYPES = {class_.__name__: class_ for class_ in (Account, Booking, Facility, Login, Sport, Token)}
API_TYPE = Union[Account, Booking, Facility, Login, Sport, Token]
def perform_test_not_expecting_error(
test_name: str,
function: Callable,
*args,
expected_response_type: Optional[Union[API_TYPE, str]] = None,
**kwargs
) -> None:
try:
result = function(*args, **kwargs)
except Exception as e:
raise UnexpectedError(test_name, type(e), e.args[0])
else:
if expected_response_type is not None: # expected_response_type was provided
if isinstance(expected_response_type, str):
if expected_response_type[-2:] != '[]':
raise InvalidResponseType(test_name, expected_response_type)
expected_type = API_TYPES.get(expected_response_type[:-2])
if not expected_type:
raise InvalidResponseType(test_name, expected_response_type[:-2])
# We expect `result` to be list[expected_type]
# First make sure that result is a list:
if not isinstance(result, list):
raise InvalidResponse(test_name, expected_response_type, result)
# And now ensure each item is of type `expected_type`
for item in result:
if not isinstance(item, expected_type):
raise InvalidResponse(test_name, expected_type.__name__, item)
else:
if not isinstance(result, expected_response_type):
raise InvalidResponse(test_name, expected_response_type.__name__, result)
print(
f"Test {test_name!r} passed! No unexpected errors occurred "
f"and got the expected response type of ({expected_response_type})."
)
```This is the actual code. Essentially ensuring that a function returns the expected type
This should work, was just wondering if there was a nicer way
Actually it doesn't seem to work. PyCharm gives this warning:
No clue what's going on
why do most of the asyncio lock primatives always return True
I supposed to feel a bit like threading.Lock?
anyway typeshed seems to think they return something useful - like a bool https://github.com/python/typeshed/pull/7122
Not sure how I feel about this.
It definitely sounds like a bandaid
Weirdly specific, that's a better word maybe
I like it but a better more general purpose solution would be very cool
idk, it may feel weirdly specific until you realize that what dataclasses are basically doing is bringing what would largely be built-in class functionality to python
If you want to statically describe your class, and you also have a typical init function, you're already repeating each field four times, which is horrible
This would actually solve the exact issue I've had in the past where I want to write my own decorators that do relatively simple things before delegating back to dataclass/attrs decorators (like change defaults, add some other simple functionality, etc)
you can't really do this right now because your decorator won't work properly with the typechecker
hygenic macros?
Yeah they'd be nice
I would imagine some kind of procedural language on types. Like in Zig
But that would be quite a lot of work to implement in all the different type checkers
Does pyright see function as a method here?
class Test:
def __init__(self, function):
self.function: Callable[[int], int] = function
file.py:6:14 - error: Cannot assign member "function" for type "Test"
ย ย Expression of type "() -> int" cannot be assigned to member "function" of class "Test"
ย ย ย ย Type "() -> int" cannot be assigned to type "(int) -> int"
ย ย ย ย ย ย Function accepts too many positional parameters; expected 0 but received 1 (reportGeneralTypeIssues)
or where is it getting from when the param is not typed?
Weird that this is the issue
although if you move it to a to function signature the error stops
which is where it should be anyway
I honestly can't parse Pyright erros, why cant they be like mypys xD
where can i identify/find type annotation for argparse.ArgumentParser.parse_args() method? googling does not give any obvious hints/reasonable links for my understanding, and it feels like quite widespread lib imho to have this already addressed
My legacy code has nsargs (output of parse_args()) pased around, and i need to annotate it properly - Any feels a bit too generic
Lib/argparse.py line 1824
def parse_args(self, args=None, namespace=None):```
Maybe with the help with typeshed
which also doesnt seems to document it from just looking at it
Actually nvm it is right there, how did I miss it lol
# The type-ignores in these overloads should be temporary. See:
# https://github.com/python/typeshed/pull/2643#issuecomment-442280277
@overload
def parse_args(self, args: Sequence[str] | None = ...) -> Namespace: ...
@overload
def parse_args(self, args: Sequence[str] | None, namespace: None) -> Namespace: ... # type: ignore[misc]
@overload
def parse_args(self, args: Sequence[str] | None, namespace: _N) -> _N: ...
@overload
def parse_args(self, *, namespace: None) -> Namespace: ... # type: ignore[misc]
@overload
def parse_args(self, *, namespace: _N) -> _N: ...
@cold osprey
๐ฎ typeshed, thank you! going to study...
Note though that the typeshed annotations don't give you full type safety, typeshed doesn't know what's going to be in your argparse namespace
That's not something that the standard type system can do in this case
typer is nice for argument parsing that supports type annotations
right, it's sufficient for my use case to guide the code user towards specific code if needed
thanks, will note for investigation for new code or code cleanup phase, current case is annotating legacy only with zero to minimal changes unrelated to annotations
You might like mypy-clean-slate
neat! ๐ i started on about 500-ish errors in strict mode, and am down below 10 currently, fun few days it's been ๐
nice
What's the status of MyPy ParamSpec? I want to use it but this project needs to support MyPy ๐
They said they would bring basic ParamSpec support this month
idk what is meant by basic though
The latest release supports it for functions I believe, but no Concatenate yet
Oooh, that's fine! I just need ParamSpec
I have a decorator that applies return values :3
Is it possible to write typings for a module using ModuleType somehow maybe? It's for Cython. There's a file called Cython/Shadow.py which is imported under cython. I can probably type a Protocol and then be able to use from cython import my_module.
In reality I can patch sys.modules to be able to use from cython.my_module import ... but is there a way to tell the static type checker about this?
Implementing ModuleType might not even be necessary as typing.re/io never did that and I think they were usable as modules
pretty sure mypy never actually supported those
Oh well maybe it doesn't work then
๐ค yeah that's a shame
Any idea why MyPy is failing even though I set --python-version 3.10? https://github.com/cython/cython/runs/5058388880?check_suite_focus=true#step:5:11574
MyPy is run like this: https://github.com/cython/cython/pull/4335/files#diff-17019d8813f584a97bc5531faa1df3f1a590413d61a77d7d93db76df66163b63R1621
I assume it's complaining about the positional-only character
looks like mypy itself is run under 3.6
that means it doesn't have a parser that can understand pos-only syntax
Ah, hmm. That puts me in a very difficult situation.
to fix it you could make typed-ast support positional-only syntax ๐
Sebastian Rittau tried that years ago but couldn't get it to work
That doesn't give me much hope ๐
and now we've basically given up on typed-ast and mypy uses the builtin parser
I see
Why do you need positional only anyway?
Just prefix it with a __ and it should achieve the same thing
Why did typeshed CI permit this constructor? It doesn't match the superclass: https://github.com/python/typeshed/pull/7124/files#diff-719eeffb0007f50384bab4a97e6f975c4d4b828ebee1cfa18691e5b91cc932ddR36
do you mean it doesn't match the runtime?
the comment you're pointing at is a class with no superclass?
class Lock(_ContextManagerMixin):
You need to expand the line for the fragment to link :/
oh right, mypy doesn't care about LSP violations for __init__
because they're too common in practice
Not sure what to do about the loop kwarg though
It's still there in 3.10 - it just NoReturns, so @overload?
like it always throws an error if you supply it?
then I'd just omit it from the stub, so type checkers can tell you not to pass it
Hmm, might be a change that's insufficiently esoteric that mypy_primer might complain
seems like we'd generate new true positives
what should be the typehint for other here
class A:
def __lt__(self, other) -> bool:
if not isinstance(other, A):
return NotImplemented
return ...
other: object?
also i dont need to account for NotImplemented in the return type annotation, do I
aight
mypy strict mode?
do pyright strict next, then pyre non-strict ๐
What should be returned if we dont know how to compare objects?
False, True or NotImplemented?
how about
class A:
def __lt__(self, other) -> bool:
if not isinstance(other, A):
raise NotImplementedError("describe the error")
return True
a = A()
b = 5
print(a<b)
Traceback (most recent call last):
File "/home/naa/repos/exp/test.py", line 14, in <module>
print(a<b)
File "/home/naa/repos/exp/test.py", line 8, in __lt__
raise NotImplementedError("describe the error")
__main__.NotImplementedError: describe the error
;b
lets it be raised as a NotImplementedError
NotImplementedError is already existing built-in available exception 
nobody forces to consider raised errors in type hints ๐
NotImplementedError is for a different purpose, when a base class needs to have its subclasses implement a method
class BaseClass:
def method_that_has_to_be_overriden(self):
raise NotImplementedError
class Child:
def method_that_has_to_be_overriden(self):
# implementation
return NotImplemented
lets raise explicitely TypeError then
it sounds funny to return NotImplemented only in order to have hidden TypeError
It's shorter and the message is pretty obvious
the behavior is obscure in comparison to raising exception directly
No because if you return NotImplemented Python goes over to the right-hand equivalent (for example __rand__ for __and__)

looked with confusion at appeared from nowhere __rand__ and __and__
completely expected behavior, completely expected, thought Darkwind
i thought we just had overloaded < operator
Yeah I know we're talking about __lt__, where the behaviour would be __gt__ I believe: ```python
x < y
result = x.lt(y)
if result is NotImplemented:
result = y.gt(x)
if result is NotImplemented:
raise TypeError(...)
okay, I get it where appear rand and and
!e ```python
class Lefthand:
def add(self, other):
return 1
class NotImplementedHand:
def add(self, other):
return NotImplemented
def radd(self, other):
return NotImplemented
class Righthand:
def radd(self, other):
return 2
print(Lefthand() + Righthand())
print(NotImplementedHand() + Righthand())
print(Lefthand() + NotImplementedHand())
print(Righthand() + Lefthand())
@blazing nest :x: Your eval job has completed with return code 1.
001 | 1
002 | 2
003 | 1
004 | Traceback (most recent call last):
005 | File "<string>", line 18, in <module>
006 | TypeError: unsupported operand type(s) for +: 'Righthand' and 'Lefthand'
__and__ is for & and originally the first example I could come up with, but it's probably easier to visualize __add__
yes this is why you return notimplemented
Can a bytearray be passed where AnyStr is expected?
Apparently yes
Ah there, I found the relevant docs
As a shorthand for this type, bytes can be used to annotate arguments of any of the types mentioned above.
so bytes is just promoted to typing.ByteString which
This type represents the types bytes, bytearray, and memoryview of byte sequences.
This is rather counter intuitive, but it seems that because almost no one uses bytearray, it is made an exception in the type system for convenience
@rustic gull
I wonder how many of these "conveniences" are there
Is there a good way to set a method on a class to be a union type?
class SomeClass:
def example(self) -> list[int]:
return [1, 2, 3]
class SomeChildClass(SomeClass):
example = [1, 2, 3]
In this example I want example to be Callable | list
Declaring it on the child class with a type removes warnings (eg. example: list = [1, 2, 3]) but wondering if there is a way to define this on the parent class?
One workaround we had was to do this:
class SomeClass:
def default_example(self) -> list[int]:
return [1, 2, 3]
example: Callable | list = default_example
What is wrong with the workaround? Although isnt that a runtime error?
because default_example isnt defined yet
Also the workaround isnt really that, doing example: list = [1, 2, 3] on the subclass should still lead to warnings
Nothing really, just wondering if there was a "better" way
We have a bunch of attributes defined at the top of the class, then we have the method then another property - that's a little annoying
Well, a better way would be to see why you are needing to change an method into an attribute when you are subclassing
Our base class defines the default implementation but the child can override it with a hardcoded list, if it likes
The class is used to build pages from SQLAlchemy models
The method on the base class will dig into a model and get a list of columns
A child class might want to manually set a subset of columns to use
We could make the child use a method and just return the hardcoded list, but would be nice if we could type hint that it's a union type and use a list
Unfortunately method definitions introduce a new type declaration, i.e. shadow the old example: list | Callable
Therefore you need to explicitly assign it as an attribute if you want a "method-or-literal" like so
And there's really no way around that
Thanks for the info, suppose we'll stick with the workaround of setting it to a default explicitly ๐
Yeah actually I am very confused now
class SomeClass:
def default_example(self) -> list[int]:
return [1, 2, 3]
example: Callable[[Any], list[int]] | list[int] = default_example
class SomeChildClassA(SomeClass):
example = lambda self: [1,2,3]
class SomeChildClassB(SomeClass):
example: list[int] = [1, 2, 3]
class SomeChildClassC(SomeClass):
def default_example(self) -> list[int]:
return [1, 2, 3]
example = default_example
class SomeChildClassD(SomeClass):
def example(self) -> list[int]:
return [1, 2, 3]
main.py:23: error: Signature of "example" incompatible with supertype "SomeClass"
Found 1 error in 1 file (checked 1 source file)
Yeah I dont know what is happening here x)
Also if I change the Any to a TypeVar bound to SomeClass then I also get an error on SomeChildClassC
main.py:20: error: Incompatible types in assignment (expression has type "Callable[[SomeChildClassC], List[int]]", base class "SomeClass" defined the type as "Union[Callable[[T], List[int]], List[int]]")
main.py:23: error: Signature of "example" incompatible with supertype "SomeClass"
Try either ... or [Self] (if your type checkers supports it)
I am just playing around on https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=e1357fb1614ff293858ed686b3e3bb50
Callables are contravariant on their arguments, so (ChildD) -> list[int] isn't compatible with (Any) -> list[int]
I can try ... but mypy doesnt support Self yet, maybe on main
Ohhhh
Wait the other way around right?
(ChildD) -> list[int] isn't compatible with (bounded T) -> list[int]
Because SomeChildClassC did work with Any
But I had completely forgotten about the contravariant part
Using ... gives the same as using Any, and yeah Self wasnt merged in yet
Self is indeed the behavior you're looking for
You can probably use a bound typevar as a replacement but I'm not 100% sure how that interacts with contravariant attributes and subclassing
I see, although if I just use a generic it should work no? The thing I find weird here is mostly the inconstancy in erros
class SomeClass:
def default_example(self) -> list[int]:
return [1, 2, 3]
example: Callable | list[int] = default_example
class SomeChildClassA(SomeClass):
example = lambda self: [1,2,3]
class SomeChildClassB(SomeClass):
example: list[int] = [1, 2, 3]
class SomeChildClassC(SomeClass):
def default_example(self) -> list[int]:
return [1, 2, 3]
example = default_example
class SomeChildClassD(SomeClass):
def example(self) -> list[int]:
return [1, 2, 3]
SomeChildClassA is perfectly fine with it, I guess it is because lambdas arent typed checked the same way
SomeChildClassB is fine
SomeChildClassC is fine, but SomeChildClassD is not fine
Yeah odd
maybe you should use property?
This is not for me, I was testing with their issue and stumbled on this weird behaviour
my bad
we can use metaclass to annotate and implement class-property
ok, fine
?
You can chain the descriptors as of 3.9, I would guess that mypy would already have some support for it
any reason why mypy raises invalid syntax on this line
it complains about type comment
# type: here.should.be.valid.python.syntax - you have syntax error here
I think the issue is you have both an annotation and a type comment
you only need one
then it nags about the str not having .seek and what not
no. that's not what the error says
if i remove the comment
because you have errors in code or your annotation isnt valid
hm
alr ima try a workaround
why are you annotating it as Type?
fp: Union[str, Type[os.PathLike], Type[io.BufferedIOBase]]
@whole panther
pydantic
work around was just to set it as io.BufferedIOBase and use pre on the validator
Ah alright
Thanks
is there a way of using Generic TypeVars in a Union?
HKTs eventually; I think there's still other higher priority/lower hanging fruit in python typing though
If you mean using a Generic[T] inside a Union you can, otherwise I am not sure what you mean
SN = TypeVar("SN", int)
class Foo:
x: Generic[SN]
this is what mypy wants
which is fine
till u stick it in a union
yh ik just an example
so theres a typevar which represents an str which has its own formatting and what not.
So doing Union[..., T] is fine, but MyPy asks for Generic[T] or Protocol[T] and im abit stuck here
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.
MyPy
acord\payloads.py:90: error: Type variable "acord.models.Snowflake" is unbound
acord\payloads.py:90: note: (Hint: Use "Generic[Snowflake]" or "Protocol[Snowflake]" base class to bind "Snowflake" inside a class)
acord\payloads.py:90: note: (Hint: Use "Snowflake" in function signature to bind "Snowflake" inside a function)
Code
SN = TypeVar("SN", str, int)
class Message(pydantic.BaseModel, Hashable):
message_reference: Union[dict, SN]
I don't know what that type is supposed to mean. Can you explain what you want message_reference to hold?
so in a nutshell working with discords API
to reply to a message you can use its ID or a message ref object which is a dict
you also receive the reference as a dict
SN stands for snowflake which is the ID
But how are you actually using the code?
?
It's not clear to me either
Well, at some point you are passing something to message or using in some way
yup
At that point is probably where the error originates
What values do you want message_reference to take?
return Message(**(request.json()))
Just dict, or something else?
a dict or the SN object
What is an SN object?
the typevar
What is it? a string? a number? a list?
It is the type var, but I dont get why no just use the Snowflake then
Why not this, for example?
class Message(pydantic.BaseModel, Hashable):
message_reference: Union[dict, str, int]
string/number
guess that works
? I thought it was a Snowflake, now I am more confused
If you want a type alias for a repeating pattern, you can do this:
Snowflake = Union[int, str]
class Message(pydantic.BaseModel, Hashable):
message_reference: Union[dict, Snowflake]
TypeVars are for a different purpose: for linking two types together
(you can read more here: https://dev.to/decorator_factory/typevars-explained-hmo)
Unrelated question:
are there any resources/blogs/tutorials that focus on typing? apart from the docs of mypy, pyright and pyre
@oblique urchin I'm going to look into that callable() check to see what removing it breaks (if anything) and if there's alternatives; it was the cause of that dataclasses bug and will probably continue to be a surprise/source of bugs to anyone trying to implement their own typeforms
a snowflake is essentially an str or int
we're trying to put something together at typing.readthedocs.io, but not much progress so far
nice, thanks
It it a model? acord.models.Snowflake
Or did you just define a Union on that module?
afaik those are the best sources; pyright has a few good ones like the only description of the overload resolution algorithm
called Snowflake
just defined as a type var
(it's not in the PEP)
called Snowflake
I was thinking of making some kind of micro-blog where I post some easy to understand articles (preferably without type theory nonsense). Also including some common pitfalls and misunderstandings (like using NoReturn when the function simply doesn't have a return, or using a typevar when you need a type alias, or using Any when you probably wanted object, etc.).
I wonder if that would be useful to at least one person
idk if pyre is the best beginner resource because it has some very strict views on a few things
definitely! I've actually been thinking of writing a style guide for typing too
Something in this style maybe: https://dev.to/decorator_factory/typevars-explained-hmo
here's pyanalyze's description for that: https://pyanalyze.readthedocs.io/en/latest/reference/signature.html#pyanalyze.signature.OverloadedSignature.check_call
ye go for it, its confusing enough
Ahhh, yeah in that case you should be able to just use it as a TypeVar, if you need to proliferate the Type forward
The hint suggests to use Generic[Snowflake] or Protocol[Snowflake] as a base class
So to subclass
ah
well, in your case you don't need type vars
ye forget that
Why not wrap this raw value in a proper object? I can imagine it's a pain in the ass to deal with a value that's either an int or a string-encoding-an-int
like ```py
@final
class Snowflake:
def init(self, source: Union[int, str]) -> None:
...
def timestamp(self) -> datetime:
...
You could also then make so that the conversion is in the model itself
Or idk, do you have to do that for every field that uses Snowflake? Again never used pydantic
This is my plan for the near future:
- `TypeVar` series:
- What `TypeVar`s are for (copy&paste from dev.to)
- Using standard library generics
- Variance
- Making your own generics
[- More examples of generics]
[- Overengineered/advanced/esoteric uses of generics]
- `NewType` vs `TypeVar` vs type aliases
- "Best practices" series?
- Use `object` instead of `Any`
- Use read-only collection types
- "Type smells"?
[- When to _not_ use type hints]
- Interfaces: ABCs and Protocols
``` any additions/comments?
it would also be cool if some native speaker dedicated a bit of time to read the mess that I write lol
Not a native speaker but happy to proofread! Other confusions I have seen include: AnyStr (which is a TypeVar, not a union); Optional means | None, not an optional argument
ParamSpec and Concatenate and just annotating decorators in general might be something else
it is very confusing
I have seen some bizzare misuses of typing in some real code, so I think I'm slightly more equipped to talk about stuff than a year ago 
One issue is, though: how do you embed code snippets with errors in some standard way?
Maybe that's a good use case to add to pyright-playground
like, export a thing you can embed that will show errors
@oblique urchin Completely random but: how to pronounce your name?
almost like the color. "yella"
ah

