Yeah i dont know how much more there is to say about this.
allows you to lookup objects and look at them, automatically calls methods starting with ‘get_’ that only take self or eventtime… dunno if that will ever cause issues, if it does, feel free to remove it.
Its not typescript. i just choose typescript because every other highlighting was broken.
How to use?
INSPECT_OBJECT O=<your_object>
(toolhead for example)
[gcode_macro INSPECT_OBJECT]
description: "Usage: INSPECT_OBJECT O=<name> [F=<filter>] [DUNDER=1]"
variable_color_attr: "rgb(150, 220, 255)"
variable_color_method: "rgb(130, 255, 150)"
variable_color_private_method: "rgb(180, 220, 180)"
variable_color_getter: "rgb(255, 200, 100)"
variable_color_command: "rgb(255, 160, 122)"
variable_color_val: "rgb(240, 240, 240)"
variable_color_type: "rgb(160, 160, 160)"
variable_color_doc: "rgb(140, 160, 140)"
variable_color_klipper_obj: "rgba(255, 130, 80, 0.6)"
variable_color_container: "rgb(210, 180, 255)"
variable_color_arg_self: "rgb(255, 110, 180)"
variable_color_arg_other: "rgb(137, 207, 240)"
variable_val_cutoff: 200
variable_font_size: "0.8em"
gcode:
{%- set p = printer.printer -%}
{%- set obj_name = params.O|default(params.OBJECT|default(None)) -%}
{%- if obj_name is none -%} {action_raise_error("Usage: INSPECT_OBJECT O=<name>")} {%- endif -%}
# find the closest match if it didnt exactly match
{%- set obj = p.lookup_object(obj_name, None) -%}
{%- if obj is none -%}
{%- set ns = namespace(best_match_obj=None, best_match_name=None) -%}
{%- set search_lower = obj_name|lower -%}
{%- for candidate_name, candidate_obj in p.lookup_objects() if search_lower in candidate_name|lower -%}
{%- if ns.best_match_name is none or candidate_name|length < ns.best_match_name|length -%}
{%- set ns.best_match_obj = candidate_obj -%}
{%- set ns.best_match_name = candidate_name -%}
{%- endif -%}
{%- endfor -%}
{%- if ns.best_match_obj is not none -%}
{action_respond_info("Object '" ~ obj_name|e ~ "' not found. Using best match: '" ~ ns.best_match_name|e ~ "'")}
{%- set obj = ns.best_match_obj -%}
{%- set obj_name = ns.best_match_name -%}
{%- else -%}
{action_respond_info("Object '" ~ obj_name|e ~ "' not found. No partial matches found either.")}
{%- set obj = {} -%}
{%- endif -%}
{%- endif -%}
#-------------------------------------------------------------------------------------------------------------------------------------
# just small helpers
{%- macro _span(color, txt) -%}
{"<span style='color:" ~ color ~ "'>" ~ txt|e ~ "</span>"}
{%- endmacro -%}
{%- macro _details(summary, content, is_open=False) -%}
{"<details" ~ (" open" if is_open else "") ~ "><summary style='cursor:pointer;'>" ~ summary ~ "</summary><div style='padding-left: 20px; border-left: 1px solid rgb(51, 51, 51);'>" ~ content ~ "</div></details>"}
{%- endmacro -%}
# render values
{%- macro _render_value(val, as_string=False) -%}
{%- set type_name = val.__class__.__name__ -%}
{%- set val_str_raw = val|string -%}
{%- if as_string -%}
{%- if val is none -%}None
{%- elif val is sameas True or val is sameas False -%}{val|string}
{%- elif val is mapping -%}{"{...}"}
{%- elif val is sequence and val is not string -%}{"[...]" }
{%- elif '<' in val_str_raw and 'object at 0x' in val_str_raw -%}{"<" ~ type_name ~ ">"}
{%- else -%}{ "'" ~ val_str_raw|truncate(30) ~ "'" }
{%- endif -%}
{%- else -%}
{%- if val is none -%} {_span(color_val, 'None') ~ " " ~ _span(color_type, '(NoneType)')}
{%- elif val is sameas True or val is sameas False -%} {_span(color_val, val|string) ~ " " ~ _span(color_type, '(bool)')}
{%- elif val is mapping -%}
{%- set summary = _span(color_container, "(dict, " ~ val|length ~ " items)") -%}
{%- set items = [] -%}
{%- for k, v in val.items() -%}{%- set _ = items.append("<div style='display: flex; align-items: baseline;'>" ~ _span(color_attr, k|string ~ ": ") ~ _render_value(v) ~ "</div>") -%}{%- endfor -%}
{_details(summary, items|join(''))}
{%- elif val is sequence and val is not string -%}
{%- set summary = _span(color_container, "(list, " ~ val|length ~ " items)") -%}
{%- set items = [] -%}
{%- for item in val -%}{%- set _ = items.append("<div style='display: flex; align-items: baseline;'>" ~ _span(color_type, "[" ~ loop.index0 ~ "]: ") ~ _render_value(item) ~ "</div>") -%}{%- endfor -%}
{_details(summary, items|join(''))}
{%- elif '<' in val_str_raw and 'object at 0x' in val_str_raw -%}{ _span(color_klipper_obj, "<" ~ type_name ~ ">") }
{%- else -%}
{%- set val_str = val_str_raw -%}
{%- if val_str|length > val_cutoff -%}{%- set val_str = val_str[:val_cutoff] ~ '…' -%}{%- endif -%}
{ _span(color_val, "'" ~ val_str ~ "'") ~ " " ~ _span(color_type, "(" ~ type_name ~ ")") }
{%- endif -%}
{%- endif -%}
{%- endmacro -%}
# render method/function + signature
{%- macro _render_callable(callable_obj, name, color, override_doc=None) -%}
{%- if callable_obj.__code__.co_varnames is defined -%}
{%- set signature_html = [] -%}
{%- set code = callable_obj.__code__ -%}
{%- set defaults = callable_obj.__defaults__ or () -%}
{%- set arg_names = code.co_varnames[:code.co_argcount] -%}
{%- set first_default_idx = arg_names|length - defaults|length -%}
{%- for i in range(arg_names|length) -%}
{%- set arg_name = arg_names[i] -%}
{%- set arg_color = color_arg_self if arg_name == 'self' else color_arg_other -%}
{%- if i >= first_default_idx -%}
{%- set default_val_str = _render_value(defaults[i - first_default_idx], as_string=True) -%}
{%- set _ = signature_html.append(_span(arg_color, arg_name) ~ "=" ~ _span(color_val, default_val_str)) -%}
{%- else -%}
{%- set _ = signature_html.append(_span(arg_color, arg_name)) -%}
{%- endif -%}
{%- endfor -%}
{%- set doc = override_doc or callable_obj.__doc__|string|trim -%}
{%- set full_signature = _span(color, name ~ "(") ~ signature_html|join(', ') ~ _span(color, ")") -%}
{%- if doc and doc != 'None' -%}
{%- set doc_html = _span(color_doc, " // " ~ doc|replace('\n', ' ')|truncate(100)) -%}
{full_signature ~ doc_html}
{%- else -%}
{full_signature}
{%- endif -%}
{%- else -%}
{%- set type_name = callable_obj.__class__.__name__ -%}
{_span(color, name ~ "(i dunno)") ~ _span(color_type, "<" ~ type_name ~ ">")}
{%- endif -%}
{%- endmacro -%}
#-------------------------------------------------------------------------------------------------------------------------------------
{%- set attributes, public_methods, private_methods, getters, commands, output = [], [], [], [], [], [] -%}
{%- set filt = params.F|default("")|lower -%}
{%- set help_strings = {} -%}
{%- if obj -%}
# find _help to attatch later
{%- for attr_name in obj.__dir__() -%}
{%- if attr_name.startswith('cmd_') and attr_name.endswith('_help') -%}
{%- set _ = help_strings.update({attr_name|replace('_help', ''): obj|attr(attr_name)}) -%}
{%- endif -%}
{%- endfor -%}
# main loop
{%- for attr_name in obj.__dir__()|sort -%}
{%- if (not attr_name.startswith('__') or params.DUNDER) and (not filt or filt in attr_name|lower) and not attr_name.endswith('_help') -%}
{%- set attr_val = obj|attr(attr_name) -%}
{%- if attr_val.__code__ is defined and attr_val.__code__.co_varnames is defined -%}
{%- set doc = help_strings.get(attr_name, None) -%}
{%- if attr_name.startswith('get_') -%}
{%- set code = attr_val.__code__ -%}
{%- if code.co_argcount == 2 and code.co_varnames[1] == 'eventtime' or code.co_argcount == 1 -%}
{%- set return_val = attr_val(p.get_reactor().monotonic()) if code.co_argcount != 1 else attr_val() -%}
{%- set status_html = _render_callable(attr_val, attr_name, color_getter) ~ ': ' ~ _render_value(return_val) -%}
{%- set _ = getters.append( "<div style='display: flex; align-items: baseline;'>" ~ status_html ~ "</div>") -%}
{%- else -%}
{%- set _ = getters.append(_render_callable(attr_val, attr_name, color_getter, doc)) -%}
{%- endif -%}
{%- elif attr_name.startswith('cmd_') -%} {%- set _ = commands.append( _render_callable(attr_val, attr_name, color_command, doc)) -%}
{%- elif attr_name.startswith('_') -%} {%- set _ = private_methods.append(_render_callable(attr_val, attr_name, color_private_method, doc)) -%}
{%- else -%} {%- set _ = public_methods.append( _render_callable(attr_val, attr_name, color_method, doc)) -%}
{%- endif -%}
{%- elif attr_val is callable -%}
{%- set type_name = attr_val.__class__.__name__ -%}
{%- set fallback_html = "<div>" ~ _span(color_private_method, attr_name ~ "(i dunno)") ~ _span(color_type, "<"|e ~ type_name) ~ ">"|e ~ "</div>" -%}
{%- set _ = private_methods.append(fallback_html) if attr_name.startswith('_') else public_methods.append(fallback_html) -%}
{%- else -%}
{%- set attr_html = "<div style='display: flex; align-items: baseline;'>" ~ _span(color_attr, attr_name ~ ": ") ~ _render_value(attr_val) ~ "</div>" -%}
{%- set _ = attributes.append(attr_html) -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
#--- Assemble and Print Report ---
{%- set _ = output.append(_details(_span(color_attr, "Attributes (" ~ attributes|length ~ ")"), attributes|sort|join(''))) if attributes else '' -%}
{%- set _ = output.append(_details(_span(color_getter, "Getters (" ~ getters|length ~ ")"), "<div>" ~ getters|sort|join('</div><div>') ~ "</div>")) if getters else '' -%}
{%- set _ = output.append(_details(_span(color_command, "Commands (" ~ commands|length ~ ")"), "<div>" ~ commands|sort|join('</div><div>') ~ "</div>")) if commands else '' -%}
{%- set _ = output.append(_details(_span(color_method, "Public Methods (" ~ public_methods|length ~ ")"), "<div>" ~ public_methods|sort|join('</div><div>') ~ "</div>")) if public_methods else '' -%}
{%- set _ = output.append(_details(_span(color_private_method, "Private Methods (" ~ private_methods|length ~ ")"), "<div>" ~ private_methods|sort|join('</div><div>') ~ "</div>")) if private_methods else '' -%}
{%- set title = "<b>Inspecting Object: '" ~ obj_name|e ~ "'</b>" -%}
{%- set report = _details(title, output|join(''), is_open=True) -%}
# may klipper forgive me for shitting literal megabytes to the console in one respond
{action_respond_info("<div style='font-size: " ~ font_size ~ ";'>" ~ report ~ "</div>")} #font-family: monospace;