Let me start off by saying I wanted to open an issue in pydantic
repo. Once I started rubber duck debugging I came up to the conclusion it’s actually pyyaml
that isn’t working right but I’m not so sure anymore.
JavaScript
x
67
67
1
from dataclasses import dataclass
2
from functools import partial
3
from typing import List, Type
4
5
import yaml
6
from pydantic import BaseModel
7
8
yaml_input = """
9
!Foo
10
name: foo
11
bar:
12
- !Bar
13
name: bar
14
"""
15
16
17
def get_loader():
18
loader = yaml.SafeLoader
19
for tag_name, tag_constructor in tag_model_map.items():
20
loader.add_constructor(tag_name, tag_constructor)
21
return loader
22
23
24
def dynamic_constructor_mapping(model_class: Type[BaseModel], loader: yaml.SafeLoader,
25
node: yaml.nodes.MappingNode) -> BaseModel:
26
return model_class(**loader.construct_mapping(node))
27
28
29
def get_constructor_for_mapping(model_class: Type[BaseModel]):
30
return partial(dynamic_constructor_mapping, model_class)
31
32
33
class Bar(BaseModel):
34
name: str
35
36
37
class Foo1(BaseModel):
38
name: str
39
bar: list
40
41
42
class Foo2(BaseModel):
43
name: str
44
bar: List
45
46
47
class Foo3(BaseModel):
48
name: str
49
bar: List[Bar]
50
51
52
@dataclass
53
class Foo4:
54
name: str
55
bar: List[Bar]
56
57
58
foos = [Foo1, Foo2, Foo3, Foo4]
59
60
for foo_cls in foos:
61
tag_model_map = {
62
"!Foo": get_constructor_for_mapping(foo_cls),
63
"!Bar": get_constructor_for_mapping(Bar),
64
}
65
print(f"{foo_cls.__qualname__} loaded {yaml.load(yaml_input, Loader=get_loader())}")
66
67
which prints
JavaScript
1
5
1
Foo1 loaded name='foo' bar=[Bar(name='bar')]
2
Foo2 loaded name='foo' bar=[]
3
Foo3 loaded name='foo' bar=[]
4
Foo4 loaded Foo4(name='foo', bar=[Bar(name='bar')])
5
- list of
pydantic
objects is parsed correctly iflist
is used in static typing - list of
pydantic
objects is NOT parsed correctly ifList
is used in static typing - list of
pydantic
objects is NOT parsed correctly ifList[Bar]
is used in static typing - list of
dataclass
objects is always parsed correctly
The constructor seems to be returning the correct object in all examples so I don’t understand where the problem lies.
JavaScript
1
3
1
pydantic==1.8.2
2
Python 3.8.10
3
Advertisement
Answer
I had the same exact issue as you.
The thing that solved the issue for me was setting the deep
to true in the construct_mapping method.
Example:
JavaScript
1
2
1
fields = loader.construct_mapping(node, deep=True)
2