feat(style): use black code style / formatter

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
This commit is contained in:
dw-0
2023-10-29 00:31:34 +02:00
parent 84a530be7d
commit 358c666da9
17 changed files with 261 additions and 170 deletions

View File

@@ -19,8 +19,13 @@ class BaseInstance(ABC):
def blacklist(cls) -> List[str]: def blacklist(cls) -> List[str]:
return [] return []
def __init__(self, prefix: Optional[str], name: Optional[str], def __init__(
user: Optional[str], data_dir_name: Optional[str]): self,
prefix: Optional[str],
name: Optional[str],
user: Optional[str],
data_dir_name: Optional[str],
):
self._prefix = prefix self._prefix = prefix
self._name = name self._name = name
self._user = user self._user = user
@@ -82,4 +87,5 @@ class BaseInstance(ABC):
@abstractmethod @abstractmethod
def get_service_file_name(self) -> str: def get_service_file_name(self) -> str:
raise NotImplementedError( raise NotImplementedError(
"Subclasses must implement the get_service_file_name method") "Subclasses must implement the get_service_file_name method"
)

View File

@@ -22,11 +22,16 @@ from kiauh.utils.logger import Logger
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class InstanceManager: class InstanceManager:
def __init__(self, instance_type: Type[BaseInstance], def __init__(
current_instance: Optional[BaseInstance] = None) -> None: self,
instance_type: Type[BaseInstance],
current_instance: Optional[BaseInstance] = None,
) -> None:
self.instance_type = instance_type self.instance_type = instance_type
self.current_instance = current_instance self.current_instance = current_instance
self.instance_name = current_instance.name if current_instance is not None else None self.instance_name = (
current_instance.name if current_instance is not None else None
)
self.instances = [] self.instances = []
def get_current_instance(self) -> BaseInstance: def get_current_instance(self) -> BaseInstance:
@@ -34,7 +39,9 @@ class InstanceManager:
def set_current_instance(self, instance: BaseInstance) -> None: def set_current_instance(self, instance: BaseInstance) -> None:
self.current_instance = instance self.current_instance = instance
self.instance_name = f"{instance.prefix}-{instance.name}" if instance.name else instance.prefix self.instance_name = (
f"{instance.prefix}-{instance.name}" if instance.name else instance.prefix
)
def create_instance(self) -> None: def create_instance(self) -> None:
if self.current_instance is not None: if self.current_instance is not None:
@@ -59,49 +66,41 @@ class InstanceManager:
def enable_instance(self) -> None: def enable_instance(self) -> None:
Logger.print_info(f"Enabling {self.instance_name}.service ...") Logger.print_info(f"Enabling {self.instance_name}.service ...")
try: try:
command = ["sudo", "systemctl", "enable", command = ["sudo", "systemctl", "enable", f"{self.instance_name}.service"]
f"{self.instance_name}.service"]
if subprocess.run(command, check=True): if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_name}.service enabled.") Logger.print_ok(f"{self.instance_name}.service enabled.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error( Logger.print_error(f"Error enabling service {self.instance_name}.service:")
f"Error enabling service {self.instance_name}.service:")
Logger.print_error(f"{e}") Logger.print_error(f"{e}")
def disable_instance(self) -> None: def disable_instance(self) -> None:
Logger.print_info(f"Disabling {self.instance_name}.service ...") Logger.print_info(f"Disabling {self.instance_name}.service ...")
try: try:
command = ["sudo", "systemctl", "disable", command = ["sudo", "systemctl", "disable", f"{self.instance_name}.service"]
f"{self.instance_name}.service"]
if subprocess.run(command, check=True): if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_name}.service disabled.") Logger.print_ok(f"{self.instance_name}.service disabled.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error( Logger.print_error(f"Error disabling service {self.instance_name}.service:")
f"Error disabling service {self.instance_name}.service:")
Logger.print_error(f"{e}") Logger.print_error(f"{e}")
def start_instance(self) -> None: def start_instance(self) -> None:
Logger.print_info(f"Starting {self.instance_name}.service ...") Logger.print_info(f"Starting {self.instance_name}.service ...")
try: try:
command = ["sudo", "systemctl", "start", command = ["sudo", "systemctl", "start", f"{self.instance_name}.service"]
f"{self.instance_name}.service"]
if subprocess.run(command, check=True): if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_name}.service started.") Logger.print_ok(f"{self.instance_name}.service started.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error( Logger.print_error(f"Error starting service {self.instance_name}.service:")
f"Error starting service {self.instance_name}.service:")
Logger.print_error(f"{e}") Logger.print_error(f"{e}")
def stop_instance(self) -> None: def stop_instance(self) -> None:
Logger.print_info(f"Stopping {self.instance_name}.service ...") Logger.print_info(f"Stopping {self.instance_name}.service ...")
try: try:
command = ["sudo", "systemctl", "stop", command = ["sudo", "systemctl", "stop", f"{self.instance_name}.service"]
f"{self.instance_name}.service"]
if subprocess.run(command, check=True): if subprocess.run(command, check=True):
Logger.print_ok(f"{self.instance_name}.service stopped.") Logger.print_ok(f"{self.instance_name}.service stopped.")
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error( Logger.print_error(f"Error stopping service {self.instance_name}.service:")
f"Error stopping service {self.instance_name}.service:")
Logger.print_error(f"{e}") Logger.print_error(f"{e}")
raise raise
@@ -120,8 +119,7 @@ class InstanceManager:
if not self.instances: if not self.instances:
self._find_instances() self._find_instances()
return sorted(self.instances, return sorted(self.instances, key=lambda x: self._sort_instance_list(x.name))
key=lambda x: self._sort_instance_list(x.name))
def _find_instances(self) -> None: def _find_instances(self) -> None:
prefix = self.instance_type.__name__.lower() prefix = self.instance_type.__name__.lower()
@@ -131,12 +129,13 @@ class InstanceManager:
service_list = [ service_list = [
os.path.join(SYSTEMD, service) os.path.join(SYSTEMD, service)
for service in os.listdir(SYSTEMD) for service in os.listdir(SYSTEMD)
if pattern.search(service) if pattern.search(service) and not any(s in service for s in excluded)
and not any(s in service for s in excluded)] ]
instance_list = [ instance_list = [
self.instance_type(name=self._get_instance_name(Path(service))) self.instance_type(name=self._get_instance_name(Path(service)))
for service in service_list] for service in service_list
]
self.instances = instance_list self.instances = instance_list

