Files
mangavalkandClaude Opus 5.5 9dc733b5bb Fix pause/resume chain, shared print cleanup, add CLAUDE.md
- RESUME is defined only in print/pause_resume.cfg; heat_soak.cfg's
  duplicate replaced it (no unretract, no fan restore). It now runs
  HEAT_SOAK_RESUME -> _RESUME_PRINT -> RESUME_BASE.
- PAUSE runs PAUSE_BASE first, then retracts and parks once (X20 Y345);
  a parked flag makes resume undo only what PAUSE did.
- New _PRINT_CLEANUP shared by PRINT_END and CANCEL_PRINT: heaters incl.
  iHEATER_OFF, fan, M221 S100, bdwidth off, mesh and skew cleared.
  In PRINT_END it runs after RESTORE_GCODE_STATE so the flow reset sticks.
- Add CLAUDE.md and tools/effective_config.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:20:28 +02:00

83 lines
2.9 KiB
Python

"""Show or compare the config Klipper builds from macros/klipper-macros.cfg.
Klipper expands [include ...] in order and merges sections with the same
name (the last value of an option wins). This script does the same, so a
refactor can be checked for unintended changes.
python tools/effective_config.py list sections of the working tree
python tools/effective_config.py HEAD compare HEAD with the working tree
python tools/effective_config.py A B compare two git refs
python tools/effective_config.py HEAD -v also print old/new values
"""
import configparser
import os
import re
import subprocess
import sys
import tempfile
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE = os.path.join('macros', 'klipper-macros.cfg')
def expand(path):
lines = []
with open(path, encoding='utf-8') as f:
for line in f.read().replace('\r\n', '\n').split('\n'):
m = re.match(r'^\[include ([^\]]+)\]', line.split('#')[0])
if m:
inc = os.path.join(os.path.dirname(path), m.group(1).strip())
lines += expand(inc)
else:
lines.append(line)
return lines
def load(root):
cp = configparser.RawConfigParser(strict=False,
inline_comment_prefixes=(';', '#'))
cp.read_string('\n'.join(expand(os.path.join(root, BASE))))
return {s: dict(cp.items(s)) for s in cp.sections()}
def checkout(ref, tmp):
archive = subprocess.run(['git', '-C', ROOT, 'archive', ref, 'macros'],
check=True, capture_output=True).stdout
subprocess.run(['tar', '-x', '-C', tmp], input=archive, check=True)
return tmp
def main(argv):
verbose = '-v' in argv
refs = [a for a in argv if a != '-v']
with tempfile.TemporaryDirectory() as t1, tempfile.TemporaryDirectory() as t2:
if not refs:
for s in sorted(load(ROOT)):
print(s)
return 0
old = load(checkout(refs[0], t1))
new = load(checkout(refs[1], t2) if len(refs) > 1 else ROOT)
added, removed = sorted(set(new) - set(old)), sorted(set(old) - set(new))
changed = 0
for s in added:
print('added ', s)
for s in removed:
print('removed ', s)
for s in sorted(set(old) & set(new)):
keys = sorted(k for k in set(old[s]) | set(new[s])
if old[s].get(k) != new[s].get(k))
if keys:
changed += 1
print('changed ', s, keys)
if verbose:
for k in keys:
print(' old %s: %r' % (k, old[s].get(k)))
print(' new %s: %r' % (k, new[s].get(k)))
print('%d sections, %d added, %d removed, %d changed'
% (len(new), len(added), len(removed), changed))
return 0
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))