Coverage for src / zooc / config / config.py: 0%
78 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 21:45 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 21:45 +0000
1"""Post-install and uninstall script for ZOOC.
3Run this script after the ZOOC package is installed:
5.. code-block:: shell
7 ~/klippy-env/bin/zooc-setup
9Script copies the necessary configuration files and modifies the printer configuration to include ZOOC:
11- ~/klipper/klippy/extras/zooc.py for klipper to load ZOOC module.
12- ~/klipper_config/zooc.cfg for the default ZOOC configuration, if it does not exist yet.
13- Modify ~/printer.cfg to include the above ZOOC configuration.
15To uninstall ZOOC, run the teardown script first to undo the zooc-setup changes:
17.. code-block:: shell
19 ~/klippy-env/bin/zooc-teardown
21"""
23import os
24import re
25import shutil
26from importlib.resources import files
27from typing import IO # noqa: F401
29# Define filenames and paths once to avoid repetition
30KLIPPER_EXTRAS_DIR = os.path.expanduser("~/klipper/klippy/extras")
31KLIPPER_CFG_DIR = os.path.expanduser("~/klipper_config")
32PRINTER_CFG_PATH = os.path.expanduser("~/printer.cfg")
33ZOOC_CFG_INCLUDE_LINE = "[include klipper_config/zooc.cfg]"
35def _insert_line_before(filename: str, match_line: str, new_line: str) -> bool:
36 """Insert a new line before a matching line in a file, or if not found, at the end of the file.
38 :param filename: Path to the file to modify.
39 :param match_line: Regular expression to match the line before which the new line should be inserted.
40 :param new_line: The new line to insert.
41 :return: True if the new line was inserted, False if it already exists.
42 """
43 with open(file=filename, encoding="utf-8") as f:
44 lines = f.readlines()
45 if any(new_line in line for line in lines):
46 return False # Line already exists, do nothing
47 pattern = re.compile(match_line)
48 inserted = False
49 with open(file=filename, mode="w", encoding="utf-8") as f:
50 for line in lines:
51 if not inserted and pattern.match(line):
52 f.write(new_line + "\n")
53 inserted = True
54 f.write(line)
55 return True
58def _remove_line_from(filename: str, line_to_remove: str) -> bool:
59 """Remove a specific line from a file.
61 :param filename: Path to the file to modify.
62 :param line_to_remove: The line to remove.
63 :return: True if the line was removed, False if it was not found.
64 """
65 with open(file=filename, encoding="utf-8") as f:
66 lines = f.readlines()
68 line_removed = False
69 with open(file=filename, mode="w", encoding="utf-8") as f:
70 for line in lines:
71 if line.strip() == line_to_remove.strip():
72 line_removed = True
73 continue
74 f.write(line)
75 return line_removed
78def copy_resource_file(input_pkg: str, input_file: str, output_path: str, mkdir: bool = True, overwrite: bool = True) -> bool:
79 """Copy a file from a package to a destination path.
81 :param input_pkg: Package name where the file is located.
82 :param input_file: File name to copy.
83 :param output_path: Destination path where the file should be copied.
84 :param mkdir: If True, create the directory if it does not exist.
85 :param overwrite: If True, overwrite the file if it already exists.
86 :return: True if the file was copied, False if it already exists and overwrite is False.
87 :raises FileNotFoundError: If the output path does not exist and mkdir is False
88 """
89 if mkdir:
90 os.makedirs(output_path, exist_ok=True)
92 if os.path.exists(output_path):
93 # Ensure the output path is a directory
94 output_file = os.path.join(output_path, input_file)
95 if overwrite or not os.path.exists(output_file):
96 source = files(input_pkg).joinpath(input_file)
97 with source.open("rb") as src: # type: IO[bytes]
98 with open(output_file, "wb") as dst: # type: IO[bytes]
99 # noinspection PyTypeChecker
100 shutil.copyfileobj(src, dst)
101 os.chmod(output_file, 0o644)
102 print(f"Copied {input_file} to {output_file}")
103 return True
104 return False
105 raise FileNotFoundError(f"File {input_file} was not copied due to directory {output_path} does not exist.")
107# -----------------
108# Setup and Teardown Functions
109# -----------------
111def setup() -> int:
112 """Post-install script for ZOOC.
114 :return: 0 on success, 1 on failure.
115 """
116 print("Running post-install steps for ZOOC...")
118 # Copy zooc.py to the Klipper extras directory.
119 copy_resource_file(input_pkg='extras', input_file='zooc.py', output_path=KLIPPER_EXTRAS_DIR, mkdir=False)
121 # Copy zooc.cfg to the Klipper configuration if it does not exist.
122 cfg_copied = copy_resource_file(input_pkg='zooc.config', input_file='zooc.cfg', output_path=KLIPPER_CFG_DIR, mkdir=True, overwrite=False)
124 # Include ZOOC in printer configuration
125 if os.path.exists(PRINTER_CFG_PATH):
126 if (_insert_line_before(
127 PRINTER_CFG_PATH,
128 r"^#\*\#",
129 ZOOC_CFG_INCLUDE_LINE,
130 )):
131 print(f"Modified '{PRINTER_CFG_PATH} 'to include ZOOC configuration.")
132 else:
133 print(f"'{PRINTER_CFG_PATH}' already includes the ZOOC configuration")
134 else:
135 print(f"Warning: '{PRINTER_CFG_PATH}' does not exist. Please add '{ZOOC_CFG_INCLUDE_LINE}' manually if needed.")
137 if cfg_copied:
138 print(f"Default configuration copied to '{KLIPPER_CFG_DIR}'. Check and modify the zooc.cfg if needed.")
139 else:
140 print(f"Keeping existing '{KLIPPER_CFG_DIR}'. Make sure it is compatible with the current version.")
142 return 0
145def teardown() -> int:
146 """Uninstalls ZOOC post-install files.
148 :return: 0 on success, 1 on failure.
149 """
150 print("Running uninstallation steps for ZOOC...")
152 # Remove zooc.py from the Klipper extras directory.
153 zooc_py_path = os.path.join(KLIPPER_EXTRAS_DIR, "zooc.py")
154 if os.path.exists(zooc_py_path):
155 os.remove(zooc_py_path)
156 print(f"Removed {zooc_py_path}.")
157 else:
158 print(f"{zooc_py_path} not found, nothing to remove.")
160 # Remove ZOOC from printer.cfg.
161 if os.path.exists(PRINTER_CFG_PATH):
162 if _remove_line_from(PRINTER_CFG_PATH, ZOOC_CFG_INCLUDE_LINE):
163 print(f"Removed ZOOC configuration from '{PRINTER_CFG_PATH}'.")
164 else:
165 print(f"ZOOC configuration was not found in '{PRINTER_CFG_PATH}'.")
166 else:
167 print(f"Warning: '{PRINTER_CFG_PATH}' does not exist. Please remove '{ZOOC_CFG_INCLUDE_LINE}' manually if needed.")
168 print(f"Please, remove `ZOOC_ADJUST` and related commands from `{PRINTER_CFG_PATH}` manually.")
170 # zooc.cfg is *not* removed to avoid deleting user modifications.
171 # The user should remove it manually if they wish.
172 zooc_cfg_path = os.path.join(KLIPPER_CFG_DIR, "zooc.cfg")
173 print(f"Skipping '{zooc_cfg_path}' removal to preserve user data. Please remove it manually if you no longer need it.")
175 return 0