Skip to content

Commit d2a4010

Browse files
committed
Make settings generator script accept custom XML path
1 parent 7475378 commit d2a4010

File tree

1 file changed

+177
-153
lines changed

1 file changed

+177
-153
lines changed

tools/generate-settings-dataclasses.py

Lines changed: 177 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/usr/bin/env python
22
# SPDX-License-Identifier: LGPL-2.1-or-later
33
import collections
4-
from typing import Any, Dict, List, OrderedDict, Optional, Tuple
5-
import xml.etree.ElementTree as ElementTree
64
import textwrap
5+
import xml.etree.ElementTree as ElementTree
6+
from argparse import ArgumentParser
7+
from pathlib import Path
8+
from typing import Any, Dict, List, Optional, OrderedDict, Tuple
79

810

911
def dbg(msg: Any) -> None:
@@ -183,158 +185,180 @@ def find_first_not_none(itr: List[Any]) -> Optional[Any]:
183185
return next((i for i in itr if i is not None), None)
184186

185187

186-
###############################################################################
187-
gl_input_files = ["man/nm-settings-docs-dbus.xml"]
188-
189-
xml_roots = [ElementTree.parse(f).getroot() for f in gl_input_files]
190-
assert all(root.tag == "nm-setting-docs" for root in xml_roots)
191-
settings_roots = [node_to_dict(root, "setting", "name") for root in xml_roots]
192-
193-
root_node = ElementTree.Element("nm-setting-docs")
194-
# print("")
195-
license = "SPDX-License-Identifier: LGPL-2.1-or-later"
196-
script = "This file was generated by tools/generate-settings-dataclasses.py"
197-
header = f"""# {license}\n# {script},
198-
# if possible, please make changes by also updating the script.
199-
"""
200-
i = open("sdbus_async/networkmanager/settings/__init__.py", mode="w")
201-
p = open("sdbus_async/networkmanager/settings/profile.py", mode="r")
202-
profile_py = open("sdbus_async/networkmanager/settings/profile.py").read()
203-
start_string = "# start of the generated list of settings classes\n"
204-
start_index = profile_py.index(start_string) + len(start_string)
205-
# end_string = " def to_dbus"
206-
end_string = " # end of the generated list of settings classes\n"
207-
end_index = profile_py.index(end_string)
208-
p = open("sdbus_async/networkmanager/settings/profile.py", mode="w")
209-
i.write(header)
210-
p.write(profile_py[:start_index])
211-
classes = []
212-
for settingname in iter_keys_of_dicts(settings_roots, key_fcn_setting_name):
213-
settings = [d.get(settingname) for d in settings_roots]
214-
properties = [node_to_dict(s, "property", "name") for s in settings]
215-
if properties == [OrderedDict()]:
216-
continue
217-
if settingname in ["tc", "sriov"]:
218-
continue # Not supported by this codegen yet(needs Qdiscs and vfs)
219-
module = settingname.replace('-', '_')
220-
for prefix in ["6", "802_11_", "802_3_"]:
221-
if module.startswith(prefix):
222-
module = module.replace(prefix, "")
223-
break
224-
if settingname.startswith("802-1x"):
225-
module = f"ieee{module}"
226-
temp = module.split('_') + ["Settings"]
227-
classname = temp[0].title() + ''.join(ele.title() for ele in temp[1:])
228-
if classname.startswith("Ieee"):
229-
classname.replace("Ieee", "IEEE")
230-
i.write(f"from .{module} import {classname}\n")
231-
classes.append(classname)
232-
f = open(f"sdbus_async/networkmanager/settings/{module}.py", mode="w")
233-
f.write(header)
234-
f.write("from __future__ import annotations\n")
235-
f.write("from dataclasses import dataclass, field\n")
236-
f.write("from typing import")
237-
if module in ["bond", "ethernet", "ovs_external_ids", "vpn", "user"]:
238-
f.write(" Dict,")
239-
if module in list_modules:
240-
f.write(" List,")
241-
f.write(" Optional\n")
242-
f.write("from .base import NetworkManagerSettingsMixin\n")
243-
if settingname.startswith("ipv"):
244-
f.write("from .datatypes import AddressData, RouteData\n")
245-
if settingname.startswith("bridge"):
246-
f.write("from .datatypes import Vlans\n")
247-
if settingname.startswith("team"):
248-
f.write("from .datatypes import LinkWatchers\n")
249-
if settingname == "wireguard":
250-
f.write("from .datatypes import WireguardPeers as Peers\n")
251-
f.write("\n\n")
252-
253-
setting_node = ElementTree.SubElement(root_node, "setting")
254-
if module != "connection":
255-
p.write(f" {module}: Optional[{classname}] = field(\n")
256-
p.write(f" metadata={{'dbus_name': '{settingname}',\n")
257-
p.write(f" 'settings_class': {classname}}},\n")
258-
p.write(" default=None,\n")
259-
p.write(" )\n")
260-
f.write("@dataclass\n")
261-
f.write(f"class {classname}(NetworkManagerSettingsMixin):\n")
262-
setting_node.set("name", settingname)
263-
desc = node_get_attr(settings, "description")
264-
f.write(' """' + desc + '"""\n\n')
265-
# node_set_attr(setting_node, "alias", settings)
266-
for property_name in iter_keys_of_dicts(properties):
267-
properties_attrs = [p.get(property_name) for p in properties]
268-
property_node = ElementTree.SubElement(setting_node, "property")
269-
property_node.set("name", property_name)
270-
t = node_get_attr(properties_attrs, "type")
271-
attribute = property_name.replace('-', '_')
272-
for builtin in ["id", "type"]:
273-
if attribute == builtin:
274-
attribute = f"{module}_{attribute}"
275-
276-
if not t:
277-
t = "string"
278-
if t.startswith("array of legacy"):
188+
def main(settings_xml_path: Path) -> None:
189+
gl_input_files = [settings_xml_path]
190+
191+
xml_roots = [ElementTree.parse(f).getroot() for f in gl_input_files]
192+
assert all(root.tag == "nm-setting-docs" for root in xml_roots)
193+
settings_roots = [node_to_dict(root, "setting", "name")
194+
for root in xml_roots]
195+
196+
root_node = ElementTree.Element("nm-setting-docs")
197+
# print("")
198+
license = "SPDX-License-Identifier: LGPL-2.1-or-later"
199+
script = ("This file was generated by "
200+
"tools/generate-settings-dataclasses.py")
201+
header = f"""# {license}\n# {script},
202+
# if possible, please make changes by also updating the script.
203+
"""
204+
i = open("sdbus_async/networkmanager/settings/__init__.py", mode="w")
205+
p = open("sdbus_async/networkmanager/settings/profile.py", mode="r")
206+
profile_py = open("sdbus_async/networkmanager/settings/profile.py").read()
207+
start_string = "# start of the generated list of settings classes\n"
208+
start_index = profile_py.index(start_string) + len(start_string)
209+
# end_string = " def to_dbus"
210+
end_string = " # end of the generated list of settings classes\n"
211+
end_index = profile_py.index(end_string)
212+
p = open("sdbus_async/networkmanager/settings/profile.py", mode="w")
213+
i.write(header)
214+
p.write(profile_py[:start_index])
215+
classes = []
216+
for settingname in iter_keys_of_dicts(settings_roots,
217+
key_fcn_setting_name):
218+
settings = [d.get(settingname) for d in settings_roots]
219+
properties = [node_to_dict(s, "property", "name") for s in settings]
220+
if properties == [OrderedDict()]:
279221
continue
280-
if t not in dbus_name_type_map:
281-
print(f"{settingname}.{property_name}: type '{t}' not found")
282-
ty = t.replace(")", "")[-5:]
283-
if ty in dbus_name_type_map:
284-
t = ty
285-
if t in ["{sv}'"]:
286-
t = "{sv}"
287-
dbustype = dbus_name_type_map[t]
288-
if dbustype == "aa{sv}":
289-
default = node_get_attr(properties_attrs, "default")
290-
inner_cls = (
291-
property_name.title().replace("-", "").replace("data", "Data")
292-
)
293-
f.write(f" {attribute}: Optional[List[{inner_cls}]] = field(\n")
294-
f.write(f" metadata={{'dbus_name': '{property_name}',\n")
295-
f.write(f" 'dbus_type': '{dbustype}',\n")
296-
f.write(f" 'dbus_inner_class': {inner_cls}}},\n")
297-
f.write(f" default={str(default).title()},\n )\n")
298-
else:
299-
attribute_type = dbus_type_name_map[dbustype]
300-
optional = module != "bond"
301-
if optional:
302-
f.write(f" {attribute}: Optional[{attribute_type}]")
222+
if settingname in ["tc", "sriov"]:
223+
continue # Not supported by this codegen yet(needs Qdiscs and vfs)
224+
module = settingname.replace('-', '_')
225+
for prefix in ["6", "802_11_", "802_3_"]:
226+
if module.startswith(prefix):
227+
module = module.replace(prefix, "")
228+
break
229+
if settingname.startswith("802-1x"):
230+
module = f"ieee{module}"
231+
temp = module.split('_') + ["Settings"]
232+
classname = temp[0].title() + ''.join(ele.title() for ele in temp[1:])
233+
if classname.startswith("Ieee"):
234+
classname.replace("Ieee", "IEEE")
235+
i.write(f"from .{module} import {classname}\n")
236+
classes.append(classname)
237+
f = open(f"sdbus_async/networkmanager/settings/{module}.py", mode="w")
238+
f.write(header)
239+
f.write("from __future__ import annotations\n")
240+
f.write("from dataclasses import dataclass, field\n")
241+
f.write("from typing import")
242+
if module in ["bond", "ethernet", "ovs_external_ids", "vpn", "user"]:
243+
f.write(" Dict,")
244+
if module in list_modules:
245+
f.write(" List,")
246+
f.write(" Optional\n")
247+
f.write("from .base import NetworkManagerSettingsMixin\n")
248+
if settingname.startswith("ipv"):
249+
f.write("from .datatypes import AddressData, RouteData\n")
250+
if settingname.startswith("bridge"):
251+
f.write("from .datatypes import Vlans\n")
252+
if settingname.startswith("team"):
253+
f.write("from .datatypes import LinkWatchers\n")
254+
if settingname == "wireguard":
255+
f.write("from .datatypes import WireguardPeers as Peers\n")
256+
f.write("\n\n")
257+
258+
setting_node = ElementTree.SubElement(root_node, "setting")
259+
if module != "connection":
260+
p.write(f" {module}: Optional[{classname}] = field(\n")
261+
p.write(f" metadata={{'dbus_name': '{settingname}',\n")
262+
p.write(f" 'settings_class': {classname}}},\n")
263+
p.write(" default=None,\n")
264+
p.write(" )\n")
265+
f.write("@dataclass\n")
266+
f.write(f"class {classname}(NetworkManagerSettingsMixin):\n")
267+
setting_node.set("name", settingname)
268+
desc = node_get_attr(settings, "description")
269+
f.write(' """' + desc + '"""\n\n')
270+
# node_set_attr(setting_node, "alias", settings)
271+
for property_name in iter_keys_of_dicts(properties):
272+
properties_attrs = [p.get(property_name) for p in properties]
273+
property_node = ElementTree.SubElement(setting_node, "property")
274+
property_node.set("name", property_name)
275+
t = node_get_attr(properties_attrs, "type")
276+
attribute = property_name.replace('-', '_')
277+
for builtin in ["id", "type"]:
278+
if attribute == builtin:
279+
attribute = f"{module}_{attribute}"
280+
281+
if not t:
282+
t = "string"
283+
if t.startswith("array of legacy"):
284+
continue
285+
if t not in dbus_name_type_map:
286+
print(f"{settingname}.{property_name}: type '{t}' not found")
287+
ty = t.replace(")", "")[-5:]
288+
if ty in dbus_name_type_map:
289+
t = ty
290+
if t in ["{sv}'"]:
291+
t = "{sv}"
292+
dbustype = dbus_name_type_map[t]
293+
if dbustype == "aa{sv}":
294+
default = node_get_attr(properties_attrs, "default")
295+
inner_cls = (
296+
property_name.title(
297+
).replace("-", "").replace("data", "Data")
298+
)
299+
f.write(
300+
f" {attribute}: Optional[List[{inner_cls}]] = field(\n")
301+
f.write(
302+
f" metadata={{'dbus_name': '{property_name}',\n")
303+
f.write(f" 'dbus_type': '{dbustype}',\n")
304+
f.write(
305+
f" 'dbus_inner_class': {inner_cls}}},\n")
306+
f.write(f" default={str(default).title()},\n )\n")
303307
else:
304-
f.write(f" {attribute}: {attribute_type}")
305-
f.write(" = field(\n")
306-
meta = f"'dbus_name': '{property_name}', 'dbus_type':@'{dbustype}'"
307-
line = "metadata={" + meta + "},"
308-
wrapper = textwrap.TextWrapper(
309-
width=80,
310-
initial_indent=" ",
311-
subsequent_indent=" ",
312-
)
313-
lines = wrapper.wrap(text=line)
314-
for line in lines:
315-
f.write(line.replace(":@", ": ") + '\n')
316-
default = node_get_attr(properties_attrs, "default")
317-
if default in ["{}", "0", "-1"]:
318-
default = "None"
319-
if optional:
320-
f.write(f" default={str(default).title()},\n")
321-
f.write(" )\n")
322-
generate_descriptions_for_attributes = False
323-
if generate_descriptions_for_attributes:
324-
desc = node_get_attr(properties_attrs, "description")
308+
attribute_type = dbus_type_name_map[dbustype]
309+
optional = module != "bond"
310+
if optional:
311+
f.write(f" {attribute}: Optional[{attribute_type}]")
312+
else:
313+
f.write(f" {attribute}: {attribute_type}")
314+
f.write(" = field(\n")
315+
meta = (f"'dbus_name': '{property_name}', "
316+
f"'dbus_type':@'{dbustype}'")
317+
line = "metadata={" + meta + "},"
325318
wrapper = textwrap.TextWrapper(
326-
width=74, initial_indent=" ", subsequent_indent=" "
319+
width=80,
320+
initial_indent=" ",
321+
subsequent_indent=" ",
327322
)
328-
lines = wrapper.wrap(text=f'"""{desc}')
329-
if len(lines) == 1:
330-
print(lines[0] + '"""')
331-
else:
332-
for line in lines:
333-
f.write(line)
334-
f.write(' """')
335-
f.write("")
336-
i.write('\n__all__ = (\n')
337-
for cls in classes:
338-
i.write(f" '{cls}',\n")
339-
i.write(")\n")
340-
p.write(profile_py[end_index:])
323+
lines = wrapper.wrap(text=line)
324+
for line in lines:
325+
f.write(line.replace(":@", ": ") + '\n')
326+
default = node_get_attr(properties_attrs, "default")
327+
if default in ["{}", "0", "-1"]:
328+
default = "None"
329+
if optional:
330+
f.write(f" default={str(default).title()},\n")
331+
f.write(" )\n")
332+
generate_descriptions_for_attributes = False
333+
if generate_descriptions_for_attributes:
334+
desc = node_get_attr(properties_attrs, "description")
335+
wrapper = textwrap.TextWrapper(
336+
width=74,
337+
initial_indent=" ",
338+
subsequent_indent=" ",
339+
)
340+
lines = wrapper.wrap(text=f'"""{desc}')
341+
if len(lines) == 1:
342+
print(lines[0] + '"""')
343+
else:
344+
for line in lines:
345+
f.write(line)
346+
f.write(' """')
347+
f.write("")
348+
i.write('\n__all__ = (\n')
349+
for cls in classes:
350+
i.write(f" '{cls}',\n")
351+
i.write(")\n")
352+
p.write(profile_py[end_index:])
353+
354+
355+
if __name__ == '__main__':
356+
arg_parser = ArgumentParser()
357+
arg_parser.add_argument(
358+
'nm_settings_xml',
359+
type=Path,
360+
default=Path('man/nm-settings-docs-dbus.xml'),
361+
)
362+
args = arg_parser.parse_args()
363+
364+
main(args.nm_settings_xml)

0 commit comments

Comments
 (0)