View File

@@ -15,6 +15,6 @@ from kiauh.utils.logger import Logger
def main(): def main():
try: try:
MainMenu().start() MainMenu().start()
except KeyboardInterrupt: except KeyboardInterrupt:
Logger.print_ok("\nHappy printing!\n", prefix=False) Logger.print_ok("\nHappy printing!\n", prefix=False)

View File

@@ -17,14 +17,11 @@ from kiauh.utils.constants import COLOR_YELLOW, RESET_FORMAT
class AdvancedMenu(BaseMenu): class AdvancedMenu(BaseMenu):
def __init__(self): def __init__(self):
super().__init__( super().__init__(header=True, options={}, footer_type="back")
header=True,
options={},
footer_type="back"
)
def print_menu(self): def print_menu(self):
menu = textwrap.dedent(f""" menu = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_YELLOW}~~~~~~~~~~~~~ [ Advanced Menu ] ~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_YELLOW}~~~~~~~~~~~~~ [ Advanced Menu ] ~~~~~~~~~~~~~{RESET_FORMAT} |
|-------------------------------------------------------| |-------------------------------------------------------|
@@ -36,5 +33,6 @@ class AdvancedMenu(BaseMenu):
| 2) [Flash only] | | | 2) [Flash only] | |
| 3) [Build + Flash] | Extras: | | 3) [Build + Flash] | Extras: |
| 4) [Get MCU ID] | 7) [G-Code Shell Command] | | 4) [Get MCU ID] | 7) [G-Code Shell Command] |
""")[1:] """
)[1:]
print(menu, end="") print(menu, end="")

View File

@@ -15,8 +15,13 @@ import textwrap
from abc import abstractmethod, ABC from abc import abstractmethod, ABC
from typing import Dict, Any from typing import Dict, Any
from kiauh.utils.constants import COLOR_GREEN, COLOR_YELLOW, COLOR_RED, \ from kiauh.utils.constants import (
COLOR_CYAN, RESET_FORMAT COLOR_GREEN,
COLOR_YELLOW,
COLOR_RED,
COLOR_CYAN,
RESET_FORMAT,
)
def clear(): def clear():
@@ -24,58 +29,70 @@ def clear():
def print_header(): def print_header():
header = textwrap.dedent(f""" header = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_CYAN}~~~~~~~~~~~~~~~~~ [ KIAUH ] ~~~~~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_CYAN}~~~~~~~~~~~~~~~~~ [ KIAUH ] ~~~~~~~~~~~~~~~~~{RESET_FORMAT} |
| {COLOR_CYAN} Klipper Installation And Update Helper {RESET_FORMAT} | | {COLOR_CYAN} Klipper Installation And Update Helper {RESET_FORMAT} |
| {COLOR_CYAN}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_CYAN}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(header, end="") print(header, end="")
def print_quit_footer(): def print_quit_footer():
footer = textwrap.dedent(f""" footer = textwrap.dedent(
f"""
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_RED}Q) Quit{RESET_FORMAT} | | {COLOR_RED}Q) Quit{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(footer, end="") print(footer, end="")
def print_back_footer(): def print_back_footer():
footer = textwrap.dedent(f""" footer = textwrap.dedent(
f"""
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_GREEN}B) « Back{RESET_FORMAT} | | {COLOR_GREEN}B) « Back{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(footer, end="") print(footer, end="")
def print_back_help_footer(): def print_back_help_footer():
footer = textwrap.dedent(f""" footer = textwrap.dedent(
f"""
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_RED}Q) Quit{RESET_FORMAT} | | {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_RED}Q) Quit{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(footer, end="") print(footer, end="")
def print_back_quit_footer(): def print_back_quit_footer():
footer = textwrap.dedent(f""" footer = textwrap.dedent(
f"""
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_YELLOW}H) Help [?]{RESET_FORMAT} | | {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_YELLOW}H) Help [?]{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(footer, end="") print(footer, end="")
def print_back_quit_help_footer(): def print_back_quit_help_footer():
footer = textwrap.dedent(f""" footer = textwrap.dedent(
f"""
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_RED}Q) Quit{RESET_FORMAT} | {COLOR_YELLOW}H) Help [?]{RESET_FORMAT} | | {COLOR_GREEN}B) « Back{RESET_FORMAT} | {COLOR_RED}Q) Quit{RESET_FORMAT} | {COLOR_YELLOW}H) Help [?]{RESET_FORMAT} |
\=======================================================/ \=======================================================/
""")[1:] """
)[1:]
print(footer, end="") print(footer, end="")
@@ -86,8 +103,9 @@ class BaseMenu(ABC):
BACK_QUIT_FOOTER = "back_quit" BACK_QUIT_FOOTER = "back_quit"
BACK_QUIT_HELP_FOOTER = "back_quit_help" BACK_QUIT_HELP_FOOTER = "back_quit_help"
def __init__(self, options: Dict[int, Any], options_offset=0, header=True, def __init__(
footer_type="quit"): self, options: Dict[int, Any], options_offset=0, header=True, footer_type="quit"
):
self.options = options self.options = options
self.options_offset = options_offset self.options_offset = options_offset
self.header = header self.header = header
@@ -95,8 +113,7 @@ class BaseMenu(ABC):
@abstractmethod @abstractmethod
def print_menu(self): def print_menu(self):
raise NotImplementedError( raise NotImplementedError("Subclasses must implement the print_menu method")
"Subclasses must implement the print_menu method")
def print_footer(self): def print_footer(self):
footer_type_map = { footer_type_map = {
@@ -104,10 +121,9 @@ class BaseMenu(ABC):
self.BACK_FOOTER: print_back_footer, self.BACK_FOOTER: print_back_footer,
self.BACK_HELP_FOOTER: print_back_help_footer, self.BACK_HELP_FOOTER: print_back_help_footer,
self.BACK_QUIT_FOOTER: print_back_quit_footer, self.BACK_QUIT_FOOTER: print_back_quit_footer,
self.BACK_QUIT_HELP_FOOTER: print_back_quit_help_footer self.BACK_QUIT_HELP_FOOTER: print_back_quit_help_footer,
} }
footer_function = footer_type_map.get(self.footer_type, footer_function = footer_type_map.get(self.footer_type, print_quit_footer)
print_quit_footer)
footer_function() footer_function()
def display(self): def display(self):
@@ -121,9 +137,11 @@ class BaseMenu(ABC):
while True: while True:
choice = input(f"{COLOR_CYAN}###### Perform action: {RESET_FORMAT}") choice = input(f"{COLOR_CYAN}###### Perform action: {RESET_FORMAT}")
error_msg = f"{COLOR_RED}Invalid input.{RESET_FORMAT}" \ error_msg = (
if choice.isalpha() \ f"{COLOR_RED}Invalid input.{RESET_FORMAT}"
if choice.isalpha()
else f"{COLOR_RED}Invalid input. Select a number between {min(self.options)} and {max(self.options)}.{RESET_FORMAT}" else f"{COLOR_RED}Invalid input. Select a number between {min(self.options)} and {max(self.options)}.{RESET_FORMAT}"
)
if choice.isdigit() and 0 <= int(choice) < len(self.options): if choice.isdigit() and 0 <= int(choice) < len(self.options):
return choice return choice
@@ -133,10 +151,12 @@ class BaseMenu(ABC):
"back": ["b"], "back": ["b"],
"back_help": ["b", "h"], "back_help": ["b", "h"],
"back_quit": ["b", "q"], "back_quit": ["b", "q"],
"back_quit_help": ["b", "q", "h"] "back_quit_help": ["b", "q", "h"],
} }
if self.footer_type in allowed_input and choice.lower() in \ if (
allowed_input[self.footer_type]: self.footer_type in allowed_input
and choice.lower() in allowed_input[self.footer_type]
):
return choice return choice
else: else:
print(error_msg) print(error_msg)
@@ -169,7 +189,8 @@ class BaseMenu(ABC):
raise NotImplementedError(f"No implementation for option {choice}") raise NotImplementedError(f"No implementation for option {choice}")
else: else:
raise TypeError( raise TypeError(
f"Type {type(option)} of option {choice} not of type BaseMenu or Method") f"Type {type(option)} of option {choice} not of type BaseMenu or Method"
)
def navigate_to_submenu(self, submenu_class): def navigate_to_submenu(self, submenu_class):
submenu = submenu_class() submenu = submenu_class()

