strictyaml
strictyaml copied to clipboard
Support for type hints
Feature request
Does strictyaml work well with type hints? I can't find any documentation for this. I would like to have this functionality so I won't need to define my types twice.
No, not yet, but I'm open to the idea. Can you share some example code that demonstrates the problem?
My use case is this:
from strictyaml import load as load_yaml, Map
schema = MAP({ ... })
class SomeClass():
config: schema # I want this part
def __init__(self, config: schema):
self.config = config
@static_method
def from_yaml(yaml_str: str):
return SomeClass(load_yaml(yaml_str, schema))
if __name__ == "__main":
...
obj = SomeClass.from_yaml(yaml_str)
Does the above look possible?
An example the other way around: the program wants a list of TypedDict from a yaml,
strictyaml.load(yaml, List[Movie])
would auto-generate a schema.
Based on the PEP 589 (TypeDict) Movies snippet.
from typing import cast, List, Optional, TypedDict
import strictyaml
from strictyaml import Int, Map, Seq, Str
class Location(TypedDict):
country: str
SCHEMA_LOCATION = Map(
{
"country": Str()
}
)
class Movie(TypedDict):
name: str
year: int
locations: List[Location]
comments: Optional[List[str]]
SCHEMA_MOVIE = Map(
{
"name": Str(),
"year": Int(),
"locations": Seq(SCHEMA_LOCATION),
strictyaml.Optional("comments", default=None): Seq(Str()),
}
)
def movies(yaml: str) -> List[Movie]:
# current, working code:
data = strictyaml.load(yaml, schema=Seq(SCHEMA_MOVIE)).data
return cast(List[Movie], data)
# ideally it would be:
return strictyaml.load(yaml, hint=List[Movie])
These are minor issues, but it would be nice if I could avoid
- type information duplication
-
cast()
- looking up the docs for the
strictyaml
equivalents oftyping
'sUnion
, etc
strictyaml.Optional
is qualified above, for the collision with typing.Optional
.
(Edit) For runtime introspection of List
and Dict
, Python >= 3.8 has typing.get_origin
and typing.get_args
, which were added in Python 3.8. For Python < 3.8, there are snippets in typing_inspect issue #37.
For runtime introspection of TypedDict
, the typing
docs says that the type info is in __annotations__
and __total__
(totality is defined in the PEP).
In this example, load()
would detect a List
[1] of something that has __annotations__
[2].
[1] List.__origin__ == list
should work, according to the typing
module source code, but maybe a better way is documented somewhere.
[2] per PEP 3107 § Accessing Function Annotations
For completeness and myself: the rationale for the cast above is that .data
contains just other dicts, lists and strings/ints/etc
, as opposed to custom containers, and TypedDict
is essentially a constructor and type hint for a plain dict
(PEP: the runtime type of a TypedDict object will always be just dict (it is never a subclass of dict)
).
(edit) Strictly speaking, that cast is incorrect with the current strictyaml
implementation, that makes List[OrderedDict]
(#80). In fact, PEP 484 section "Covariance and contravariance" lets a client of List[Dict]
rely on type()
being exactly dict
, as opposed to a subclass. A workaround might be (#80) to return dict
for load(..., List[Dict])
and OrderedDict
for load(..., Seq(Map()))
.
👀