I am labelling this as a bug because I view missing / unclear documentation as such.
Attached is a script that can assist others in the future - my suggestion is that it could be added to the repository.
"""Dump every dsf-python ObjectModel property path as snake_case (Python) alongside camelCase (JSON).
Walks the model classes, not a live printer, so empty collections (axes, heaters, ...) are still listed.
Usage: python om_reference.py [output_file] (default: om_reference.txt)
How it works:
-No printer needed. The script reads the dsf-python model classes directly. For lists like axes and heaters, it builds one blank item to document its fields, so they show up even when empty. [n] stands for a list index and [key] for a dictionary key.
-Only public properties are listed. Those are the names you use with getattr.
-NoneType in the type column means the field defaults to None until the printer fills it in (usually an int, float or string). Real types appear once you're connected to a printer.
-Only the default version of some objects is shown. For example, move.kinematics is listed with its default class's fields. A different kinematics type on the printer may have extra fields.
-One odd name: JSON global is globals in Python, because global is a reserved word. The script accounts for this.
"""
import sys
from enum import Enum
from dsf.object_model import ObjectModel
from dsf.object_model.model_object import ModelObject
from dsf.object_model.model_collection import ModelCollection
from dsf.object_model.model_dictionary import ModelDictionary
from dsf.utils import snake_to_camel
def type_name(value):
if isinstance(value, Enum):
return f"{type(value).__name__} (enum: {', '.join(m.name for m in type(value))})"
return type(value).__name__
def new_item(constructor):
"""Create a default instance of a collection/dictionary item type, or None if not possible"""
if not isinstance(constructor, type) or constructor is object:
return None
try:
return constructor()
except Exception:
return None
def walk(obj, py_path, json_path, rows, seen):
if isinstance(obj, ModelObject):
if type(obj) in seen: # guard against recursive types
return
seen = seen | {type(obj)}
for name in sorted(dir(type(obj))):
if name.startswith('_') or not isinstance(getattr(type(obj), name), property):
continue
json_name = 'global' if name == 'globals' else snake_to_camel(name)
try:
value = getattr(obj, name)
except Exception:
continue
p, j = f"{py_path}.{name}", f"{json_path}.{json_name}"
rows.append((p, j, type_name(value)))
walk(value, p, j, rows, seen)
elif isinstance(obj, ModelCollection):
item = obj[0] if len(obj) else new_item(getattr(obj, '_item_constructor', None))
if item is not None:
walk(item, f"{py_path}[n]", f"{json_path}[n]", rows, seen)
elif isinstance(obj, ModelDictionary):
item = new_item(getattr(obj, '_item_constructor', None))
if item is not None:
walk(item, f"{py_path}[key]", f"{json_path}[key]", rows, seen)
def main():
out_file = sys.argv[1] if len(sys.argv) > 1 else 'om_reference.txt'
rows = []
walk(ObjectModel(), 'object_model', 'om', rows, frozenset())
w1 = max(len(r[0]) for r in rows)
w2 = max(len(r[1]) for r in rows)
with open(out_file, 'w') as f:
f.write(f"{'Python (snake_case)':{w1}} {'JSON (camelCase)':{w2}} Type\n")
f.write(f"{'-' * w1} {'-' * w2} ----\n")
for p, j, t in rows:
f.write(f"{p:{w1}} {j:{w2}} {t}\n")
print(f"Wrote {len(rows)} properties to {out_file}")
if __name__ == '__main__':
main()
Which Duet products are you using?
Firmware Version
3.7
Duet Web Control Version
3.7
Are you using a Single Board Computer (RaspberryPi) with your Duet?
Please upload the results of sending M122 in the gcode console.
Please upload the content of your config.g file.
Please upload the content of any other releveant macro files.
Details specific to your printer.
Links to additional info.
No response
What happened?
I am labelling this as a bug because I view missing / unclear documentation as such.
Attached is a script that can assist others in the future - my suggestion is that it could be added to the repository.