1
1
from oarepo_model_builder .schema import ModelSchema
2
- from oarepo_model_builder .utils .python_name import package_name
3
- from oarepo_model_builder .validation import InvalidModelException
4
2
5
3
6
4
def extract_extended_record (included_data , * , context , ** kwargs ):
@@ -32,70 +30,28 @@ def extract_extended_record(included_data, *, context, **kwargs):
32
30
33
31
def extend_modify_marshmallow (included_data , * , context , ** kwargs ):
34
32
"""
35
- This processor modified marshmallow of the extended object. At first, it puts
36
- marshmallow and ui back to the included data. Then, for the top-level marshmallow & ui.marshmallow
37
- it converts class -> base-classes and adds import for that.
38
- For the properties, it marks them as read=False and write=False and for each object, it marks it as
39
- generate=False - this way, the classes will be reused from the already existing library and not
40
- generated again.
33
+ This processor moves the marshmallow section of the base record to base-class-marshmallow
34
+ and base-class-ui-marshmallow. It also sets the from-base-class flag to True.
41
35
"""
42
36
43
- def remove_marshmallow_from_children (node ):
37
+ def mark_as_from_base_class (node ):
44
38
ret = {** node }
45
39
node_properties = ret .pop ("properties" , None )
46
40
node_items = ret .pop ("items" , None )
47
-
48
- if "marshmallow" in ret :
49
- convert_marshmallow_class_to_base_class (ret ["marshmallow" ])
50
- elif "properties" in ret :
51
- raise InvalidModelException (
52
- f"marshmallow section not in object { node } . "
53
- f"Please pass generated model (records.json5), not the source model."
54
- )
55
-
56
- if "ui" in ret and "marshmallow" in ret ["ui" ]:
57
- convert_marshmallow_class_to_base_class (ret ["ui" ]["marshmallow" ])
58
- elif "properties" in ret :
59
- raise InvalidModelException (
60
- f"ui.marshmallow section not in object { node } . "
61
- f"Please pass generated model (records.json5), not the source model."
62
- )
41
+ ret ["from-base-class" ] = True
63
42
64
43
if node_properties :
65
44
properties = ret .setdefault ("properties" , {})
66
45
for k , v in node_properties .items ():
67
- remove_marshmallow_from_child (v )
68
- v = remove_marshmallow_from_children (v )
46
+ v = mark_as_from_base_class (v )
69
47
properties [k ] = v
70
48
if node_items :
71
- remove_marshmallow_from_child (node .item )
72
- ret ["items" ] = remove_marshmallow_from_children (node .item )
73
- return ret
74
-
75
- def remove_marshmallow_from_child (child ):
76
- # for object/nested, do not set the read & write to False because
77
- # the extending schema might add more properties.
78
- # This will generate unnecessary classes, but these might be dealt
79
- # on later in marshmallow generator
80
- if "properties" not in child and "items" not in child :
81
- marshmallow = child .setdefault ("marshmallow" , {})
82
- marshmallow .update ({"read" : False , "write" : False })
83
-
84
- ui_marshmallow = child .setdefault ("ui" , {}).setdefault ("marshmallow" , {})
85
- ui_marshmallow .update ({"read" : False , "write" : False })
86
-
87
- def convert_marshmallow_class_to_base_class (marshmallow ):
88
- # pop module & package
89
- marshmallow .pop ("module" , None )
90
- marshmallow .pop ("package" , None )
91
-
92
- if "class" not in marshmallow :
93
- return
94
- clz = marshmallow .pop ("class" )
95
- marshmallow .setdefault ("base-classes" , []).insert (0 , clz )
96
- marshmallow .setdefault ("imports" , []).append (
97
- {"import" : package_name (clz ), "alias" : package_name (clz )}
49
+ ret ["items" ] = mark_as_from_base_class (node .item )
50
+ ret ["base-class-marshmallow" ] = ret .pop ("marshmallow" , {})
51
+ ret ["base-class-ui-marshmallow" ] = ret .setdefault ("ui" , {}).pop (
52
+ "marshmallow" , {}
98
53
)
54
+ return ret
99
55
100
56
def as_array (x ):
101
57
if isinstance (x , list ):
@@ -124,7 +80,7 @@ def replace_use_with_extend(data):
124
80
125
81
included_data ["marshmallow" ] = context ["props" ].get ("marshmallow" , {})
126
82
included_data ["ui" ] = context ["props" ].get ("ui" , {})
127
- ret = remove_marshmallow_from_children (included_data )
83
+ ret = mark_as_from_base_class (included_data )
128
84
129
85
for ext in (
130
86
ModelSchema .EXTEND_KEYWORD ,
@@ -136,3 +92,93 @@ def replace_use_with_extend(data):
136
92
137
93
replace_use_with_extend (ret )
138
94
return ret
95
+
96
+
97
+ def post_extend_modify_marshmallow (* , element , ** kwargs ):
98
+ def convert_schema_classes (node ):
99
+ node_properties = node .get ("properties" , None )
100
+ node_items = node .get ("items" , None )
101
+
102
+ was_inherited = "from-base-class" in node
103
+ if not was_inherited :
104
+ return False
105
+
106
+ contains_only_inherited_properties = node .pop ("from-base-class" , False )
107
+ if node_properties :
108
+ for k , v in node_properties .items ():
109
+ prop_contains_only_inherited_properties = convert_schema_classes (v )
110
+ if not prop_contains_only_inherited_properties :
111
+ contains_only_inherited_properties = False
112
+ elif node_items :
113
+ contains_only_inherited_properties = (
114
+ convert_schema_classes (node_items )
115
+ and contains_only_inherited_properties
116
+ )
117
+ base_class_marshmallow = node .pop ("base-class-marshmallow" , {})
118
+ base_class_ui_marshmallow = node .pop ("base-class-ui-marshmallow" , {})
119
+
120
+ def update_marshmallow (new_marshmallow , base_marshmallow ):
121
+ if new_marshmallow .get ("generate" , True ) is False :
122
+ # the class is set to not generate -> if there is a class, do not change it,
123
+ # if not, set it to the base class
124
+ if not new_marshmallow .get ("class" ) and base_marshmallow .get ("class" ):
125
+ new_marshmallow ["class" ] = base_marshmallow ["class" ]
126
+ return
127
+
128
+ if "items" in node :
129
+ # array itself does not have a marshmallow, so no need to modify this
130
+ _update_non_existing (new_marshmallow , base_marshmallow )
131
+ return
132
+
133
+ if "properties" not in node :
134
+ # primitive data type -> set it not to be generated unless the field says otherwise
135
+ if "read" not in new_marshmallow :
136
+ new_marshmallow ["read" ] = False
137
+ if "write" not in new_marshmallow :
138
+ new_marshmallow ["write" ] = False
139
+ for k , v in base_marshmallow .items ():
140
+ if k not in new_marshmallow :
141
+ new_marshmallow [k ] = v
142
+ return
143
+
144
+ # now we have an object to modify - convert to base classes only if there are extra properties
145
+ convert_to_base_classes = (
146
+ node_properties and not contains_only_inherited_properties
147
+ )
148
+
149
+ if "class" in new_marshmallow :
150
+ # someone added class to the new_marshmallow, so we do not want to change it
151
+ convert_to_base_classes = True
152
+
153
+ if convert_to_base_classes :
154
+ if base_marshmallow .get ("class" ):
155
+ new_marshmallow .setdefault ("base-classes" , []).insert (
156
+ 0 , base_marshmallow ["class" ]
157
+ )
158
+ new_marshmallow ["generate" ] = True
159
+
160
+ elif contains_only_inherited_properties :
161
+ # keep the base class marshmallow, but do not generate the class as it has been generated
162
+ # in the extended library
163
+ new_marshmallow .clear ()
164
+ new_marshmallow .update (base_marshmallow )
165
+ new_marshmallow ["generate" ] = False
166
+
167
+ else :
168
+ _update_non_existing (new_marshmallow , base_marshmallow )
169
+
170
+ update_marshmallow (node .setdefault ("marshmallow" , {}), base_class_marshmallow )
171
+ update_marshmallow (
172
+ node .setdefault ("ui" , {}).setdefault ("marshmallow" , {}),
173
+ base_class_ui_marshmallow ,
174
+ )
175
+
176
+ return contains_only_inherited_properties
177
+
178
+ convert_schema_classes (element )
179
+
180
+
181
+ def _update_non_existing (target , source ):
182
+ for k , v in source .items ():
183
+ if k not in target :
184
+ target [k ] = v
0 commit comments