You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note that because dict is invariant, you might run into some issues e.g. with dict[str, str]. That is, you will not be able to pass something with type dict[str, str] to a function that takes a JSON
For such use cases you can use cast, and if you don't need mutability, something like the following might work:
I tried to do that but a recursive type alias doesn't work in mypy right now, and I'm not sure how to make it work. In the mean time I use JsonDict = Dict[str, Any] (which is not very useful but at least clarifies that the keys are strings), and Any for places where a more general JSON type is expected.
(I'm sure you meant t.Mapping[str, 'JSON'].)
Reacted by Bernát Gábor, Noam Elfanbaum, Dasha, wouter bolsterlee, yoshi-lyosha, μrainЗ⑨, Olúwátóósìn Anímáṣahun, Christopher Greening, Atef Arfaoui, franco-atman and 11 more
Reacted by Chris Chang, dolamroth and Roman Novatorov
If you read RFC 4627 it says a JSON object must be an object or array at the top level (RFC 7159 loosens that to any JSON value, but it's not an accepted standard). If you want to play it safe with what json.loads() takes, then you can just flatten it to:
I guess the real question is how far you want to take this, because if you assume that most JSON objects only go, e.g. 4 levels deeps, you could handcraft accuracy to that level:
I guess that'll work, but I'm not convinced that it's very useful to
do the multiple levels.
The next question is where this would live? Would you add it to
typing.py? Or to the json module? It would have to be added both to
json.pyi as well to the actual implementation module. :-(
OK, so JSONType = t.Union[str, int, float, bool, None, t.Dict[str, t.Any], t.List[t.Any]] seems to be the best solution.
As for where it should go, I don't have a good answer unfortunately. It's like the collections.abc issue; do we have a single place to keep all types -- i.e., typing -- or do we keep types in the specific modules that they relate to (i.e., json in this case)? I guess this would be the first type that wasn't a generic container if we add it to the stdlib somewhere, so there is no precedent to go by.
If we put it in typing then at least all types related to the stdlib are in a single location which is handy for only having to do import typing as t to get at all types. Unfortunately that doesn't work for for third-party libraries so it doesn't seem like the best way to go. So I guess my suggestion is the json module should house it and keep the name JSONType for the module attribute. If you agree I will open an issue on bugs.python.org to add the type to Python 3.6 and then also an accompanying issue for https://github.com/python/typeshed to add a json.pyi and you can close this issue. Otherwise I'll submit a PR to add the type to typing.
Reacted by Nick Sweeting, Mattwmaster58, Olúwátóósìn Anímáṣahun, jel and HongyuanQiuiii
I think it's best to add it to the json module; we can't keep adding everything to typing.py (even the io and re types there are already questionable). Code that wants to use these in Python 3.5 or earlier can write
if False:
from json import JSONType
(Or they can copy the definition into their own code.)
Question: should we name it JsonType or JSONType? There doesn't seem to be a strong convention here in the Python stdlib -- we have XmlListener and HTMLParser... But somehow I am beginning to prefer AcronymsAreWords.
Reacted by Olúwátóósìn Anímáṣahun, Gabriel Curio, Lawrence Chan, Jaakko Sirén, Jack Leow, Ryan Kroon and Tonny Madsen
I'm marking this for 3.5.2 so we at least have the discussion. I'm still not sure of the solution -- add it to typeshed/.../json.pyi or to typing.py? It can't appear in the stdlib json module until 3.6 but it could appear in typing.py in 3.5.2 (since typing.py is provisional), but I'm not excited about pushing everything to typing.py. So maybe adding it to typeshed/**/json.pyi now and the stdlib json module in 3.6 would be best? If you want to use it then you'd have to write if false: from json import JsonObject.
(I've got a feeling I'm just summarizing where we ended up before but I'm currently in the mood to make the definitive list of things to discuss and/or implement before 3.5.2.)
I'm not a fan of having a "partial" JSON type that soon degenerates into Any. Type checkers would enforce things inconsistently. As soon as you descent into a JSON object you'd have to manually annotate the result to get type checking for the component object, and this would be hard to do consistently.
Having a recursive JSON type seems like a better idea to me, but even then I'd like to see how the type works for real-world code before including it in the PEP. I suspect that the majority of code doing JSON parsing actually doesn't perform enough isinstance checks when manipulating JSON objects for the code to type check cleanly. I wouldn't like PEP 484 to require programmers to jump through hoops to get their code to type check. For example, just today I reviewed some JSON parsing code that does not perform enough checks to pass type checking if it had used a strict type for JSON, but I think that the code was fine (@gvanrossum do you recognize what code I'm talking about?) :-)
Anyway, if programmers want to use such as partial type, they can define the alias and use it in their code even without making it official, though they may have to introduce some explicit type annotations when interacting with library code that doesn't use the type.
Reacted by David Virebayre, Jendrik Stelzner and Václav Brožík
The summary @gvanrossum gave of where things left off was accurate. Didn't come up with a recursive type that could work.
In response to @JukkaL about usefulness, I view it as useful for specifying what json.load() returns, not what json.dump() accepts. This is what I came across in my own code when I was trying to do proper type hinting but didn't have a way better than Any to express an method parameter that was accepting a JSON object that was received from GitHub's API.
To translate @AdrianCert "worked for me" for others: he likely did not use the open-source mypy and probably checking with the closed-source pylance from inside vscode, which already supports recursive types. That does not count for us, even if we may be using it. As long mypy cannot work with it, it is not sorted.
I only hope that since @gvanrossum joined MSFT, he is still allowed to contribute to mypy, even if I do see how that could be seen as a conflict on interest with pylance.
probably checking with the closed-source pylance from inside vscode
I'm also pretty excited about the recent work on recursive types at mypy, but FWIW, pylance uses pyright for its type checking, and pyright is fully open source: https://github.com/microsoft/pyright#faq.
I can still contribute to mypy, I just am pretty rusty with that code base so you won’t hear much from me. Sorry! I also feel this will be sorted soon, it is just difficult to transition a large code base. See python/mypy#731 (comment) .
Reacted by Sorin Sbarnea, Piotr Dąbrowski, Jesus Alvarez, Dima Tisnek, Satyajeet Kanetkar, Benjamin Leff, Lydia and Jude
$ mypy --strict --enable-recursive-aliases test.py
test.py:4: note: Revealed type is "Union[builtins.dict[builtins.str, ...], builtins.list[...], builtins.str, builtins.int, builtins.float, builtins.bool, None]"
Success: no issues found in 1 source file
Reacted by Alexandre Morignot, Martina Ferrari, Anton Agestam, Marcelo Trylesinski, Sigurd Ljødal, Nick Sweeting, Martin Thoma, cesaregarza, Tristan Maat, Casper Weiss Bang and 17 more
If only I could create one at runtime storing self-referential (scoped,
non-string) annotations.
Will mypy eventually enable recursive aliases by default?
On Fri, 2022-10-28 at 16:08 -0700, Kevin Heavey wrote:
For anyone with a strong aversion to copy-pasting, I have published
the above one-liner from @wbolster as a library
https://github.com/kevinheavey/jsonalias
(No this isn't a joke, I need the Json type in multiple projects so I
decided to just publish it)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this
thread.Message ID: ***@***.***>
With the newest versions of Python (3.11.0) and MyPy (0.991), it looks like the one liner proposed by @wbolster works without any additional flags even for complex JSONS.
Note that because dict is invariant, you might run into some issues e.g. with dict[str, str]. That is, you will not be able to pass something with type dict[str, str] to a function that takes a JSON
For such use cases you can use cast, and if you don't need mutability, something like the following might work:
As always, if you return a union type, your callers will have to assert the structure of their JSON in order to type check. If this an issue, return e.g. dict[str, Any] or Any or cast or type-ignore or just bite the bullet and assert.
Given that it is trivial to define these aliases, that as you can see they're not necessarily one-size-fits-all, and that explicit is better than implicit, and that they don't need any type checker special casing, I am not in favour of adding these to CPython's typing.py. However if you like importing stuff, we may ship this and some other useful type definitions in a PyPI package at some point; there also appears to be https://github.com/kevinheavey/jsonalias. If you have opinions about this, please see python/typing_extensions#6.
In order to keep this message visible, I may lock this thread. Please strongly consider posting follow-ups at one of the above locations.
Thank you to everyone who contributed support for recursive type aliases to type checkers — cheers and happy typing!
Reacted by Marcelo Trylesinski, John Ericson, Nick Sweeting, Benjamin Leff, Max Marrone, Gabriel, Pyprohly, Ellis Breen, Jelmer Vernooij, Olúwátóósìn Anímáṣahun and 35 more
Reacted by Guido van Rossum, Greg Werbin, John Ericson, Tuukka Mustonen, Bradley Sherman, Nick Sweeting, laurent pinson, Kamil, Daniel Miranda, Mehdi Drissi and 15 more
Reacted by fcccp, OlegAlexander, Agriya Khetarpal and stevin-illumina
Activity
gvanrossum commentedon Feb 19, 2016
I tried to do that but a recursive type alias doesn't work in mypy right now, and I'm not sure how to make it work. In the mean time I use
JsonDict = Dict[str, Any](which is not very useful but at least clarifies that the keys are strings), and Any for places where a more general JSON type is expected.(I'm sure you meant
t.Mapping[str, 'JSON'].)brettcannon commentedon Feb 19, 2016
You are right about what I meant and I fixed my comment to not confuse anyone in the future.
And I understand about the lack of recursive object support.
Would this be a better definition?
If you read RFC 4627 it says a JSON object must be an object or array at the top level (RFC 7159 loosens that to any JSON value, but it's not an accepted standard). If you want to play it safe with what
json.loads()takes, then you can just flatten it to:I guess the real question is how far you want to take this, because if you assume that most JSON objects only go, e.g. 4 levels deeps, you could handcraft accuracy to that level:
But then again the union of those objects is pretty broad so this might not be the most useful type hint. :)
gvanrossum commentedon Feb 20, 2016
I guess that'll work, but I'm not convinced that it's very useful to
do the multiple levels.
The next question is where this would live? Would you add it to
typing.py? Or to the json module? It would have to be added both to
json.pyi as well to the actual implementation module. :-(
brettcannon commentedon Feb 20, 2016
OK, so
JSONType = t.Union[str, int, float, bool, None, t.Dict[str, t.Any], t.List[t.Any]]seems to be the best solution.As for where it should go, I don't have a good answer unfortunately. It's like the
collections.abcissue; do we have a single place to keep all types -- i.e.,typing-- or do we keep types in the specific modules that they relate to (i.e.,jsonin this case)? I guess this would be the first type that wasn't a generic container if we add it to the stdlib somewhere, so there is no precedent to go by.If we put it in
typingthen at least all types related to the stdlib are in a single location which is handy for only having to doimport typing as tto get at all types. Unfortunately that doesn't work for for third-party libraries so it doesn't seem like the best way to go. So I guess my suggestion is thejsonmodule should house it and keep the nameJSONTypefor the module attribute. If you agree I will open an issue on bugs.python.org to add the type to Python 3.6 and then also an accompanying issue for https://github.com/python/typeshed to add ajson.pyiand you can close this issue. Otherwise I'll submit a PR to add the type totyping.gvanrossum commentedon Feb 21, 2016
I think it's best to add it to the json module; we can't keep adding everything to typing.py (even the io and re types there are already questionable). Code that wants to use these in Python 3.5 or earlier can write
(Or they can copy the definition into their own code.)
Question: should we name it
JsonTypeorJSONType? There doesn't seem to be a strong convention here in the Python stdlib -- we have XmlListener and HTMLParser... But somehow I am beginning to prefer AcronymsAreWords.gvanrossum commentedon Feb 21, 2016
Actually, since the json module consistently uses JSONWhatevs, it should be JSONType.
brettcannon commentedon Feb 21, 2016
PEP 8 says to capitalize abbreviations.
Open an issue for the stdlib at http://bugs.python.org/issue26396 and one for typeshed at python/typeshed#84.
gvanrossum commentedon Mar 18, 2016
I'm marking this for 3.5.2 so we at least have the discussion. I'm still not sure of the solution -- add it to typeshed/.../json.pyi or to typing.py? It can't appear in the stdlib json module until 3.6 but it could appear in typing.py in 3.5.2 (since typing.py is provisional), but I'm not excited about pushing everything to typing.py. So maybe adding it to typeshed/**/json.pyi now and the stdlib json module in 3.6 would be best? If you want to use it then you'd have to write
if false: from json import JsonObject.(I've got a feeling I'm just summarizing where we ended up before but I'm currently in the mood to make the definitive list of things to discuss and/or implement before 3.5.2.)
JukkaL commentedon Mar 18, 2016
I'm not a fan of having a "partial" JSON type that soon degenerates into
Any. Type checkers would enforce things inconsistently. As soon as you descent into a JSON object you'd have to manually annotate the result to get type checking for the component object, and this would be hard to do consistently.Having a recursive JSON type seems like a better idea to me, but even then I'd like to see how the type works for real-world code before including it in the PEP. I suspect that the majority of code doing JSON parsing actually doesn't perform enough
isinstancechecks when manipulating JSON objects for the code to type check cleanly. I wouldn't like PEP 484 to require programmers to jump through hoops to get their code to type check. For example, just today I reviewed some JSON parsing code that does not perform enough checks to pass type checking if it had used a strict type for JSON, but I think that the code was fine (@gvanrossum do you recognize what code I'm talking about?) :-)Anyway, if programmers want to use such as partial type, they can define the alias and use it in their code even without making it official, though they may have to introduce some explicit type annotations when interacting with library code that doesn't use the type.
gvanrossum commentedon Mar 18, 2016
Two problems with adding a recursive JSON type to the PEP:
brettcannon commentedon Mar 18, 2016
The summary @gvanrossum gave of where things left off was accurate. Didn't come up with a recursive type that could work.
In response to @JukkaL about usefulness, I view it as useful for specifying what
json.load()returns, not whatjson.dump()accepts. This is what I came across in my own code when I was trying to do proper type hinting but didn't have a way better thanAnyto express an method parameter that was accepting a JSON object that was received from GitHub's API.gvanrossum commentedon Mar 21, 2016
Somehow this was closed but we don't even have consensus!
72 remaining items
ssbarnea commentedon Aug 26, 2022
To translate @AdrianCert "worked for me" for others: he likely did not use the open-source mypy and probably checking with the closed-source pylance from inside vscode, which already supports recursive types. That does not count for us, even if we may be using it. As long mypy cannot work with it, it is not sorted.
I only hope that since @gvanrossum joined MSFT, he is still allowed to contribute to mypy, even if I do see how that could be seen as a conflict on interest with pylance.
Now by looking at https://github.com/python/mypy/blame/dc5f89142cb36debc0e804a472f49f51448d76f3/test-data/unit/check-recursive-types.test#L7 I think there is something baking there, but there is no release containing it. It should be included in next release. Not sure how well it works but I will be one of the first to check it. I do need to navigate JSON-like structures a lot these days.
AlexWaygood commentedon Aug 26, 2022
I'm also pretty excited about the recent work on recursive types at mypy, but FWIW, pylance uses pyright for its type checking, and pyright is fully open source: https://github.com/microsoft/pyright#faq.
gvanrossum commentedon Aug 26, 2022
I can still contribute to mypy, I just am pretty rusty with that code base so you won’t hear much from me. Sorry! I also feel this will be sorted soon, it is just difficult to transition a large code base. See python/mypy#731 (comment) .
wbolster commentedon Sep 27, 2022
seems that mypy 0.981 has some (preliminary) support for recursive types using the
--enable-recursive-aliasesflag. this recursive type definition:… can be used to type check this code …
… successfully 🥳:
kevinheavey commentedon Oct 28, 2022
For anyone with a strong aversion to copy-pasting, I have published the above one-liner from @wbolster as a library https://github.com/kevinheavey/jsonalias
(No this isn't a joke, I need the
Jsontype in multiple projects so I decided to just publish it)pbryan commentedon Oct 29, 2022
antonagestam commentedon Oct 29, 2022
@pbryan mypy will enable recursive aliases by default in the next release: python/mypy#13516
joooeey commentedon Nov 19, 2022
With the newest versions of Python (
3.11.0) and MyPy (0.991), it looks like the one liner proposed by @wbolster works without any additional flags even for complex JSONS.Examples:
mixed-type object
nested lists and dictionaries of strings with a slice hidden to throw off the type checker
The type check of all examples together discovers the slice I hid in the last example:
I'm not sure what MyPy does when data passes through several functions..
hauntsaninja commentedon Nov 19, 2022
All major type checkers now support recursive type aliases by default, so this should largely work:
Note that because dict is invariant, you might run into some issues e.g. with
dict[str, str]. That is, you will not be able to pass something with typedict[str, str]to a function that takes aJSONFor such use cases you can use
cast, and if you don't need mutability, something like the following might work:Although this can sometimes result in false negatives, for instance, see python/mypy#13786 (comment)
As always, if you return a union type, your callers will have to assert the structure of their JSON in order to type check. If this an issue, return e.g.
dict[str, Any]orAnyor cast or type-ignore or just bite the bullet and assert.Given that it is trivial to define these aliases, that as you can see they're not necessarily one-size-fits-all, and that explicit is better than implicit, and that they don't need any type checker special casing, I am not in favour of adding these to CPython's typing.py. However if you like importing stuff, we may ship this and some other useful type definitions in a PyPI package at some point; there also appears to be https://github.com/kevinheavey/jsonalias. If you have opinions about this, please see python/typing_extensions#6.
In order to keep this message visible, I may lock this thread. Please strongly consider posting follow-ups at one of the above locations.
Thank you to everyone who contributed support for recursive type aliases to type checkers — cheers and happy typing!
Enhance all dict type annotations
Enhance all dict type annotations
refactor: Improve JsonSerializable type to be recursive (#745)
gjson: type public APIs with a JSON type alias