]>
git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/rook/rook-client-python/generate_model_classes.py
2 Generate Python files containing data Python models classes for
3 all properties of the all CRDs in the file
5 **Note**: generate_model_classes.py is independent of Rook or Ceph. It can be used for all
10 pip install -r requirements.txt
11 python generate_model_classes.py <crds.yaml> <output-folder>
12 python setup.py develop
15 generate_model_classes.py <crds.yaml> <output-folder>
18 from abc
import ABC
, abstractmethod
19 from collections
import OrderedDict
20 from typing
import List
, Union
, Iterator
, Optional
24 from dataclasses
import dataclass
26 from attr
import dataclass
# type: ignore
29 This file is automatically generated.
34 from typing import Any, Optional, Union, List
38 from .._helper import _omit, CrdObject, CrdObjectList, CrdClass
42 @dataclass # type: ignore
61 def py_property(self
):
64 def {self.py_name}(self):
65 # type: () -> {self.py_property_return_type}
66 return self._property_impl('{self.py_name}')
68 @{self.py_name}.setter
69 def {self.py_name}(self, new_val):
70 # type: ({self.py_param_type}) -> None
71 self._{self.py_name} = new_val
76 if not self
.has_default
:
77 return f
'{self.py_name}, # type: {self.py_param_type}'
78 return f
'{self.py_name}=_omit, # type: {self.py_param_type}'
81 def has_default(self
):
82 return not self
.required
85 def py_param_type(self
):
86 return f
'Optional[{self.py_type}]' if (self
.nullable
or not self
.required
) else self
.py_type
89 def py_property_return_type(self
):
90 return f
'Optional[{self.py_type}]' if (self
.nullable
) else self
.py_type
93 class CRDAttribute(CRDBase
):
95 default_value
: str='_omit'
99 if not self
.has_default
:
100 return f
'{self.py_name}, # type: {self.py_param_type}'
101 return f
'{self.py_name}={self.default_value}, # type: {self.py_param_type}'
104 def has_default(self
):
105 return not self
.required
or self
.default_value
!= '_omit'
125 class CRDList(CRDBase
):
134 return self
.name
[0].upper() + self
.name
[1:] + 'List'
137 def py_param_type(self
):
138 inner
= f
'Union[List[{self.items.py_type}], CrdObjectList]'
139 return f
'Optional[{inner}]' if (self
.nullable
or not self
.required
) else inner
142 def py_property_return_type(self
):
143 inner
= f
'Union[List[{self.items.py_type}], CrdObjectList]'
144 return f
'Optional[{inner}]' if (self
.nullable
) else inner
147 yield from self
.items
.flatten()
151 py_type
= self
.items
.py_type
156 class {self.py_type}(CrdObjectList):
157 {indent('_items_type = ' + py_type)}
162 class CRDClass(CRDBase
):
163 attrs
: List
[Union
[CRDAttribute
, 'CRDClass']]
164 base_class
: str = 'CrdObject'
167 ps
= '\n\n'.join(a
.py_property() for a
in self
.attrs
)
168 return f
"""class {self.py_type}({self.base_class}):
169 {indent(self.py_properties())}
171 {indent(self.py_init())}
177 def sub_classes(self
) -> List
["CRDClass"]:
178 return [a
for a
in self
.attrs
if isinstance(a
, CRDClass
)]
182 return self
.name
[0].upper() + self
.name
[1:]
184 def py_properties(self
):
186 return ', '.join((f
"'{a.name}'",
188 a
.py_type
.replace('Any', 'object'),
192 attrlist
= ',\n'.join([f
'({a_to_tuple(a)})' for a
in self
.attrs
])
193 return f
"""_properties = [\n{indent(attrlist)}\n]"""
195 def flatten(self
) -> Iterator
['CRDClass']:
196 for sub_cls
in self
.attrs
:
197 yield from sub_cls
.flatten()
201 sorted_attrs
= sorted(self
.attrs
, key
=lambda a
: a
.has_default
)
202 params
= '\n'.join(a
.py_param
for a
in sorted_attrs
)
203 params_set
= '\n'.join(f
'{a.py_name}={a.py_name},' for a
in sorted_attrs
)
206 {indent(params, indent=4+9)}
208 super({self.py_type}, self).__init__(
209 {indent(params_set, indent=8)}
213 def indent(s
, indent
=4):
214 return '\n'.join(' '*indent
+ l
for l
in s
.splitlines())
217 def handle_property(elem_name
, elem
: dict, required
: bool):
218 nullable
= elem
.get('nullable', False)
219 if 'properties' in elem
:
220 ps
= elem
['properties']
221 required_elems
= elem
.get('required', [])
222 sub_props
= [handle_property(k
, v
, k
in required_elems
) for k
, v
in ps
.items()]
223 return CRDClass(elem_name
, nullable
, required
, sub_props
)
224 elif 'items' in elem
:
225 item
= handle_property(elem_name
+ 'Item', elem
['items'], False)
226 return CRDList(elem_name
, nullable
, required
, item
)
228 return CRDAttribute(elem_name
, nullable
, required
, elem
['type'])
230 return CRDAttribute(elem_name
, nullable
, required
, 'object')
231 assert False, str((elem_name
, elem
))
234 def handle_crd(c_dict
) -> Optional
[CRDClass
]:
236 name
= c_dict
['spec']['names']['kind']
237 s
= c_dict
['spec']['validation']['openAPIV3Schema']
238 except (KeyError, TypeError):
240 s
['required'] = ['spec']
241 c
= handle_property(name
, s
, True)
242 k8s_attrs
= [CRDAttribute('apiVersion', False, True, 'string'),
243 CRDAttribute('metadata', False, True, 'object'),
244 CRDAttribute('status', False, False, 'object')]
245 return CRDClass(c
.name
, False, True, k8s_attrs
+ c
.attrs
, base_class
='CrdClass')
248 def local(yaml_filename
):
249 with
open(yaml_filename
) as f
:
250 yamls
= yaml
.safe_load_all(f
.read())
254 except AttributeError:
258 def remove_duplicates(items
):
259 return OrderedDict
.fromkeys(items
).keys()
262 def get_toplevels(crd
):
263 elems
= list(crd
.flatten())
266 ds
= set([x
for x
in l
if l
.count(x
) > 1])
269 names
= [t
.name
for t
in elems
]
270 for dup_name
in dup_elems(names
):
271 dups
= set(e
.toplevel() for e
in elems
if e
.name
== dup_name
)
272 assert len(dups
) == 1, str(dups
)
274 return remove_duplicates(cls
.toplevel() for cls
in elems
)
277 def main(yaml_filename
, outfolder
):
278 for crd
in local(yaml_filename
):
279 valid_crd
= handle_crd(crd
)
280 if valid_crd
is not None:
283 except FileExistsError
:
285 open(f
'{outfolder}/__init__.py', 'w').close()
287 with
open(f
'{outfolder}/{valid_crd.name.lower()}.py', 'w') as f
:
289 classes
= get_toplevels(valid_crd
)
290 f
.write('\n\n\n'.join(classes
))
294 if __name__
== '__main__':
295 from docopt
import docopt
296 args
= docopt(__doc__
)
297 yaml_filename
= '/dev/stdin' if args
["<crds.yaml>"] == '-' else args
["<crds.yaml>"]
298 main(yaml_filename
, args
["<output-folder>"])