View File

@@ -32,13 +32,14 @@ class InstallMenu(BaseMenu):
8: self.install_obico, 8: self.install_obico,
9: self.install_octoeverywhere, 9: self.install_octoeverywhere,
10: self.install_mobileraker, 10: self.install_mobileraker,
11: self.install_crowsnest 11: self.install_crowsnest,
}, },
footer_type="back" footer_type="back",
) )
def print_menu(self): def print_menu(self):
menu = textwrap.dedent(f""" menu = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_GREEN}~~~~~~~~~~~ [ Installation Menu ] ~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_GREEN}~~~~~~~~~~~ [ Installation Menu ] ~~~~~~~~~~~{RESET_FORMAT} |
|-------------------------------------------------------| |-------------------------------------------------------|
@@ -56,7 +57,8 @@ class InstallMenu(BaseMenu):
| | Webcam Streamer: | | | Webcam Streamer: |
| Touchscreen GUI: | 11) [Crowsnest] | | Touchscreen GUI: | 11) [Crowsnest] |
| 5) [KlipperScreen] | | | 5) [KlipperScreen] | |
""")[1:] """
)[1:]
print(menu, end="") print(menu, end="")
def install_klipper(self): def install_klipper(self):

View File

@@ -31,13 +31,14 @@ class MainMenu(BaseMenu):
3: RemoveMenu, 3: RemoveMenu,
4: AdvancedMenu, 4: AdvancedMenu,
5: None, 5: None,
6: SettingsMenu 6: SettingsMenu,
}, },
footer_type="quit" footer_type="quit",
) )
def print_menu(self): def print_menu(self):
menu = textwrap.dedent(f""" menu = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_CYAN}~~~~~~~~~~~~~~~ [ Main Menu ] ~~~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_CYAN}~~~~~~~~~~~~~~~ [ Main Menu ] ~~~~~~~~~~~~~~~{RESET_FORMAT} |
|-------------------------------------------------------| |-------------------------------------------------------|
@@ -58,7 +59,8 @@ class MainMenu(BaseMenu):
| | OctoEverywhere: <TODO> | | | OctoEverywhere: <TODO> |
|-------------------------------------------------------| |-------------------------------------------------------|
| {COLOR_CYAN}KIAUH v6.0.0{RESET_FORMAT} | Changelog: {COLOR_MAGENTA}https://git.io/JnmlX{RESET_FORMAT} | | {COLOR_CYAN}KIAUH v6.0.0{RESET_FORMAT} | Changelog: {COLOR_MAGENTA}https://git.io/JnmlX{RESET_FORMAT} |
""")[1:] """
)[1:]
print(menu, end="") print(menu, end="")
def test(self): def test(self):

