From df2f2f40b549762d5663dbf7bef8d494d0759913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCller?= Date: Sat, 21 Dec 2024 01:01:24 +0100 Subject: [PATCH] Move autosaved data to dedicated config file Autosaved data is instead saved to a *.autosave.cfg config file, which is automatically loaded along it's associated main *.cfg file. The goal is to eventually no longer modify user-authored configuration automatically, so that it can't introduce unintended changes. This will also allow proper version control of config files without producing unnecessarily noisy diffs. Autosaved data will keep being read from both embedded and dedicated sources, but the SAVE_CONFIG command will only write the dedicated file from now on. This maintains backwards-compatibility, while gradually migrating existing autosaved settings to the *.autosave.cfg file whenever the command keeps being used. This is just the first step in a bigger migration process, and therefore still requires some automated changes to the user config files at the moment. However, this will reduce the necessary changes, and is only required while the embedded autosave system is still supported. --- klippy/configfile.py | 48 ++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/klippy/configfile.py b/klippy/configfile.py index 346df4e2f..e0a65f3f7 100644 --- a/klippy/configfile.py +++ b/klippy/configfile.py @@ -323,16 +323,17 @@ def __init__(self, printer): def get_printer(self): return self.printer + def _read_config_file_silent(self, filename): + with open(filename, "r") as f: + return f.read().replace("\r\n", "\n") + def _read_config_file(self, filename): try: - f = open(filename, "r") - data = f.read() - f.close() + return self._read_config_file_silent(filename) except: msg = "Unable to open config file %s" % (filename,) logging.exception(msg) raise error(msg) - return data.replace("\r\n", "\n") def _find_autosave_data(self, data): regular_data = data @@ -472,10 +473,27 @@ def read_config(self, filename): self._read_config_file(filename), filename ) + def _build_autosave_filename(self, filename): + directory, name = os.path.split(filename) + base, ext = os.path.splitext(name) + return os.path.join(directory, base + ".autosave" + ext) + def read_main_config(self): filename = self.printer.get_start_args()["config_file"] - data = self._read_config_file(filename) - regular_data, autosave_data = self._find_autosave_data(data) + + # Autosave data used to get written straight into the user's main config + # file. This was very invasive, so we now write all autosave data into a + # separate file, which is then loaded implicitly with the main config. + # However, we still have to read existing autosave data in the main + # config file to remain backwards-compatible. + regular_data, autosave_data = self._find_autosave_data( + self._read_config_file(filename)) + try: + autosave_data = self._read_config_file_silent( + self._build_autosave_filename(filename)) + "\n" + autosave_data + except FileNotFoundError: + pass # optional + regular_config = self._build_config_wrapper(regular_data, filename) autosave_data = self._strip_duplicates(autosave_data, regular_config) self.autosave = self._build_config_wrapper(autosave_data, filename) @@ -726,11 +744,8 @@ def cmd_SAVE_CONFIG(self, gcmd): return gcode = self.printer.lookup_object("gcode") # Create string containing autosave data - autosave_data = self._build_config_string(self.autosave) - lines = [("#*# " + l).strip() for l in autosave_data.split("\n")] - lines.insert(0, "\n" + AUTOSAVE_HEADER.rstrip()) - lines.append("") - autosave_data = "\n".join(lines) + autosave_data = AUTOSAVE_HEADER.strip() + "\n\n" + self._build_config_string( + self.autosave) + "\n" # Read in and validate current config file cfgname = self.printer.get_start_args()["config_file"] try: @@ -748,8 +763,15 @@ def cmd_SAVE_CONFIG(self, gcmd): # NOW we're safe to check for conflicts self._disallow_include_conflicts(regular_data, cfgname, gcode) - data = regular_data.rstrip() + autosave_data - self._write_backup(cfgname, data, gcode) + + # Eventually we would want to stop touching the user config at all and + # only write autosave data independently, but for as long as we support + # auto-migration from embedded autosave data, we have to keep fixing up + # those user configs. See config loading for more info. + with open(self._build_autosave_filename(cfgname), "w+") as f: + f.write(autosave_data) + if regular_data != data: + self._write_backup(cfgname, regular_data, gcode) # If requested restart or no restart just flag config saved require_restart = gcmd.get_int("RESTART", 1, minval=0, maxval=1)