View File

@@ -38,11 +38,12 @@ class RemoveMenu(BaseMenu):
14: self.remove_mobileraker, 14: self.remove_mobileraker,
15: self.remove_nginx, 15: self.remove_nginx,
}, },
footer_type="back" footer_type="back",
) )
def print_menu(self): def print_menu(self):
menu = textwrap.dedent(f""" menu = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_RED}~~~~~~~~~~~~~~ [ Remove Menu ] ~~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_RED}~~~~~~~~~~~~~~ [ Remove Menu ] ~~~~~~~~~~~~~~{RESET_FORMAT} |
|-------------------------------------------------------| |-------------------------------------------------------|
@@ -60,7 +61,8 @@ class RemoveMenu(BaseMenu):
| | 14) [Mobileraker] | | | 14) [Mobileraker] |
| Touchscreen GUI: | 15) [NGINX] | | Touchscreen GUI: | 15) [NGINX] |
| 7) [KlipperScreen] | | | 7) [KlipperScreen] | |
""")[1:] """
)[1:]
print(menu, end="") print(menu, end="")
def remove_klipper(self): def remove_klipper(self):

View File

@@ -15,10 +15,7 @@ from kiauh.menus.base_menu import BaseMenu
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class SettingsMenu(BaseMenu): class SettingsMenu(BaseMenu):
def __init__(self): def __init__(self):
super().__init__( super().__init__(header=True, options={})
header=True,
options={}
)
def print_menu(self): def print_menu(self):
print("self") print("self")

View File

@@ -35,11 +35,12 @@ class UpdateMenu(BaseMenu):
11: self.update_crowsnest, 11: self.update_crowsnest,
12: self.upgrade_system_packages, 12: self.upgrade_system_packages,
}, },
footer_type="back" footer_type="back",
) )
def print_menu(self): def print_menu(self):
menu = textwrap.dedent(f""" menu = textwrap.dedent(
f"""
/=======================================================\\ /=======================================================\\
| {COLOR_GREEN}~~~~~~~~~~~~~~ [ Update Menu ] ~~~~~~~~~~~~~~{RESET_FORMAT} | | {COLOR_GREEN}~~~~~~~~~~~~~~ [ Update Menu ] ~~~~~~~~~~~~~~{RESET_FORMAT} |
|-------------------------------------------------------| |-------------------------------------------------------|
@@ -65,7 +66,8 @@ class UpdateMenu(BaseMenu):
| 11) [Crowsnest] | | | | 11) [Crowsnest] | | |
| |-----------------------------| | |-----------------------------|
| 12) [System] | | | | 12) [System] | | |
""")[1:] """
)[1:]
print(menu, end="") print(menu, end="")
def update_all(self): def update_all(self):

View File

@@ -10,7 +10,6 @@
# ======================================================================= # # ======================================================================= #
import os import os
import pwd
import shutil import shutil
import subprocess import subprocess
from pathlib import Path from pathlib import Path
@@ -29,10 +28,12 @@ class Klipper(BaseInstance):
return ["None", "mcu"] return ["None", "mcu"]
def __init__(self, name: str): def __init__(self, name: str):
super().__init__(name=name, super().__init__(
prefix="klipper", name=name,
user=CURRENT_USER, prefix="klipper",
data_dir_name=self._get_data_dir_from_name(name)) user=CURRENT_USER,
data_dir_name=self._get_data_dir_from_name(name),
)
self.klipper_dir = KLIPPER_DIR self.klipper_dir = KLIPPER_DIR
self.env_dir = KLIPPER_ENV_DIR self.env_dir = KLIPPER_ENV_DIR
self.cfg_file = f"{self.cfg_dir}/printer.cfg" self.cfg_file = f"{self.cfg_dir}/printer.cfg"
@@ -43,26 +44,35 @@ class Klipper(BaseInstance):
def create(self) -> None: def create(self) -> None:
Logger.print_info("Creating Klipper Instance") Logger.print_info("Creating Klipper Instance")
module_path = os.path.dirname(os.path.abspath(__file__)) module_path = os.path.dirname(os.path.abspath(__file__))
service_template_path = os.path.join(module_path, "res", service_template_path = os.path.join(module_path, "res", "klipper.service")
"klipper.service")
env_template_file_path = os.path.join(module_path, "res", "klipper.env") env_template_file_path = os.path.join(module_path, "res", "klipper.env")
service_file_name = self.get_service_file_name(extension=True) service_file_name = self.get_service_file_name(extension=True)
service_file_target = f"{SYSTEMD}/{service_file_name}" service_file_target = f"{SYSTEMD}/{service_file_name}"
env_file_target = os.path.abspath(f"{self.sysd_dir}/klipper.env") env_file_target = os.path.abspath(f"{self.sysd_dir}/klipper.env")
# create folder structure # create folder structure
dirs = [self.data_dir, self.cfg_dir, self.log_dir, dirs = [
self.comms_dir, self.sysd_dir] self.data_dir,
self.cfg_dir,
self.log_dir,
self.comms_dir,
self.sysd_dir,
]
for _dir in dirs: for _dir in dirs:
create_directory(Path(_dir)) create_directory(Path(_dir))
try: try:
# writing the klipper service file (requires sudo!) # writing the klipper service file (requires sudo!)
service_content = self._prep_service_file(service_template_path, service_content = self._prep_service_file(
env_file_target) service_template_path, env_file_target
)
command = ["sudo", "tee", service_file_target] command = ["sudo", "tee", service_file_target]
subprocess.run(command, input=service_content.encode(), subprocess.run(
stdout=subprocess.DEVNULL, check=True) command,
input=service_content.encode(),
stdout=subprocess.DEVNULL,
check=True,
)
Logger.print_ok(f"Service file created: {service_file_target}") Logger.print_ok(f"Service file created: {service_file_target}")
# writing the klipper.env file # writing the klipper.env file
@@ -73,11 +83,11 @@ class Klipper(BaseInstance):
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error( Logger.print_error(
f"Error creating service file {service_file_target}: {e}") f"Error creating service file {service_file_target}: {e}"
)
raise raise
except OSError as e: except OSError as e:
Logger.print_error( Logger.print_error(f"Error creating env file {env_file_target}: {e}")
f"Error creating env file {env_file_target}: {e}")
raise raise
def read(self) -> None: def read(self) -> None:
@@ -118,7 +128,7 @@ class Klipper(BaseInstance):
Logger.print_ok("Directories successfully deleted.") Logger.print_ok("Directories successfully deleted.")
def get_service_file_name(self, extension=False) -> str: def get_service_file_name(self, extension=False) -> str:
name = self.prefix if self.name is None else self.prefix + '-' + self.name name = self.prefix if self.name is None else self.prefix + "-" + self.name
return name if not extension else f"{name}.service" return name if not extension else f"{name}.service"
def _get_service_file_path(self): def _get_service_file_path(self):
@@ -138,11 +148,11 @@ class Klipper(BaseInstance):
template_content = template_file.read() template_content = template_file.read()
except FileNotFoundError: except FileNotFoundError:
Logger.print_error( Logger.print_error(
f"Unable to open {service_template_path} - File not found") f"Unable to open {service_template_path} - File not found"
)
raise raise
service_content = template_content.replace("%USER%", self.user) service_content = template_content.replace("%USER%", self.user)
service_content = service_content.replace("%KLIPPER_DIR%", service_content = service_content.replace("%KLIPPER_DIR%", self.klipper_dir)
self.klipper_dir)
service_content = service_content.replace("%ENV%", self.env_dir) service_content = service_content.replace("%ENV%", self.env_dir)
service_content = service_content.replace("%ENV_FILE%", env_file_path) service_content = service_content.replace("%ENV_FILE%", env_file_path)
return service_content return service_content
@@ -153,10 +163,12 @@ class Klipper(BaseInstance):
env_template_file_content = env_file.read() env_template_file_content = env_file.read()
except FileNotFoundError: except FileNotFoundError:
Logger.print_error( Logger.print_error(
f"Unable to open {env_template_file_path} - File not found") f"Unable to open {env_template_file_path} - File not found"
)
raise raise
env_file_content = env_template_file_content.replace("%KLIPPER_DIR%", env_file_content = env_template_file_content.replace(
self.klipper_dir) "%KLIPPER_DIR%", self.klipper_dir
)
env_file_content = env_file_content.replace("%CFG%", self.cfg_file) env_file_content = env_file_content.replace("%CFG%", self.cfg_file)
env_file_content = env_file_content.replace("%SERIAL%", self.serial) env_file_content = env_file_content.replace("%SERIAL%", self.serial)
env_file_content = env_file_content.replace("%LOG%", self.log) env_file_content = env_file_content.replace("%LOG%", self.log)

View File

@@ -19,15 +19,27 @@ from typing import Optional, List, Union
from kiauh.instance_manager.instance_manager import InstanceManager from kiauh.instance_manager.instance_manager import InstanceManager
from kiauh.modules.klipper.klipper import Klipper from kiauh.modules.klipper.klipper import Klipper
from kiauh.modules.klipper.klipper_utils import print_instance_overview, print_missing_usergroup_dialog from kiauh.modules.klipper.klipper_utils import (
print_instance_overview,
print_missing_usergroup_dialog,
)
from kiauh.utils.constants import CURRENT_USER, KLIPPER_DIR, KLIPPER_ENV_DIR from kiauh.utils.constants import CURRENT_USER, KLIPPER_DIR, KLIPPER_ENV_DIR
from kiauh.utils.input_utils import get_user_confirm, get_user_number_input, \ from kiauh.utils.input_utils import (
get_user_string_input, get_user_selection_input get_user_confirm,
get_user_number_input,
get_user_string_input,
get_user_selection_input,
)
from kiauh.utils.logger import Logger from kiauh.utils.logger import Logger
from kiauh.utils.system_utils import parse_packages_from_file, \ from kiauh.utils.system_utils import (
clone_repo, create_python_venv, \ parse_packages_from_file,
install_python_requirements, update_system_package_lists, \ clone_repo,
install_system_packages, mask_system_service create_python_venv,
install_python_requirements,
update_system_package_lists,
install_system_packages,
mask_system_service,
)
def run_klipper_setup(install: bool) -> None: def run_klipper_setup(install: bool) -> None:
@@ -81,8 +93,8 @@ def install_klipper(instance_manager: InstanceManager) -> None:
instance_list = instance_manager.get_instances() instance_list = instance_manager.get_instances()
if_adding = " additional" if len(instance_list) > 0 else "" if_adding = " additional" if len(instance_list) > 0 else ""
install_count = get_user_number_input( install_count = get_user_number_input(
f"Number of{if_adding} Klipper instances to set up", f"Number of{if_adding} Klipper instances to set up", 1, default=1
1, default=1) )
instance_names = set_instance_names(instance_list, install_count) instance_names = set_instance_names(instance_list, install_count)
@@ -132,8 +144,7 @@ def install_klipper_packages(klipper_dir: Path) -> None:
install_system_packages(packages) install_system_packages(packages)
def set_instance_names(instance_list, install_count: int) -> List[ def set_instance_names(instance_list, install_count: int) -> List[Union[str, None]]:
Union[str, None]]:
instance_count = len(instance_list) instance_count = len(instance_list)
# default single instance install # default single instance install
@@ -141,9 +152,11 @@ def set_instance_names(instance_list, install_count: int) -> List[
return [None] return [None]
# new multi instance install # new multi instance install
elif ((instance_count == 0 and install_count > 1) elif (
# or convert single instance install to multi instance install (instance_count == 0 and install_count > 1)
or (instance_count == 1 and install_count >= 1)): # or convert single instance install to multi instance install
or (instance_count == 1 and install_count >= 1)
):
if get_user_confirm("Assign custom names?", False): if get_user_confirm("Assign custom names?", False):
return assign_custom_names(instance_count, install_count, None) return assign_custom_names(instance_count, install_count, None)
else: else:
@@ -153,8 +166,7 @@ def set_instance_names(instance_list, install_count: int) -> List[
# existing multi instance install # existing multi instance install
elif instance_count > 1: elif instance_count > 1:
if has_custom_names(instance_list): if has_custom_names(instance_list):
return assign_custom_names(instance_count, install_count, return assign_custom_names(instance_count, install_count, instance_list)
instance_list)
else: else:
start = get_highest_index(instance_list) + 1 start = get_highest_index(instance_list) + 1
_range = range(start, start + install_count) _range = range(start, start + install_count)
@@ -170,8 +182,9 @@ def has_custom_names(instance_list: List[Klipper]) -> bool:
return False return False
def assign_custom_names(instance_count: int, install_count: int, def assign_custom_names(
instance_list: Optional[List[Klipper]]) -> List[str]: instance_count: int, install_count: int, instance_list: Optional[List[Klipper]]
) -> List[str]:
instance_names = [] instance_names = []
exclude = Klipper.blacklist() exclude = Klipper.blacklist()
@@ -190,7 +203,7 @@ def assign_custom_names(instance_count: int, install_count: int,
def get_highest_index(instance_list: List[Klipper]) -> int: def get_highest_index(instance_list: List[Klipper]) -> int:
indices = [int(instance.name.split('-')[-1]) for instance in instance_list] indices = [int(instance.name.split("-")[-1]) for instance in instance_list]
return max(indices) return max(indices)
@@ -209,14 +222,12 @@ def remove_single_instance(instance_manager: InstanceManager) -> None:
def remove_multi_instance(instance_manager: InstanceManager) -> None: def remove_multi_instance(instance_manager: InstanceManager) -> None:
instance_list = instance_manager.get_instances() instance_list = instance_manager.get_instances()
print_instance_overview(instance_list, show_index=True, print_instance_overview(instance_list, show_index=True, show_select_all=True)
show_select_all=True)
options = [str(i) for i in range(len(instance_list))] options = [str(i) for i in range(len(instance_list))]
options.extend(["a", "A", "b", "B"]) options.extend(["a", "A", "b", "B"])
selection = get_user_selection_input( selection = get_user_selection_input("Select Klipper instance to remove", options)
"Select Klipper instance to remove", options)
print(selection) print(selection)
if selection == "b".lower(): if selection == "b".lower():
@@ -231,7 +242,8 @@ def remove_multi_instance(instance_manager: InstanceManager) -> None:
else: else:
instance = instance_list[int(selection)] instance = instance_list[int(selection)]
Logger.print_info( Logger.print_info(
f"Removing Klipper instance: {instance.get_service_file_name()}") f"Removing Klipper instance: {instance.get_service_file_name()}"
)
instance_manager.set_current_instance(instance) instance_manager.set_current_instance(instance)
instance_manager.stop_instance() instance_manager.stop_instance()
instance_manager.disable_instance() instance_manager.disable_instance()
@@ -254,7 +266,9 @@ def check_user_groups():
print_missing_usergroup_dialog(missing_groups) print_missing_usergroup_dialog(missing_groups)
if not get_user_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"): if not get_user_confirm(f"Add user '{CURRENT_USER}' to group(s) now?"):
Logger.warn("Skipped adding user to required groups. You might encounter issues.") Logger.warn(
"Skipped adding user to required groups. You might encounter issues."
)
return return
try: try:
@@ -267,13 +281,19 @@ def check_user_groups():
Logger.print_error(f"Unable to add user to usergroups: {e}") Logger.print_error(f"Unable to add user to usergroups: {e}")
raise raise
Logger.print_warn("Remember to relog/restart this machine for the group(s) to be applied!") Logger.print_warn(
"Remember to relog/restart this machine for the group(s) to be applied!"
)
def handle_disruptive_system_packages() -> None: def handle_disruptive_system_packages() -> None:
services = [] services = []
brltty_status = subprocess.run(["systemctl", "is-enabled", "brltty"], capture_output=True, text=True) brltty_status = subprocess.run(
modem_manager_status = subprocess.run(["systemctl", "is-enabled", "ModemManager"], capture_output=True, text=True) ["systemctl", "is-enabled", "brltty"], capture_output=True, text=True
)
modem_manager_status = subprocess.run(
["systemctl", "is-enabled", "ModemManager"], capture_output=True, text=True
)
if "enabled" in brltty_status.stdout: if "enabled" in brltty_status.stdout:
services.append("brltty") services.append("brltty")
@@ -282,13 +302,17 @@ def handle_disruptive_system_packages() -> None:
for service in services if services else []: for service in services if services else []:
try: try:
Logger.print_info(f"{service} service detected! Masking {service} service ...") Logger.print_info(
f"{service} service detected! Masking {service} service ..."
)
mask_system_service(service) mask_system_service(service)
Logger.print_ok(f"{service} service masked!") Logger.print_ok(f"{service} service masked!")
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
warn_msg = textwrap.dedent(f""" warn_msg = textwrap.dedent(
f"""
KIAUH was unable to mask the {service} system service. KIAUH was unable to mask the {service} system service.
Please fix the problem manually. Otherwise, this may have Please fix the problem manually. Otherwise, this may have
undesirable effects on the operation of Klipper. undesirable effects on the operation of Klipper.
""")[1:] """
)[1:]
Logger.print_warn(warn_msg) Logger.print_warn(warn_msg)

View File

@@ -13,12 +13,12 @@ from typing import List
from kiauh.instance_manager.base_instance import BaseInstance from kiauh.instance_manager.base_instance import BaseInstance
from kiauh.menus.base_menu import print_back_footer from kiauh.menus.base_menu import print_back_footer
from kiauh.utils.constants import COLOR_GREEN, COLOR_CYAN, COLOR_YELLOW, \ from kiauh.utils.constants import COLOR_GREEN, COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
RESET_FORMAT
def print_instance_overview(instances: List[BaseInstance], show_index=False, def print_instance_overview(
show_select_all=False): instances: List[BaseInstance], show_index=False, show_select_all=False
):
headline = f"{COLOR_GREEN}The following Klipper instances were found:{RESET_FORMAT}" headline = f"{COLOR_GREEN}The following Klipper instances were found:{RESET_FORMAT}"
print("/=======================================================\\") print("/=======================================================\\")
@@ -38,13 +38,20 @@ def print_instance_overview(instances: List[BaseInstance], show_index=False,
print_back_footer() print_back_footer()
def print_missing_usergroup_dialog(missing_groups) -> None: def print_missing_usergroup_dialog(missing_groups) -> None:
print("/=======================================================\\") print("/=======================================================\\")
print(f"| {COLOR_YELLOW}WARNING: Your current user is not in group:{RESET_FORMAT} |") print(
f"| {COLOR_YELLOW}WARNING: Your current user is not in group:{RESET_FORMAT} |"
)
if "tty" in missing_groups: if "tty" in missing_groups:
print(f"| {COLOR_CYAN}● tty{RESET_FORMAT} |") print(
f"| {COLOR_CYAN}● tty{RESET_FORMAT} |"
)
if "dialout" in missing_groups: if "dialout" in missing_groups:
print(f"| {COLOR_CYAN}● dialout{RESET_FORMAT} |") print(
f"| {COLOR_CYAN}● dialout{RESET_FORMAT} |"
)
print("| |") print("| |")
print("| It is possible that you won't be able to successfully |") print("| It is possible that you won't be able to successfully |")
print("| connect and/or flash the controller board without |") print("| connect and/or flash the controller board without |")
@@ -52,6 +59,10 @@ def print_missing_usergroup_dialog(missing_groups) -> None:
print("| If you want to add the current user to the group(s) |") print("| If you want to add the current user to the group(s) |")
print("| listed above, answer with 'Y'. Else skip with 'n'. |") print("| listed above, answer with 'Y'. Else skip with 'n'. |")
print("| |") print("| |")
print(f"| {COLOR_YELLOW}INFO:{RESET_FORMAT} |") print(
print(f"| {COLOR_YELLOW}Relog required for group assignments to take effect!{RESET_FORMAT} |") f"| {COLOR_YELLOW}INFO:{RESET_FORMAT} |"
)
print(
f"| {COLOR_YELLOW}Relog required for group assignments to take effect!{RESET_FORMAT} |"
)
print("\\=======================================================/") print("\\=======================================================/")

View File

@@ -11,8 +11,8 @@
from typing import Optional, List from typing import Optional, List
from kiauh.utils.logger import Logger
from kiauh.utils.constants import COLOR_CYAN, RESET_FORMAT from kiauh.utils.constants import COLOR_CYAN, RESET_FORMAT
from kiauh.utils.logger import Logger
def get_user_confirm(question: str, default_choice=True) -> bool: def get_user_confirm(question: str, default_choice=True) -> bool:
@@ -30,7 +30,8 @@ def get_user_confirm(question: str, default_choice=True) -> bool:
choice = ( choice = (
input(f"{COLOR_CYAN}###### {question} {def_choice} {RESET_FORMAT}") input(f"{COLOR_CYAN}###### {question} {def_choice} {RESET_FORMAT}")
.strip() .strip()
.lower()) .lower()
)
if choice in options_confirm: if choice in options_confirm:
return True return True
@@ -40,8 +41,9 @@ def get_user_confirm(question: str, default_choice=True) -> bool:
Logger.print_error("Invalid choice. Please select 'y' or 'n'.") Logger.print_error("Invalid choice. Please select 'y' or 'n'.")
def get_user_number_input(question: str, min_count: int, max_count=None, def get_user_number_input(
default=None) -> int: question: str, min_count: int, max_count=None, default=None
) -> int:
_question = question + f" (default={default})" if default else question _question = question + f" (default={default})" if default else question
_question = f"{COLOR_CYAN}###### {_question}: {RESET_FORMAT}" _question = f"{COLOR_CYAN}###### {_question}: {RESET_FORMAT}"
while True: while True:
@@ -65,8 +67,7 @@ def get_user_number_input(question: str, min_count: int, max_count=None,
def get_user_string_input(question: str, exclude=Optional[List]) -> str: def get_user_string_input(question: str, exclude=Optional[List]) -> str:
while True: while True:
_input = (input(f"{COLOR_CYAN}###### {question}: {RESET_FORMAT}") _input = input(f"{COLOR_CYAN}###### {question}: {RESET_FORMAT}").strip()
.strip())
if _input.isalnum() and _input not in exclude: if _input.isalnum() and _input not in exclude:
return _input return _input
@@ -78,8 +79,7 @@ def get_user_string_input(question: str, exclude=Optional[List]) -> str:
def get_user_selection_input(question: str, option_list: List) -> str: def get_user_selection_input(question: str, option_list: List) -> str:
while True: while True:
_input = (input(f"{COLOR_CYAN}###### {question}: {RESET_FORMAT}") _input = input(f"{COLOR_CYAN}###### {question}: {RESET_FORMAT}").strip()
.strip())
if _input in option_list: if _input in option_list:
return _input return _input

View File

@@ -9,12 +9,16 @@
# This file may be distributed under the terms of the GNU GPLv3 license # # This file may be distributed under the terms of the GNU GPLv3 license #
# ======================================================================= # # ======================================================================= #
from kiauh.utils.constants import COLOR_GREEN, COLOR_YELLOW, COLOR_RED, \ from kiauh.utils.constants import (
COLOR_MAGENTA, RESET_FORMAT COLOR_GREEN,
COLOR_YELLOW,
COLOR_RED,
COLOR_MAGENTA,
RESET_FORMAT,
)
class Logger: class Logger:
@staticmethod @staticmethod
def info(msg): def info(msg):
# log to kiauh.log # log to kiauh.log

View File

@@ -57,7 +57,8 @@ def clone_repo(target_dir: Path, url: str, branch: str) -> None:
print("Error cloning repository:", e.output.decode()) print("Error cloning repository:", e.output.decode())
else: else:
overwrite_target = get_user_confirm( overwrite_target = get_user_confirm(
"Target directory already exists. Overwrite?") "Target directory already exists. Overwrite?"
)
if overwrite_target: if overwrite_target:
try: try:
shutil.rmtree(target_dir) shutil.rmtree(target_dir)
@@ -71,13 +72,13 @@ def clone_repo(target_dir: Path, url: str, branch: str) -> None:
def parse_packages_from_file(source_file) -> List[str]: def parse_packages_from_file(source_file) -> List[str]:
packages = [] packages = []
print("Reading dependencies...") print("Reading dependencies...")
with open(source_file, 'r') as file: with open(source_file, "r") as file:
for line in file: for line in file:
line = line.strip() line = line.strip()
if line.startswith("PKGLIST="): if line.startswith("PKGLIST="):
line = line.replace("\"", "") line = line.replace('"', "")
line = line.replace("PKGLIST=", "") line = line.replace("PKGLIST=", "")
line = line.replace('${PKGLIST}', '') line = line.replace("${PKGLIST}", "")
packages.extend(line.split()) packages.extend(line.split())
return packages return packages
@@ -97,16 +98,15 @@ def create_python_venv(target: Path) -> None:
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print("Error setting up virtualenv:", e.output.decode()) print("Error setting up virtualenv:", e.output.decode())
else: else:
overwrite_venv = get_user_confirm( overwrite_venv = get_user_confirm("Virtualenv already exists. Re-create?")
"Virtualenv already exists. Re-create?")
if overwrite_venv: if overwrite_venv:
try: try:
shutil.rmtree(target) shutil.rmtree(target)
create_python_venv(target) create_python_venv(target)
except OSError as e: except OSError as e:
Logger.print_error( Logger.print_error(
f"Error removing existing virtualenv: {e.strerror}", f"Error removing existing virtualenv: {e.strerror}", False
False) )
else: else:
print("Skipping re-creation of virtualenv ...") print("Skipping re-creation of virtualenv ...")
@@ -144,10 +144,7 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
def update_system_package_lists(silent: bool, rls_info_change=False) -> None: def update_system_package_lists(silent: bool, rls_info_change=False) -> None:
cache_mtime = 0 cache_mtime = 0
cache_files = [ cache_files = ["/var/lib/apt/periodic/update-success-stamp", "/var/lib/apt/lists"]
"/var/lib/apt/periodic/update-success-stamp",
"/var/lib/apt/lists"
]
for cache_file in cache_files: for cache_file in cache_files:
if Path(cache_file).exists(): if Path(cache_file).exists():
cache_mtime = max(cache_mtime, os.path.getmtime(cache_file)) cache_mtime = max(cache_mtime, os.path.getmtime(cache_file))
@@ -196,8 +193,7 @@ def create_directory(_dir: Path) -> None:
os.makedirs(_dir, exist_ok=True) os.makedirs(_dir, exist_ok=True)
Logger.print_ok("Directory created!") Logger.print_ok("Directory created!")
else: else:
Logger.print_info( Logger.print_info(f"Directory already exists: {_dir}\nSkip creation ...")
f"Directory already exists: {_dir}\nSkip creation ...")
except OSError as e: except OSError as e:
Logger.print_error(f"Error creating folder: {e}") Logger.print_error(f"Error creating folder: {e}")
raise raise
@@ -208,5 +204,7 @@ def mask_system_service(service_name: str) -> None:
command = ["sudo", "systemctl", "mask", service_name] command = ["sudo", "systemctl", "mask", service_name]
subprocess.run(command, stderr=subprocess.PIPE, check=True) subprocess.run(command, stderr=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
Logger.print_error(f"Unable to mask system service {service_name}: {e.stderr.decode()}") Logger.print_error(
f"Unable to mask system service {service_name}: {e.stderr.decode()}"
)
raise raise

13
pyproject.toml Normal file
View File

@@ -0,0 +1,13 @@
[tool.black]
line-length = 88
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
(
\.git/
| \.github/
| docs/
| resources/
| scripts/
)
'''