feat: add mypy to the project

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
This commit is contained in:
dw-0
2024-08-04 16:15:59 +02:00
parent 0d7a471a03
commit 96daf966ee
55 changed files with 513 additions and 430 deletions

View File

@@ -6,6 +6,8 @@
# # # #
# 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 __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from subprocess import CalledProcessError, run from subprocess import CalledProcessError, run
@@ -30,13 +32,13 @@ from utils.logger import Logger
class Klipper(BaseInstance): class Klipper(BaseInstance):
klipper_dir: Path = KLIPPER_DIR klipper_dir: Path = KLIPPER_DIR
env_dir: Path = KLIPPER_ENV_DIR env_dir: Path = KLIPPER_ENV_DIR
cfg_file: Path = None cfg_file: Path | None = None
log: Path = None log: Path | None = None
serial: Path = None serial: Path | None = None
uds: Path = None uds: Path | None = None
def __init__(self, suffix: str = "") -> None: def __init__(self, suffix: str = "") -> None:
super().__init__(instance_type=self, suffix=suffix) super().__init__(suffix=suffix)
def __post_init__(self) -> None: def __post_init__(self) -> None:
super().__post_init__() super().__post_init__()
@@ -132,15 +134,15 @@ class Klipper(BaseInstance):
) )
env_file_content = env_file_content.replace( env_file_content = env_file_content.replace(
"%SERIAL%", "%SERIAL%",
self.serial.as_posix(), self.serial.as_posix() if self.serial else "",
) )
env_file_content = env_file_content.replace( env_file_content = env_file_content.replace(
"%LOG%", "%LOG%",
self.log.as_posix(), self.log.as_posix() if self.log else "",
) )
env_file_content = env_file_content.replace( env_file_content = env_file_content.replace(
"%UDS%", "%UDS%",
self.uds.as_posix(), self.uds.as_posix() if self.uds else "",
) )
return env_file_content return env_file_content

View File

@@ -34,7 +34,7 @@ def print_instance_overview(
show_index=False, show_index=False,
start_index=0, start_index=0,
show_select_all=False, show_select_all=False,
): ) -> None:
dialog = "╔═══════════════════════════════════════════════════════╗\n" dialog = "╔═══════════════════════════════════════════════════════╗\n"
if show_headline: if show_headline:
d_type = ( d_type = (
@@ -64,7 +64,7 @@ def print_instance_overview(
print_back_footer() print_back_footer()
def print_select_instance_count_dialog(): def print_select_instance_count_dialog() -> None:
line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}" line1 = f"{COLOR_YELLOW}WARNING:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}Setting up too many instances may crash your system.{RESET_FORMAT}" line2 = f"{COLOR_YELLOW}Setting up too many instances may crash your system.{RESET_FORMAT}"
dialog = textwrap.dedent( dialog = textwrap.dedent(
@@ -84,7 +84,7 @@ def print_select_instance_count_dialog():
print_back_footer() print_back_footer()
def print_select_custom_name_dialog(): def print_select_custom_name_dialog() -> None:
line1 = f"{COLOR_YELLOW}INFO:{RESET_FORMAT}" line1 = f"{COLOR_YELLOW}INFO:{RESET_FORMAT}"
line2 = f"{COLOR_YELLOW}Only alphanumeric characters are allowed!{RESET_FORMAT}" line2 = f"{COLOR_YELLOW}Only alphanumeric characters are allowed!{RESET_FORMAT}"
dialog = textwrap.dedent( dialog = textwrap.dedent(

View File

@@ -6,8 +6,9 @@
# # # #
# 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 __future__ import annotations
from typing import List, Union from typing import List
from components.klipper import KLIPPER_DIR, KLIPPER_ENV_DIR from components.klipper import KLIPPER_DIR, KLIPPER_ENV_DIR
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
@@ -47,9 +48,7 @@ def run_klipper_removal(
run_remove_routines(KLIPPER_ENV_DIR) run_remove_routines(KLIPPER_ENV_DIR)
def select_instances_to_remove( def select_instances_to_remove(instances: List[Klipper]) -> List[Klipper] | None:
instances: List[Klipper],
) -> Union[List[Klipper], None]:
start_index = 1 start_index = 1
options = [str(i + start_index) for i in range(len(instances))] options = [str(i + start_index) for i in range(len(instances))]
options.extend(["a", "b"]) options.extend(["a", "b"])
@@ -76,7 +75,7 @@ def select_instances_to_remove(
def remove_instances( def remove_instances(
instance_manager: InstanceManager, instance_manager: InstanceManager,
instance_list: List[Klipper], instance_list: List[Klipper] | None,
) -> None: ) -> None:
if not instance_list: if not instance_list:
return return

View File

@@ -129,10 +129,10 @@ def handle_instance_names(
def get_install_count_and_name_dict( def get_install_count_and_name_dict(
klipper_list: List[Klipper], moonraker_list: List[Moonraker] klipper_list: List[Klipper], moonraker_list: List[Moonraker]
) -> Tuple[int, Dict[int, str]]: ) -> Tuple[int, Dict[int, str]]:
install_count: int | None
if len(moonraker_list) > len(klipper_list): if len(moonraker_list) > len(klipper_list):
install_count = len(moonraker_list) install_count = len(moonraker_list)
name_dict = {i: moonraker.suffix for i, moonraker in enumerate(moonraker_list)} name_dict = {i: moonraker.suffix for i, moonraker in enumerate(moonraker_list)}
else: else:
install_count = get_install_count() install_count = get_install_count()
name_dict = {i: klipper.suffix for i, klipper in enumerate(klipper_list)} name_dict = {i: klipper.suffix for i, klipper in enumerate(klipper_list)}
@@ -217,11 +217,12 @@ def create_klipper_instance(name: str, create_example_cfg: bool) -> None:
def use_custom_names_or_go_back() -> bool | None: def use_custom_names_or_go_back() -> bool | None:
print_select_custom_name_dialog() print_select_custom_name_dialog()
return get_confirm( _input: bool | None = get_confirm(
"Assign custom names?", "Assign custom names?",
False, False,
allow_go_back=True, allow_go_back=True,
) )
return _input
def display_moonraker_info(moonraker_list: List[Moonraker]) -> bool: def display_moonraker_info(moonraker_list: List[Moonraker]) -> bool:
@@ -238,4 +239,5 @@ def display_moonraker_info(moonraker_list: List[Moonraker]) -> bool:
padding_top=0, padding_top=0,
padding_bottom=0, padding_bottom=0,
) )
return get_confirm("Proceed with installation?") _input: bool = get_confirm("Proceed with installation?")
return _input

View File

@@ -46,10 +46,11 @@ def get_klipper_status() -> ComponentStatus:
return get_install_status(KLIPPER_DIR, KLIPPER_ENV_DIR, Klipper) return get_install_status(KLIPPER_DIR, KLIPPER_ENV_DIR, Klipper)
def add_to_existing() -> bool: def add_to_existing() -> bool | None:
kl_instances = InstanceManager(Klipper).instances kl_instances: List[Klipper] = InstanceManager(Klipper).instances
print_instance_overview(kl_instances) print_instance_overview(kl_instances)
return get_confirm("Add new instances?", allow_go_back=True) _input: bool | None = get_confirm("Add new instances?", allow_go_back=True)
return _input
def get_install_count() -> int | None: def get_install_count() -> int | None:
@@ -66,7 +67,8 @@ def get_install_count() -> int | None:
f"{' additional' if len(kl_instances) > 0 else ''} " f"{' additional' if len(kl_instances) > 0 else ''} "
f"Klipper instances to set up" f"Klipper instances to set up"
) )
return get_number_input(question, 1, default=1, allow_go_back=True) _input: int | None = get_number_input(question, 1, default=1, allow_go_back=True)
return _input
def assign_custom_name(key: int, name_dict: Dict[int, str]) -> None: def assign_custom_name(key: int, name_dict: Dict[int, str]) -> None:
@@ -79,7 +81,7 @@ def assign_custom_name(key: int, name_dict: Dict[int, str]) -> None:
name_dict[key] = get_string_input(question, exclude=existing_names, regex=pattern) name_dict[key] = get_string_input(question, exclude=existing_names, regex=pattern)
def check_user_groups(): def check_user_groups() -> None:
user_groups = [grp.getgrgid(gid).gr_name for gid in os.getgroups()] user_groups = [grp.getgrgid(gid).gr_name for gid in os.getgroups()]
missing_groups = [g for g in ["tty", "dialout"] if g not in user_groups] missing_groups = [g for g in ["tty", "dialout"] if g not in user_groups]

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.klipper import klipper_remove from components.klipper import klipper_remove
from core.menus import FooterType, Option from core.menus import FooterType, Option
@@ -18,21 +19,19 @@ from utils.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
class KlipperRemoveMenu(BaseMenu): class KlipperRemoveMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.footer_type = FooterType.BACK self.footer_type = FooterType.BACK
self.remove_klipper_service = False self.remove_klipper_service = False
self.remove_klipper_dir = False self.remove_klipper_dir = False
self.remove_klipper_env = False self.remove_klipper_env = False
self.selection_state = False self.selection_state = False
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.remove_menu import RemoveMenu from core.menus.remove_menu import RemoveMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else RemoveMenu
previous_menu if previous_menu is not None else RemoveMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {

View File

@@ -24,12 +24,12 @@ from utils.sys_utils import log_process
def find_firmware_file() -> bool: def find_firmware_file() -> bool:
target = KLIPPER_DIR.joinpath("out") target = KLIPPER_DIR.joinpath("out")
target_exists = target.exists() target_exists: bool = target.exists()
f1 = "klipper.elf.hex" f1 = "klipper.elf.hex"
f2 = "klipper.elf" f2 = "klipper.elf"
f3 = "klipper.bin" f3 = "klipper.bin"
fw_file_exists = ( fw_file_exists: bool = (
target.joinpath(f1).exists() and target.joinpath(f2).exists() target.joinpath(f1).exists() and target.joinpath(f2).exists()
) or target.joinpath(f3).exists() ) or target.joinpath(f3).exists()
@@ -75,10 +75,11 @@ def get_sd_flash_board_list() -> List[str]:
try: try:
cmd = f"{SD_FLASH_SCRIPT} -l" cmd = f"{SD_FLASH_SCRIPT} -l"
blist = check_output(cmd, shell=True, text=True) blist: List[str] = check_output(cmd, shell=True, text=True).splitlines()[1:]
return blist.splitlines()[1:] return blist
except CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"An unexpected error occured:\n{e}") Logger.print_error(f"An unexpected error occured:\n{e}")
return []
def start_flash_process(flash_options: FlashOptions) -> None: def start_flash_process(flash_options: FlashOptions) -> None:

View File

@@ -45,7 +45,7 @@ class FlashOptions:
return cls._instance return cls._instance
@classmethod @classmethod
def destroy(cls): def destroy(cls) -> None:
cls._instance = None cls._instance = None
@property @property

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.klipper import KLIPPER_DIR from components.klipper import KLIPPER_DIR
from components.klipper_firmware.firmware_utils import ( from components.klipper_firmware.firmware_utils import (
@@ -30,16 +31,16 @@ from utils.sys_utils import (
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperBuildFirmwareMenu(BaseMenu): class KlipperBuildFirmwareMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.deps = ["build-essential", "dpkg-dev", "make"] self.deps = ["build-essential", "dpkg-dev", "make"]
self.missing_deps = check_package_install(self.deps) self.missing_deps = check_package_install(self.deps)
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.advanced_menu import AdvancedMenu from core.menus.advanced_menu import AdvancedMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else AdvancedMenu previous_menu if previous_menu is not None else AdvancedMenu
) )
@@ -109,4 +110,5 @@ class KlipperBuildFirmwareMenu(BaseMenu):
Logger.print_error("Building Klipper Firmware failed!") Logger.print_error("Building Klipper Firmware failed!")
finally: finally:
if self.previous_menu is not None:
self.previous_menu().run() self.previous_menu().run()

View File

@@ -6,8 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.klipper_firmware.flash_options import FlashMethod, FlashOptions from components.klipper_firmware.flash_options import FlashMethod, FlashOptions
from core.menus import FooterType, Option from core.menus import FooterType, Option
@@ -18,15 +20,15 @@ from utils.constants import COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperNoFirmwareErrorMenu(BaseMenu): class KlipperNoFirmwareErrorMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.footer_type = FooterType.BLANK self.footer_type = FooterType.BLANK
self.input_label_txt = "Press ENTER to go back to [Advanced Menu]" self.input_label_txt = "Press ENTER to go back to [Advanced Menu]"
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu = previous_menu self.previous_menu = previous_menu
def set_options(self) -> None: def set_options(self) -> None:
@@ -67,13 +69,13 @@ class KlipperNoFirmwareErrorMenu(BaseMenu):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperNoBoardTypesErrorMenu(BaseMenu): class KlipperNoBoardTypesErrorMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.footer_type = FooterType.BLANK self.footer_type = FooterType.BLANK
self.input_label_txt = "Press ENTER to go back to [Main Menu]" self.input_label_txt = "Press ENTER to go back to [Main Menu]"
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu = previous_menu self.previous_menu = previous_menu
def set_options(self) -> None: def set_options(self) -> None:

View File

@@ -6,8 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
@@ -15,16 +17,16 @@ from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
# noinspection DuplicatedCode # noinspection DuplicatedCode
class KlipperFlashMethodHelpMenu(BaseMenu): class KlipperFlashMethodHelpMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from components.klipper_firmware.menus.klipper_flash_menu import ( from components.klipper_firmware.menus.klipper_flash_menu import (
KlipperFlashMethodMenu, KlipperFlashMethodMenu,
) )
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else KlipperFlashMethodMenu previous_menu if previous_menu is not None else KlipperFlashMethodMenu
) )
@@ -73,16 +75,16 @@ class KlipperFlashMethodHelpMenu(BaseMenu):
# noinspection DuplicatedCode # noinspection DuplicatedCode
class KlipperFlashCommandHelpMenu(BaseMenu): class KlipperFlashCommandHelpMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from components.klipper_firmware.menus.klipper_flash_menu import ( from components.klipper_firmware.menus.klipper_flash_menu import (
KlipperFlashCommandMenu, KlipperFlashCommandMenu,
) )
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else KlipperFlashCommandMenu previous_menu if previous_menu is not None else KlipperFlashCommandMenu
) )
@@ -117,16 +119,16 @@ class KlipperFlashCommandHelpMenu(BaseMenu):
# noinspection DuplicatedCode # noinspection DuplicatedCode
class KlipperMcuConnectionHelpMenu(BaseMenu): class KlipperMcuConnectionHelpMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from components.klipper_firmware.menus.klipper_flash_menu import ( from components.klipper_firmware.menus.klipper_flash_menu import (
KlipperSelectMcuConnectionMenu, KlipperSelectMcuConnectionMenu,
) )
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu previous_menu
if previous_menu is not None if previous_menu is not None
else KlipperSelectMcuConnectionMenu else KlipperSelectMcuConnectionMenu

View File

@@ -6,10 +6,11 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
import time import time
from typing import Optional, Type from typing import Type
from components.klipper_firmware.firmware_utils import ( from components.klipper_firmware.firmware_utils import (
find_firmware_file, find_firmware_file,
@@ -44,17 +45,17 @@ from utils.logger import DialogType, Logger
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperFlashMethodMenu(BaseMenu): class KlipperFlashMethodMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.help_menu = KlipperFlashMethodHelpMenu self.help_menu = KlipperFlashMethodHelpMenu
self.input_label_txt = "Select flash method" self.input_label_txt = "Select flash method"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.advanced_menu import AdvancedMenu from core.menus.advanced_menu import AdvancedMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else AdvancedMenu previous_menu if previous_menu is not None else AdvancedMenu
) )
@@ -108,15 +109,15 @@ class KlipperFlashMethodMenu(BaseMenu):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperFlashCommandMenu(BaseMenu): class KlipperFlashCommandMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.help_menu = KlipperFlashCommandHelpMenu self.help_menu = KlipperFlashCommandHelpMenu
self.input_label_txt = "Select flash command" self.input_label_txt = "Select flash command"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else KlipperFlashMethodMenu previous_menu if previous_menu is not None else KlipperFlashMethodMenu
) )
@@ -156,18 +157,18 @@ class KlipperFlashCommandMenu(BaseMenu):
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperSelectMcuConnectionMenu(BaseMenu): class KlipperSelectMcuConnectionMenu(BaseMenu):
def __init__( def __init__(
self, previous_menu: Optional[Type[BaseMenu]] = None, standalone: bool = False self, previous_menu: Type[BaseMenu] | None = None, standalone: bool = False
): ):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.__standalone = standalone self.__standalone = standalone
self.help_menu = KlipperMcuConnectionHelpMenu self.help_menu = KlipperMcuConnectionHelpMenu
self.input_label_txt = "Select connection type" self.input_label_txt = "Select connection type"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else KlipperFlashCommandMenu previous_menu if previous_menu is not None else KlipperFlashCommandMenu
) )
@@ -243,15 +244,15 @@ class KlipperSelectMcuConnectionMenu(BaseMenu):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperSelectMcuIdMenu(BaseMenu): class KlipperSelectMcuIdMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.mcu_list = self.flash_options.mcu_list self.mcu_list = self.flash_options.mcu_list
self.input_label_txt = "Select MCU to flash" self.input_label_txt = "Select MCU to flash"
self.footer_type = FooterType.BACK_HELP self.footer_type = FooterType.BACK_HELP
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu previous_menu
if previous_menu is not None if previous_menu is not None
else KlipperSelectMcuConnectionMenu else KlipperSelectMcuConnectionMenu
@@ -288,7 +289,12 @@ class KlipperSelectMcuIdMenu(BaseMenu):
print(menu, end="\n") print(menu, end="\n")
def flash_mcu(self, **kwargs): def flash_mcu(self, **kwargs):
index = int(kwargs.get("opt_index")) try:
index: int | None = kwargs.get("opt_index", None)
if index is None:
raise Exception("opt_index is None")
index = int(index)
selected_mcu = self.mcu_list[index] selected_mcu = self.mcu_list[index]
self.flash_options.selected_mcu = selected_mcu self.flash_options.selected_mcu = selected_mcu
@@ -296,19 +302,22 @@ class KlipperSelectMcuIdMenu(BaseMenu):
KlipperSelectSDFlashBoardMenu(previous_menu=self.__class__).run() KlipperSelectSDFlashBoardMenu(previous_menu=self.__class__).run()
elif self.flash_options.flash_method == FlashMethod.REGULAR: elif self.flash_options.flash_method == FlashMethod.REGULAR:
KlipperFlashOverviewMenu(previous_menu=self.__class__).run() KlipperFlashOverviewMenu(previous_menu=self.__class__).run()
except Exception as e:
Logger.print_error(e)
Logger.print_error("Flashing failed!")
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperSelectSDFlashBoardMenu(BaseMenu): class KlipperSelectSDFlashBoardMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.available_boards = get_sd_flash_board_list() self.available_boards = get_sd_flash_board_list()
self.input_label_txt = "Select board type" self.input_label_txt = "Select board type"
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else KlipperSelectMcuIdMenu previous_menu if previous_menu is not None else KlipperSelectMcuIdMenu
) )
@@ -340,9 +349,17 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
print(menu, end="") print(menu, end="")
def board_select(self, **kwargs): def board_select(self, **kwargs):
board = int(kwargs.get("opt_index")) try:
self.flash_options.selected_board = self.available_boards[board] index: int | None = kwargs.get("opt_index", None)
if index is None:
raise Exception("opt_index is None")
index = int(index)
self.flash_options.selected_board = self.available_boards[index]
self.baudrate_select() self.baudrate_select()
except Exception as e:
Logger.print_error(e)
Logger.print_error("Board selection failed!")
def baudrate_select(self, **kwargs): def baudrate_select(self, **kwargs):
Logger.print_dialog( Logger.print_dialog(
@@ -366,13 +383,13 @@ class KlipperSelectSDFlashBoardMenu(BaseMenu):
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class KlipperFlashOverviewMenu(BaseMenu): class KlipperFlashOverviewMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.flash_options = FlashOptions() self.flash_options = FlashOptions()
self.input_label_txt = "Perform action (default=Y)" self.input_label_txt = "Perform action (default=Y)"
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {

View File

@@ -6,29 +6,29 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.log_uploads.log_upload_utils import get_logfile_list, upload_logfile from components.log_uploads.log_upload_utils import get_logfile_list, upload_logfile
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
from utils.constants import COLOR_YELLOW, RESET_FORMAT from utils.constants import COLOR_YELLOW, RESET_FORMAT
from utils.logger import Logger
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class LogUploadMenu(BaseMenu): class LogUploadMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.logfile_list = get_logfile_list() self.logfile_list = get_logfile_list()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -36,7 +36,7 @@ class LogUploadMenu(BaseMenu):
for index in range(len(self.logfile_list)) for index in range(len(self.logfile_list))
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ Log Upload ] " header = " [ Log Upload ] "
color = COLOR_YELLOW color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -58,5 +58,13 @@ class LogUploadMenu(BaseMenu):
print(menu, end="") print(menu, end="")
def upload(self, **kwargs): def upload(self, **kwargs):
index = int(kwargs.get("opt_index")) try:
index: int | None = kwargs.get("opt_index", None)
if index is None:
raise Exception("opt_index is None")
index = int(index)
upload_logfile(self.logfile_list[index]) upload_logfile(self.logfile_list[index])
except Exception as e:
Logger.print_error(e)
Logger.print_error("Log upload failed!")

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.moonraker import moonraker_remove from components.moonraker import moonraker_remove
from core.menus import Option from core.menus import Option
@@ -18,21 +19,19 @@ from utils.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
class MoonrakerRemoveMenu(BaseMenu): class MoonrakerRemoveMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.remove_moonraker_service = False self.remove_moonraker_service = False
self.remove_moonraker_dir = False self.remove_moonraker_dir = False
self.remove_moonraker_env = False self.remove_moonraker_env = False
self.remove_moonraker_polkit = False self.remove_moonraker_polkit = False
self.selection_state = False self.selection_state = False
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.remove_menu import RemoveMenu from core.menus.remove_menu import RemoveMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else RemoveMenu
previous_menu if previous_menu is not None else RemoveMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {

View File

@@ -33,15 +33,15 @@ from utils.logger import Logger
class Moonraker(BaseInstance): class Moonraker(BaseInstance):
moonraker_dir: Path = MOONRAKER_DIR moonraker_dir: Path = MOONRAKER_DIR
env_dir: Path = MOONRAKER_ENV_DIR env_dir: Path = MOONRAKER_ENV_DIR
cfg_file: Path = None cfg_file: Path | None = None
port: int = None port: int | None = None
backup_dir: Path = None backup_dir: Path | None = None
certs_dir: Path = None certs_dir: Path | None = None
db_dir: Path = None db_dir: Path | None = None
log: Path = None log: Path | None = None
def __init__(self, suffix: str = ""): def __init__(self, suffix: str = ""):
super().__init__(instance_type=self, suffix=suffix) super().__init__(suffix=suffix)
def __post_init__(self) -> None: def __post_init__(self) -> None:
super().__post_init__() super().__post_init__()
@@ -140,11 +140,11 @@ class Moonraker(BaseInstance):
return env_file_content return env_file_content
def _get_port(self) -> int | None: def _get_port(self) -> int | None:
if not self.cfg_file.is_file(): if not self.cfg_file or not self.cfg_file.is_file():
return None return None
scp = SimpleConfigParser() scp = SimpleConfigParser()
scp.read(self.cfg_file) scp.read(self.cfg_file)
port = scp.getint("server", "port", fallback=None) port: int | None = scp.getint("server", "port", fallback=None)
return port return port

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
from subprocess import DEVNULL, PIPE, CalledProcessError, run from subprocess import DEVNULL, PIPE, CalledProcessError, run
from typing import List, Union from typing import List
from components.klipper.klipper_dialogs import print_instance_overview from components.klipper.klipper_dialogs import print_instance_overview
from components.moonraker import MOONRAKER_DIR, MOONRAKER_ENV_DIR from components.moonraker import MOONRAKER_DIR, MOONRAKER_ENV_DIR
@@ -57,7 +58,7 @@ def run_moonraker_removal(
def select_instances_to_remove( def select_instances_to_remove(
instances: List[Moonraker], instances: List[Moonraker],
) -> Union[List[Moonraker], None]: ) -> List[Moonraker] | None:
start_index = 1 start_index = 1
options = [str(i + start_index) for i in range(len(instances))] options = [str(i + start_index) for i in range(len(instances))]
options.extend(["a", "b"]) options.extend(["a", "b"])
@@ -84,8 +85,11 @@ def select_instances_to_remove(
def remove_instances( def remove_instances(
instance_manager: InstanceManager, instance_manager: InstanceManager,
instance_list: List[Moonraker], instance_list: List[Moonraker] | None,
) -> None: ) -> None:
if not instance_list:
Logger.print_info("No Moonraker instances found. Skipped ...")
return
for instance in instance_list: for instance in instance_list:
Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...") Logger.print_status(f"Removing instance {instance.get_service_file_name()} ...")
instance_manager.current_instance = instance instance_manager.current_instance = instance

View File

@@ -89,7 +89,10 @@ def install_moonraker() -> None:
if selected_option == "a": if selected_option == "a":
instance_names.extend([k.suffix for k in klipper_list]) instance_names.extend([k.suffix for k in klipper_list])
else: else:
instance_names.append(options.get(selected_option).suffix) klipper_instance: Klipper | None = options.get(selected_option)
if klipper_instance is None:
raise Exception("Error selecting instance!")
instance_names.append(klipper_instance.suffix)
create_example_cfg = get_confirm("Create example moonraker.conf?") create_example_cfg = get_confirm("Create example moonraker.conf?")

View File

@@ -126,7 +126,7 @@ def create_example_moonraker_conf(
Logger.print_ok(f"Example moonraker.conf created in '{instance.cfg_dir}'") Logger.print_ok(f"Example moonraker.conf created in '{instance.cfg_dir}'")
def backup_moonraker_dir(): def backup_moonraker_dir() -> None:
bm = BackupManager() bm = BackupManager()
bm.backup_directory("moonraker", source=MOONRAKER_DIR, target=MOONRAKER_BACKUP_DIR) bm.backup_directory("moonraker", source=MOONRAKER_DIR, target=MOONRAKER_BACKUP_DIR)
bm.backup_directory( bm.backup_directory(

View File

@@ -26,6 +26,7 @@ from core.instance_manager.base_instance import BaseInstance
from utils.logger import Logger from utils.logger import Logger
# todo: make this to a dataclass
class Octoeverywhere(BaseInstance): class Octoeverywhere(BaseInstance):
@classmethod @classmethod
def blacklist(cls) -> List[str]: def blacklist(cls) -> List[str]:
@@ -64,7 +65,7 @@ class Octoeverywhere(BaseInstance):
raise raise
@staticmethod @staticmethod
def update(): def update() -> None:
try: try:
run(OE_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OE_DIR) run(OE_UPDATE_SCRIPT.as_posix(), check=True, shell=True, cwd=OE_DIR)

View File

@@ -6,6 +6,7 @@
# # # #
# 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 __future__ import annotations
import shutil import shutil
import subprocess import subprocess
@@ -112,7 +113,7 @@ def update_client_config(client: BaseWebClient) -> None:
def create_client_config_symlink( def create_client_config_symlink(
client_config: BaseWebClientConfig, klipper_instances: List[Klipper] = None client_config: BaseWebClientConfig, klipper_instances: List[Klipper] | None = None
) -> None: ) -> None:
if klipper_instances is None: if klipper_instances is None:
kl_im = InstanceManager(Klipper) kl_im = InstanceManager(Klipper)

View File

@@ -13,7 +13,7 @@ from components.webui_client.base_data import BaseWebClient
from utils.logger import DialogType, Logger from utils.logger import DialogType, Logger
def print_moonraker_not_found_dialog(): def print_moonraker_not_found_dialog() -> None:
Logger.print_dialog( Logger.print_dialog(
DialogType.WARNING, DialogType.WARNING,
[ [
@@ -29,7 +29,7 @@ def print_moonraker_not_found_dialog():
) )
def print_client_already_installed_dialog(name: str): def print_client_already_installed_dialog(name: str) -> None:
Logger.print_dialog( Logger.print_dialog(
DialogType.WARNING, DialogType.WARNING,
[ [
@@ -41,7 +41,9 @@ def print_client_already_installed_dialog(name: str):
) )
def print_client_port_select_dialog(name: str, port: int, ports_in_use: List[int]): def print_client_port_select_dialog(
name: str, port: int, ports_in_use: List[int]
) -> None:
Logger.print_dialog( Logger.print_dialog(
DialogType.CUSTOM, DialogType.CUSTOM,
[ [

View File

@@ -73,7 +73,7 @@ def get_current_client_config(clients: List[BaseWebClient]) -> str:
return f"{COLOR_CYAN}-{RESET_FORMAT}" return f"{COLOR_CYAN}-{RESET_FORMAT}"
def backup_mainsail_config_json(is_temp=False) -> None: def backup_mainsail_config_json(is_temp: bool = False) -> None:
c_json = MainsailData().client_dir.joinpath("config.json") c_json = MainsailData().client_dir.joinpath("config.json")
bm = BackupManager() bm = BackupManager()
if is_temp: if is_temp:
@@ -137,7 +137,7 @@ def get_local_client_version(client: BaseWebClient) -> str | None:
if relinfo_file.is_file(): if relinfo_file.is_file():
with open(relinfo_file, "r") as f: with open(relinfo_file, "r") as f:
return json.load(f)["version"] return str(json.load(f)["version"])
else: else:
with open(version_file, "r") as f: with open(version_file, "r") as f:
return f.readlines()[0] return f.readlines()[0]
@@ -146,7 +146,7 @@ def get_local_client_version(client: BaseWebClient) -> str | None:
def get_remote_client_version(client: BaseWebClient) -> str | None: def get_remote_client_version(client: BaseWebClient) -> str | None:
try: try:
if (tag := get_latest_tag(client.repo_path)) != "": if (tag := get_latest_tag(client.repo_path)) != "":
return tag return str(tag)
return None return None
except Exception: except Exception:
return None return None

View File

@@ -48,7 +48,8 @@ class FluiddData(BaseWebClient):
def download_url(self) -> str: def download_url(self) -> str:
from components.webui_client.client_utils import get_download_url from components.webui_client.client_utils import get_download_url
return get_download_url(self.BASE_DL_URL, self) url: str = get_download_url(self.BASE_DL_URL, self)
return url
@property @property
def client_config(self) -> BaseWebClientConfig: def client_config(self) -> BaseWebClientConfig:

View File

@@ -48,7 +48,8 @@ class MainsailData(BaseWebClient):
def download_url(self) -> str: def download_url(self) -> str:
from components.webui_client.client_utils import get_download_url from components.webui_client.client_utils import get_download_url
return get_download_url(self.BASE_DL_URL, self) url: str = get_download_url(self.BASE_DL_URL, self)
return url
@property @property
def client_config(self) -> BaseWebClientConfig: def client_config(self) -> BaseWebClientConfig:

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.webui_client import client_remove from components.webui_client import client_remove
from components.webui_client.base_data import BaseWebClient, WebClientType from components.webui_client.base_data import BaseWebClient, WebClientType
@@ -20,22 +21,20 @@ from utils.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
class ClientRemoveMenu(BaseMenu): class ClientRemoveMenu(BaseMenu):
def __init__( def __init__(
self, client: BaseWebClient, previous_menu: Optional[Type[BaseMenu]] = None self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None
): ):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.client = client self.client: BaseWebClient = client
self.remove_client = False self.remove_client: bool = False
self.remove_client_cfg = False self.remove_client_cfg: bool = False
self.backup_mainsail_config_json = False self.backup_mainsail_config_json: bool = False
self.selection_state = False self.selection_state: bool = False
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.remove_menu import RemoveMenu from core.menus.remove_menu import RemoveMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else RemoveMenu
previous_menu if previous_menu is not None else RemoveMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {

View File

@@ -6,6 +6,7 @@
# # # #
# 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 __future__ import annotations
import shutil import shutil
from pathlib import Path from pathlib import Path
@@ -20,8 +21,8 @@ from utils.logger import Logger
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class BackupManager: class BackupManager:
def __init__(self, backup_root_dir: Path = BACKUP_ROOT_DIR): def __init__(self, backup_root_dir: Path = BACKUP_ROOT_DIR):
self._backup_root_dir = backup_root_dir self._backup_root_dir: Path = backup_root_dir
self._ignore_folders = None self._ignore_folders: List[str] = []
@property @property
def backup_root_dir(self) -> Path: def backup_root_dir(self) -> Path:
@@ -39,7 +40,7 @@ class BackupManager:
def ignore_folders(self, value: List[str]): def ignore_folders(self, value: List[str]):
self._ignore_folders = value self._ignore_folders = value
def backup_file(self, file: Path, target: Path = None, custom_filename=None): def backup_file(self, file: Path, target: Path | None = None, custom_filename=None):
Logger.print_status(f"Creating backup of {file} ...") Logger.print_status(f"Creating backup of {file} ...")
if not file.exists(): if not file.exists():
@@ -62,7 +63,9 @@ class BackupManager:
else: else:
Logger.print_info(f"File '{file}' not found ...") Logger.print_info(f"File '{file}' not found ...")
def backup_directory(self, name: str, source: Path, target: Path = None) -> None: def backup_directory(
self, name: str, source: Path, target: Path | None = None
) -> None:
Logger.print_status(f"Creating backup of {name} in {target} ...") Logger.print_status(f"Creating backup of {name} in {target} ...")
if source is None or not Path(source).exists(): if source is None or not Path(source).exists():
@@ -83,9 +86,9 @@ class BackupManager:
Logger.print_error(f"Unable to backup directory '{source}':\n{e}") Logger.print_error(f"Unable to backup directory '{source}':\n{e}")
return return
def ignore_folders_func(self, dirpath, filenames): def ignore_folders_func(self, dirpath, filenames) -> List[str]:
return ( return (
[f for f in filenames if f in self._ignore_folders] [f for f in filenames if f in self._ignore_folders]
if self._ignore_folders is not None if self._ignore_folders
else [] else []
) )

View File

@@ -13,7 +13,7 @@ import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import List
from utils.constants import CURRENT_USER, SYSTEMD from utils.constants import CURRENT_USER, SYSTEMD
from utils.logger import Logger from utils.logger import Logger
@@ -21,21 +21,21 @@ from utils.logger import Logger
@dataclass @dataclass
class BaseInstance(ABC): class BaseInstance(ABC):
instance_type: BaseInstance
suffix: str suffix: str
user: str = field(default=CURRENT_USER, init=False) user: str = field(default=CURRENT_USER, init=False)
data_dir: Path = None data_dir: Path | None = None
data_dir_name: str = "" data_dir_name: str = ""
is_legacy_instance: bool = False is_legacy_instance: bool = False
cfg_dir: Path = None cfg_dir: Path | None = None
log_dir: Path = None log_dir: Path | None = None
comms_dir: Path = None comms_dir: Path | None = None
sysd_dir: Path = None sysd_dir: Path | None = None
gcodes_dir: Path = None gcodes_dir: Path | None = None
def __post_init__(self) -> None: def __post_init__(self) -> None:
self._set_data_dir() self._set_data_dir()
self._set_is_legacy_instance() self._set_is_legacy_instance()
if self.data_dir is not None:
self.cfg_dir = self.data_dir.joinpath("config") self.cfg_dir = self.data_dir.joinpath("config")
self.log_dir = self.data_dir.joinpath("logs") self.log_dir = self.data_dir.joinpath("logs")
self.comms_dir = self.data_dir.joinpath("comms") self.comms_dir = self.data_dir.joinpath("comms")
@@ -54,8 +54,8 @@ class BaseInstance(ABC):
def delete(self) -> None: def delete(self) -> None:
raise NotImplementedError("Subclasses must implement the delete method") raise NotImplementedError("Subclasses must implement the delete method")
def create_folders(self, add_dirs: Optional[List[Path]] = None) -> None: def create_folders(self, add_dirs: List[Path] | None = None) -> None:
dirs = [ dirs: List[Path | None] = [
self.data_dir, self.data_dir,
self.cfg_dir, self.cfg_dir,
self.log_dir, self.log_dir,
@@ -68,13 +68,15 @@ class BaseInstance(ABC):
dirs.extend(add_dirs) dirs.extend(add_dirs)
for _dir in dirs: for _dir in dirs:
if _dir is None:
continue
_dir.mkdir(exist_ok=True) _dir.mkdir(exist_ok=True)
# todo: refactor into a set method and access the value by accessing the property # todo: refactor into a set method and access the value by accessing the property
def get_service_file_name(self, extension: bool = False) -> str: def get_service_file_name(self, extension: bool = False) -> str:
from utils.common import convert_camelcase_to_kebabcase from utils.common import convert_camelcase_to_kebabcase
name = convert_camelcase_to_kebabcase(self.__class__.__name__) name: str = convert_camelcase_to_kebabcase(self.__class__.__name__)
if self.suffix != "": if self.suffix != "":
name += f"-{self.suffix}" name += f"-{self.suffix}"
@@ -82,12 +84,13 @@ class BaseInstance(ABC):
# todo: refactor into a set method and access the value by accessing the property # todo: refactor into a set method and access the value by accessing the property
def get_service_file_path(self) -> Path: def get_service_file_path(self) -> Path:
return SYSTEMD.joinpath(self.get_service_file_name(extension=True)) path: Path = SYSTEMD.joinpath(self.get_service_file_name(extension=True))
return path
def delete_logfiles(self, log_name: str) -> None: def delete_logfiles(self, log_name: str) -> None:
from utils.fs_utils import run_remove_routines from utils.fs_utils import run_remove_routines
if not self.log_dir.exists(): if not self.log_dir or not self.log_dir.exists():
return return
files = self.log_dir.iterdir() files = self.log_dir.iterdir()

View File

@@ -6,64 +6,66 @@
# # # #
# 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 __future__ import annotations
import re import re
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import List, Optional, TypeVar, Union from typing import List, Type, TypeVar
from core.instance_manager.base_instance import BaseInstance from core.instance_manager.base_instance import BaseInstance
from utils.constants import SYSTEMD from utils.constants import SYSTEMD
from utils.logger import Logger from utils.logger import Logger
from utils.sys_utils import cmd_sysctl_service from utils.sys_utils import cmd_sysctl_service
T = TypeVar(name="T", bound=BaseInstance, covariant=True) T = TypeVar("T", bound=BaseInstance, covariant=True)
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class InstanceManager: class InstanceManager:
def __init__(self, instance_type: T) -> None: def __init__(self, instance_type: Type[T]) -> None:
self._instance_type = instance_type self._instance_type = instance_type
self._current_instance: Optional[T] = None self._current_instance: Type[T] | None = None
self._instance_suffix: Optional[str] = None self._instance_suffix: str | None = None
self._instance_service: Optional[str] = None self._instance_service: str | None = None
self._instance_service_full: Optional[str] = None self._instance_service_full: str | None = None
self._instance_service_path: Optional[str] = None self._instance_service_path: str | None = None
self._instances: List[T] = [] self._instances: List[T] = []
@property @property
def instance_type(self) -> T: def instance_type(self) -> Type[T]:
return self._instance_type return self._instance_type
@instance_type.setter @instance_type.setter
def instance_type(self, value: T): def instance_type(self, value: Type[T]):
self._instance_type = value self._instance_type = value
@property @property
def current_instance(self) -> T: def current_instance(self) -> Type[T] | None:
return self._current_instance return self._current_instance
@current_instance.setter @current_instance.setter
def current_instance(self, value: T) -> None: def current_instance(self, value: Type[T] | None) -> None:
self._current_instance = value self._current_instance = value
if value is not None:
self.instance_suffix = value.suffix self.instance_suffix = value.suffix
self.instance_service = value.get_service_file_name() self.instance_service = value.get_service_file_name()
self.instance_service_path = value.get_service_file_path() self.instance_service_path = value.get_service_file_path()
@property @property
def instance_suffix(self) -> str: def instance_suffix(self) -> str | None:
return self._instance_suffix return self._instance_suffix
@instance_suffix.setter @instance_suffix.setter
def instance_suffix(self, value: str): def instance_suffix(self, value: str | None):
self._instance_suffix = value self._instance_suffix = value
@property @property
def instance_service(self) -> str: def instance_service(self) -> str | None:
return self._instance_service return self._instance_service
@instance_service.setter @instance_service.setter
def instance_service(self, value: str): def instance_service(self, value: str | None) -> None:
self._instance_service = value self._instance_service = value
@property @property
@@ -71,19 +73,19 @@ class InstanceManager:
return f"{self._instance_service}.service" return f"{self._instance_service}.service"
@property @property
def instance_service_path(self) -> str: def instance_service_path(self) -> str | None:
return self._instance_service_path return self._instance_service_path
@instance_service_path.setter @instance_service_path.setter
def instance_service_path(self, value: str): def instance_service_path(self, value: str | None) -> None:
self._instance_service_path = value self._instance_service_path = value
@property @property
def instances(self) -> List[T]: def instances(self) -> List[Type[T]]:
return self.find_instances() return self.find_instances()
@instances.setter @instances.setter
def instances(self, value: List[T]): def instances(self, value: List[T]) -> None:
self._instances = value self._instances = value
def create_instance(self) -> None: def create_instance(self) -> None:
@@ -157,7 +159,7 @@ class InstanceManager:
self.current_instance = instance self.current_instance = instance
self.stop_instance() self.stop_instance()
def find_instances(self) -> List[T]: def find_instances(self) -> List[Type[T]]:
from utils.common import convert_camelcase_to_kebabcase from utils.common import convert_camelcase_to_kebabcase
name = convert_camelcase_to_kebabcase(self.instance_type.__name__) name = convert_camelcase_to_kebabcase(self.instance_type.__name__)
@@ -185,10 +187,10 @@ class InstanceManager:
suffix = file_path.stem[len(name) :] suffix = file_path.stem[len(name) :]
return suffix[1:] if suffix else "" return suffix[1:] if suffix else ""
def _sort_instance_list(self, suffix: Union[int, str, None]): def _sort_instance_list(self, suffix: int | str | None):
if suffix is None: if suffix is None:
return return
elif suffix.isdigit(): elif isinstance(suffix, str) and suffix.isdigit():
return f"{int(suffix):04}" return f"{int(suffix):04}"
else: else:
return suffix return suffix

View File

@@ -6,10 +6,11 @@
# # # #
# 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 __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Any, Callable, Union from typing import Any, Callable
@dataclass @dataclass
@@ -22,7 +23,7 @@ class Option:
:param opt_data: Can be used to pass any additional data to the menu option :param opt_data: Can be used to pass any additional data to the menu option
""" """
method: Union[Callable, None] = None method: Callable | None = None
menu: bool = False menu: bool = False
opt_index: str = "" opt_index: str = ""
opt_data: Any = None opt_data: Any = None

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.klipper import KLIPPER_DIR from components.klipper import KLIPPER_DIR
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
@@ -30,18 +31,16 @@ from utils.git_utils import rollback_repository
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class AdvancedMenu(BaseMenu): class AdvancedMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self): def set_options(self) -> None:
self.options = { self.options = {
"1": Option(method=self.build, menu=True), "1": Option(method=self.build, menu=True),
"2": Option(method=self.flash, menu=False), "2": Option(method=self.flash, menu=False),
@@ -51,7 +50,7 @@ class AdvancedMenu(BaseMenu):
"6": Option(method=self.moonraker_rollback, menu=True), "6": Option(method=self.moonraker_rollback, menu=True),
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ Advanced Menu ] " header = " [ Advanced Menu ] "
color = COLOR_YELLOW color = COLOR_YELLOW
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -70,23 +69,23 @@ class AdvancedMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def klipper_rollback(self, **kwargs): def klipper_rollback(self, **kwargs) -> None:
rollback_repository(KLIPPER_DIR, Klipper) rollback_repository(KLIPPER_DIR, Klipper)
def moonraker_rollback(self, **kwargs): def moonraker_rollback(self, **kwargs) -> None:
rollback_repository(MOONRAKER_DIR, Moonraker) rollback_repository(MOONRAKER_DIR, Moonraker)
def build(self, **kwargs): def build(self, **kwargs) -> None:
KlipperBuildFirmwareMenu(previous_menu=self.__class__).run() KlipperBuildFirmwareMenu(previous_menu=self.__class__).run()
def flash(self, **kwargs): def flash(self, **kwargs) -> None:
KlipperFlashMethodMenu(previous_menu=self.__class__).run() KlipperFlashMethodMenu(previous_menu=self.__class__).run()
def build_flash(self, **kwargs): def build_flash(self, **kwargs) -> None:
KlipperBuildFirmwareMenu(previous_menu=KlipperFlashMethodMenu).run() KlipperBuildFirmwareMenu(previous_menu=KlipperFlashMethodMenu).run()
KlipperFlashMethodMenu(previous_menu=self.__class__).run() KlipperFlashMethodMenu(previous_menu=self.__class__).run()
def get_id(self, **kwargs): def get_id(self, **kwargs) -> None:
KlipperSelectMcuConnectionMenu( KlipperSelectMcuConnectionMenu(
previous_menu=self.__class__, previous_menu=self.__class__,
standalone=True, standalone=True,

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.klipper.klipper_utils import backup_klipper_dir from components.klipper.klipper_utils import backup_klipper_dir
from components.klipperscreen.klipperscreen import backup_klipperscreen_dir from components.klipperscreen.klipperscreen import backup_klipperscreen_dir
@@ -31,16 +32,14 @@ from utils.constants import COLOR_CYAN, COLOR_YELLOW, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class BackupMenu(BaseMenu): class BackupMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -55,7 +54,7 @@ class BackupMenu(BaseMenu):
"9": Option(method=self.backup_klipperscreen, menu=False), "9": Option(method=self.backup_klipperscreen, menu=False),
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ Backup Menu ] " header = " [ Backup Menu ] "
line1 = f"{COLOR_YELLOW}INFO: Backups are located in '~/kiauh-backups'{RESET_FORMAT}" line1 = f"{COLOR_YELLOW}INFO: Backups are located in '~/kiauh-backups'{RESET_FORMAT}"
color = COLOR_CYAN color = COLOR_CYAN
@@ -81,29 +80,29 @@ class BackupMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def backup_klipper(self, **kwargs): def backup_klipper(self, **kwargs) -> None:
backup_klipper_dir() backup_klipper_dir()
def backup_moonraker(self, **kwargs): def backup_moonraker(self, **kwargs) -> None:
backup_moonraker_dir() backup_moonraker_dir()
def backup_printer_config(self, **kwargs): def backup_printer_config(self, **kwargs) -> None:
backup_printer_config_dir() backup_printer_config_dir()
def backup_moonraker_db(self, **kwargs): def backup_moonraker_db(self, **kwargs) -> None:
backup_moonraker_db_dir() backup_moonraker_db_dir()
def backup_mainsail(self, **kwargs): def backup_mainsail(self, **kwargs) -> None:
backup_client_data(MainsailData()) backup_client_data(MainsailData())
def backup_fluidd(self, **kwargs): def backup_fluidd(self, **kwargs) -> None:
backup_client_data(FluiddData()) backup_client_data(FluiddData())
def backup_mainsail_config(self, **kwargs): def backup_mainsail_config(self, **kwargs) -> None:
backup_client_config_data(MainsailData()) backup_client_config_data(MainsailData())
def backup_fluidd_config(self, **kwargs): def backup_fluidd_config(self, **kwargs) -> None:
backup_client_config_data(FluiddData()) backup_client_config_data(FluiddData())
def backup_klipperscreen(self, **kwargs): def backup_klipperscreen(self, **kwargs) -> None:
backup_klipperscreen_dir() backup_klipperscreen_dir()

View File

@@ -14,7 +14,7 @@ import sys
import textwrap import textwrap
import traceback import traceback
from abc import abstractmethod from abc import abstractmethod
from typing import Dict, Optional, Type from typing import Dict, Type
from core.menus import FooterType, Option from core.menus import FooterType, Option
from utils.constants import ( from utils.constants import (
@@ -27,11 +27,11 @@ from utils.constants import (
from utils.logger import Logger from utils.logger import Logger
def clear(): def clear() -> None:
subprocess.call("clear", shell=True) subprocess.call("clear", shell=True)
def print_header(): def print_header() -> None:
line1 = " [ KIAUH ] " line1 = " [ KIAUH ] "
line2 = "Klipper Installation And Update Helper" line2 = "Klipper Installation And Update Helper"
line3 = "" line3 = ""
@@ -49,7 +49,7 @@ def print_header():
print(header, end="") print(header, end="")
def print_quit_footer(): def print_quit_footer() -> None:
text = "Q) Quit" text = "Q) Quit"
color = COLOR_RED color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -62,7 +62,7 @@ def print_quit_footer():
print(footer, end="") print(footer, end="")
def print_back_footer(): def print_back_footer() -> None:
text = "B) « Back" text = "B) « Back"
color = COLOR_GREEN color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -75,7 +75,7 @@ def print_back_footer():
print(footer, end="") print(footer, end="")
def print_back_help_footer(): def print_back_help_footer() -> None:
text1 = "B) « Back" text1 = "B) « Back"
text2 = "H) Help [?]" text2 = "H) Help [?]"
color1 = COLOR_GREEN color1 = COLOR_GREEN
@@ -90,7 +90,7 @@ def print_back_help_footer():
print(footer, end="") print(footer, end="")
def print_blank_footer(): def print_blank_footer() -> None:
print("╚═══════════════════════════════════════════════════════╝") print("╚═══════════════════════════════════════════════════════╝")
@@ -109,15 +109,15 @@ class BaseMenu(metaclass=PostInitCaller):
default_option: Option = None default_option: Option = None
input_label_txt: str = "Perform action" input_label_txt: str = "Perform action"
header: bool = False header: bool = False
previous_menu: Type[BaseMenu] = None previous_menu: Type[BaseMenu] | None = None
help_menu: Type[BaseMenu] = None help_menu: Type[BaseMenu] | None = None
footer_type: FooterType = FooterType.BACK footer_type: FooterType = FooterType.BACK
def __init__(self, **kwargs): def __init__(self, **kwargs) -> None:
if type(self) is BaseMenu: if type(self) is BaseMenu:
raise NotImplementedError("BaseMenu cannot be instantiated directly.") raise NotImplementedError("BaseMenu cannot be instantiated directly.")
def __post_init__(self): def __post_init__(self) -> None:
self.set_previous_menu(self.previous_menu) self.set_previous_menu(self.previous_menu)
self.set_options() self.set_options()
@@ -133,18 +133,22 @@ class BaseMenu(metaclass=PostInitCaller):
if self.default_option is not None: if self.default_option is not None:
self.options[""] = self.default_option self.options[""] = self.default_option
def __go_back(self, **kwargs): def __go_back(self, **kwargs) -> None:
if self.previous_menu is None:
return
self.previous_menu().run() self.previous_menu().run()
def __go_to_help(self, **kwargs): def __go_to_help(self, **kwargs) -> None:
if self.help_menu is None:
return
self.help_menu(previous_menu=self).run() self.help_menu(previous_menu=self).run()
def __exit(self, **kwargs): def __exit(self, **kwargs) -> None:
Logger.print_ok("###### Happy printing!", False) Logger.print_ok("###### Happy printing!", False)
sys.exit(0) sys.exit(0)
@abstractmethod @abstractmethod
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.crowsnest.crowsnest import install_crowsnest from components.crowsnest.crowsnest import install_crowsnest
from components.klipper import klipper_setup from components.klipper import klipper_setup
@@ -28,16 +29,14 @@ from utils.constants import COLOR_GREEN, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class InstallMenu(BaseMenu): class InstallMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -53,7 +52,7 @@ class InstallMenu(BaseMenu):
"10": Option(method=self.install_octoeverywhere, menu=False), "10": Option(method=self.install_octoeverywhere, menu=False),
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ Installation Menu ] " header = " [ Installation Menu ] "
color = COLOR_GREEN color = COLOR_GREEN
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -79,32 +78,32 @@ class InstallMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def install_klipper(self, **kwargs): def install_klipper(self, **kwargs) -> None:
klipper_setup.install_klipper() klipper_setup.install_klipper()
def install_moonraker(self, **kwargs): def install_moonraker(self, **kwargs) -> None:
moonraker_setup.install_moonraker() moonraker_setup.install_moonraker()
def install_mainsail(self, **kwargs): def install_mainsail(self, **kwargs) -> None:
client_setup.install_client(MainsailData()) client_setup.install_client(MainsailData())
def install_mainsail_config(self, **kwargs): def install_mainsail_config(self, **kwargs) -> None:
client_config_setup.install_client_config(MainsailData()) client_config_setup.install_client_config(MainsailData())
def install_fluidd(self, **kwargs): def install_fluidd(self, **kwargs) -> None:
client_setup.install_client(FluiddData()) client_setup.install_client(FluiddData())
def install_fluidd_config(self, **kwargs): def install_fluidd_config(self, **kwargs) -> None:
client_config_setup.install_client_config(FluiddData()) client_config_setup.install_client_config(FluiddData())
def install_klipperscreen(self, **kwargs): def install_klipperscreen(self, **kwargs) -> None:
install_klipperscreen() install_klipperscreen()
def install_mobileraker(self, **kwargs): def install_mobileraker(self, **kwargs) -> None:
install_mobileraker() install_mobileraker()
def install_crowsnest(self, **kwargs): def install_crowsnest(self, **kwargs) -> None:
install_crowsnest() install_crowsnest()
def install_octoeverywhere(self, **kwargs): def install_octoeverywhere(self, **kwargs) -> None:
install_octoeverywhere() install_octoeverywhere()

View File

@@ -6,9 +6,11 @@
# # # #
# 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 __future__ import annotations
import sys import sys
import textwrap import textwrap
from typing import Optional, Type from typing import Callable, Type
from components.crowsnest.crowsnest import get_crowsnest_status from components.crowsnest.crowsnest import get_crowsnest_status
from components.klipper.klipper_utils import get_klipper_status from components.klipper.klipper_utils import get_klipper_status
@@ -47,18 +49,18 @@ from utils.types import ComponentStatus, StatusMap, StatusText
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class MainMenu(BaseMenu): class MainMenu(BaseMenu):
def __init__(self): def __init__(self) -> None:
super().__init__() super().__init__()
self.header = True self.header: bool = True
self.footer_type = FooterType.QUIT self.footer_type: FooterType = FooterType.QUIT
self.kl_status = self.kl_repo = self.mr_status = self.mr_repo = "" self.kl_status = self.kl_repo = self.mr_status = self.mr_repo = ""
self.ms_status = self.fl_status = self.ks_status = self.mb_status = "" self.ms_status = self.fl_status = self.ks_status = self.mb_status = ""
self.cn_status = self.cc_status = self.oe_status = "" self.cn_status = self.cc_status = self.oe_status = ""
self._init_status() self._init_status()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
"""MainMenu does not have a previous menu""" """MainMenu does not have a previous menu"""
pass pass
@@ -94,7 +96,7 @@ class MainMenu(BaseMenu):
self._get_component_status("cn", get_crowsnest_status) self._get_component_status("cn", get_crowsnest_status)
self._get_component_status("oe", get_octoeverywhere_status) self._get_component_status("oe", get_octoeverywhere_status)
def _get_component_status(self, name: str, status_fn: callable, *args) -> None: def _get_component_status(self, name: str, status_fn: Callable, *args) -> None:
status_data: ComponentStatus = status_fn(*args) status_data: ComponentStatus = status_fn(*args)
code: int = status_data.status code: int = status_data.status
status: StatusText = StatusMap[code] status: StatusText = StatusMap[code]
@@ -119,7 +121,7 @@ class MainMenu(BaseMenu):
return f"{color}{status}{count}{RESET_FORMAT}" return f"{color}{status}{count}{RESET_FORMAT}"
def print_menu(self): def print_menu(self) -> None:
self._fetch_status() self._fetch_status()
header = " [ Main Menu ] " header = " [ Main Menu ] "
@@ -155,30 +157,30 @@ class MainMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def exit(self, **kwargs): def exit(self, **kwargs) -> None:
Logger.print_ok("###### Happy printing!", False) Logger.print_ok("###### Happy printing!", False)
sys.exit(0) sys.exit(0)
def log_upload_menu(self, **kwargs): def log_upload_menu(self, **kwargs) -> None:
LogUploadMenu().run() LogUploadMenu().run()
def install_menu(self, **kwargs): def install_menu(self, **kwargs) -> None:
InstallMenu(previous_menu=self.__class__).run() InstallMenu(previous_menu=self.__class__).run()
def update_menu(self, **kwargs): def update_menu(self, **kwargs) -> None:
UpdateMenu(previous_menu=self.__class__).run() UpdateMenu(previous_menu=self.__class__).run()
def remove_menu(self, **kwargs): def remove_menu(self, **kwargs) -> None:
RemoveMenu(previous_menu=self.__class__).run() RemoveMenu(previous_menu=self.__class__).run()
def advanced_menu(self, **kwargs): def advanced_menu(self, **kwargs) -> None:
AdvancedMenu(previous_menu=self.__class__).run() AdvancedMenu(previous_menu=self.__class__).run()
def backup_menu(self, **kwargs): def backup_menu(self, **kwargs) -> None:
BackupMenu(previous_menu=self.__class__).run() BackupMenu(previous_menu=self.__class__).run()
def settings_menu(self, **kwargs): def settings_menu(self, **kwargs) -> None:
SettingsMenu(previous_menu=self.__class__).run() SettingsMenu(previous_menu=self.__class__).run()
def extension_menu(self, **kwargs): def extension_menu(self, **kwargs) -> None:
ExtensionsMenu(previous_menu=self.__class__).run() ExtensionsMenu(previous_menu=self.__class__).run()

View File

@@ -6,9 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Type
from components.crowsnest.crowsnest import remove_crowsnest from components.crowsnest.crowsnest import remove_crowsnest
from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu from components.klipper.menus.klipper_remove_menu import KlipperRemoveMenu
@@ -29,18 +30,16 @@ from utils.constants import COLOR_RED, RESET_FORMAT
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class RemoveMenu(BaseMenu): class RemoveMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self): def set_options(self) -> None:
self.options = { self.options = {
"1": Option(method=self.remove_klipper, menu=True), "1": Option(method=self.remove_klipper, menu=True),
"2": Option(method=self.remove_moonraker, menu=True), "2": Option(method=self.remove_moonraker, menu=True),
@@ -52,7 +51,7 @@ class RemoveMenu(BaseMenu):
"8": Option(method=self.remove_octoeverywhere, menu=True), "8": Option(method=self.remove_octoeverywhere, menu=True),
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ Remove Menu ] " header = " [ Remove Menu ] "
color = COLOR_RED color = COLOR_RED
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -78,26 +77,26 @@ class RemoveMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def remove_klipper(self, **kwargs): def remove_klipper(self, **kwargs) -> None:
KlipperRemoveMenu(previous_menu=self.__class__).run() KlipperRemoveMenu(previous_menu=self.__class__).run()
def remove_moonraker(self, **kwargs): def remove_moonraker(self, **kwargs) -> None:
MoonrakerRemoveMenu(previous_menu=self.__class__).run() MoonrakerRemoveMenu(previous_menu=self.__class__).run()
def remove_mainsail(self, **kwargs): def remove_mainsail(self, **kwargs) -> None:
ClientRemoveMenu(previous_menu=self.__class__, client=MainsailData()).run() ClientRemoveMenu(previous_menu=self.__class__, client=MainsailData()).run()
def remove_fluidd(self, **kwargs): def remove_fluidd(self, **kwargs) -> None:
ClientRemoveMenu(previous_menu=self.__class__, client=FluiddData()).run() ClientRemoveMenu(previous_menu=self.__class__, client=FluiddData()).run()
def remove_klipperscreen(self, **kwargs): def remove_klipperscreen(self, **kwargs) -> None:
remove_klipperscreen() remove_klipperscreen()
def remove_mobileraker(self, **kwargs): def remove_mobileraker(self, **kwargs) -> None:
remove_mobileraker() remove_mobileraker()
def remove_crowsnest(self, **kwargs): def remove_crowsnest(self, **kwargs) -> None:
remove_crowsnest() remove_crowsnest()
def remove_octoeverywhere(self, **kwargs): def remove_octoeverywhere(self, **kwargs) -> None:
remove_octoeverywhere() remove_octoeverywhere()

View File

@@ -6,10 +6,12 @@
# # # #
# 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 __future__ import annotations
import shutil import shutil
import textwrap import textwrap
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple, Type from typing import Tuple, Type
from components.klipper import KLIPPER_DIR from components.klipper import KLIPPER_DIR
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
@@ -28,22 +30,20 @@ from utils.logger import DialogType, Logger
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class SettingsMenu(BaseMenu): class SettingsMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.klipper_repo = None self.klipper_repo: str | None = None
self.moonraker_repo = None self.moonraker_repo: str | None = None
self.mainsail_unstable = None self.mainsail_unstable: bool | None = None
self.fluidd_unstable = None self.fluidd_unstable: bool | None = None
self.auto_backups_enabled = None self.auto_backups_enabled: bool | None = None
self._load_settings() self._load_settings()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -54,7 +54,7 @@ class SettingsMenu(BaseMenu):
"5": Option(method=self.toggle_backup_before_update, menu=False), "5": Option(method=self.toggle_backup_before_update, menu=False),
} }
def print_menu(self): def print_menu(self) -> None:
header = " [ KIAUH Settings ] " header = " [ KIAUH Settings ] "
color = COLOR_CYAN color = COLOR_CYAN
count = 62 - len(color) - len(RESET_FORMAT) count = 62 - len(color) - len(RESET_FORMAT)
@@ -94,7 +94,7 @@ class SettingsMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def _load_settings(self): def _load_settings(self) -> None:
self.settings = KiauhSettings() self.settings = KiauhSettings()
self._format_repo_str("klipper") self._format_repo_str("klipper")
@@ -133,7 +133,7 @@ class SettingsMenu(BaseMenu):
return repo, branch return repo, branch
def _set_repo(self, repo_name: str): def _set_repo(self, repo_name: str) -> None:
repo_url, branch = self._gather_input() repo_url, branch = self._gather_input()
display_name = repo_name.capitalize() display_name = repo_name.capitalize()
Logger.print_dialog( Logger.print_dialog(
@@ -186,23 +186,23 @@ class SettingsMenu(BaseMenu):
im.start_all_instance() im.start_all_instance()
def set_klipper_repo(self, **kwargs): def set_klipper_repo(self, **kwargs) -> None:
self._set_repo("klipper") self._set_repo("klipper")
def set_moonraker_repo(self, **kwargs): def set_moonraker_repo(self, **kwargs) -> None:
self._set_repo("moonraker") self._set_repo("moonraker")
def toggle_mainsail_release(self, **kwargs): def toggle_mainsail_release(self, **kwargs) -> None:
self.mainsail_unstable = not self.mainsail_unstable self.mainsail_unstable = not self.mainsail_unstable
self.settings.mainsail.unstable_releases = self.mainsail_unstable self.settings.mainsail.unstable_releases = self.mainsail_unstable
self.settings.save() self.settings.save()
def toggle_fluidd_release(self, **kwargs): def toggle_fluidd_release(self, **kwargs) -> None:
self.fluidd_unstable = not self.fluidd_unstable self.fluidd_unstable = not self.fluidd_unstable
self.settings.fluidd.unstable_releases = self.fluidd_unstable self.settings.fluidd.unstable_releases = self.fluidd_unstable
self.settings.save() self.settings.save()
def toggle_backup_before_update(self, **kwargs): def toggle_backup_before_update(self, **kwargs) -> None:
self.auto_backups_enabled = not self.auto_backups_enabled self.auto_backups_enabled = not self.auto_backups_enabled
self.settings.kiauh.backup_before_update = self.auto_backups_enabled self.settings.kiauh.backup_before_update = self.auto_backups_enabled
self.settings.save() self.settings.save()

View File

@@ -6,8 +6,10 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from typing import Optional, Type from typing import Callable, Type
from components.crowsnest.crowsnest import get_crowsnest_status, update_crowsnest from components.crowsnest.crowsnest import get_crowsnest_status, update_crowsnest
from components.klipper.klipper_setup import update_klipper from components.klipper.klipper_setup import update_klipper
@@ -54,9 +56,9 @@ from utils.types import ComponentStatus
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class UpdateMenu(BaseMenu): class UpdateMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None) -> None:
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.klipper_local = self.klipper_remote = "" self.klipper_local = self.klipper_remote = ""
self.moonraker_local = self.moonraker_remote = "" self.moonraker_local = self.moonraker_remote = ""
@@ -84,12 +86,10 @@ class UpdateMenu(BaseMenu):
"octoeverywhere": {"installed": False, "local": None, "remote": None}, "octoeverywhere": {"installed": False, "local": None, "remote": None},
} }
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -107,7 +107,7 @@ class UpdateMenu(BaseMenu):
"11": Option(self.upgrade_system_packages, menu=False), "11": Option(self.upgrade_system_packages, menu=False),
} }
def print_menu(self): def print_menu(self) -> None:
spinner = Spinner() spinner = Spinner()
spinner.start() spinner.start()
@@ -149,52 +149,52 @@ class UpdateMenu(BaseMenu):
)[1:] )[1:]
print(menu, end="") print(menu, end="")
def update_all(self, **kwargs): def update_all(self, **kwargs) -> None:
print("update_all") print("update_all")
def update_klipper(self, **kwargs): def update_klipper(self, **kwargs) -> None:
if self._check_is_installed("klipper"): if self._check_is_installed("klipper"):
update_klipper() update_klipper()
def update_moonraker(self, **kwargs): def update_moonraker(self, **kwargs) -> None:
if self._check_is_installed("moonraker"): if self._check_is_installed("moonraker"):
update_moonraker() update_moonraker()
def update_mainsail(self, **kwargs): def update_mainsail(self, **kwargs) -> None:
if self._check_is_installed("mainsail"): if self._check_is_installed("mainsail"):
update_client(self.mainsail_data) update_client(self.mainsail_data)
def update_mainsail_config(self, **kwargs): def update_mainsail_config(self, **kwargs) -> None:
if self._check_is_installed("mainsail_config"): if self._check_is_installed("mainsail_config"):
update_client_config(self.mainsail_data) update_client_config(self.mainsail_data)
def update_fluidd(self, **kwargs): def update_fluidd(self, **kwargs) -> None:
if self._check_is_installed("fluidd"): if self._check_is_installed("fluidd"):
update_client(self.fluidd_data) update_client(self.fluidd_data)
def update_fluidd_config(self, **kwargs): def update_fluidd_config(self, **kwargs) -> None:
if self._check_is_installed("fluidd_config"): if self._check_is_installed("fluidd_config"):
update_client_config(self.fluidd_data) update_client_config(self.fluidd_data)
def update_klipperscreen(self, **kwargs): def update_klipperscreen(self, **kwargs) -> None:
if self._check_is_installed("klipperscreen"): if self._check_is_installed("klipperscreen"):
update_klipperscreen() update_klipperscreen()
def update_mobileraker(self, **kwargs): def update_mobileraker(self, **kwargs) -> None:
if self._check_is_installed("mobileraker"): if self._check_is_installed("mobileraker"):
update_mobileraker() update_mobileraker()
def update_crowsnest(self, **kwargs): def update_crowsnest(self, **kwargs) -> None:
if self._check_is_installed("crowsnest"): if self._check_is_installed("crowsnest"):
update_crowsnest() update_crowsnest()
def update_octoeverywhere(self, **kwargs): def update_octoeverywhere(self, **kwargs) -> None:
if self._check_is_installed("octoeverywhere"): if self._check_is_installed("octoeverywhere"):
update_octoeverywhere() update_octoeverywhere()
def upgrade_system_packages(self, **kwargs): ... def upgrade_system_packages(self, **kwargs) -> None: ...
def _fetch_update_status(self): def _fetch_update_status(self) -> None:
self._set_status_data("klipper", get_klipper_status) self._set_status_data("klipper", get_klipper_status)
self._set_status_data("moonraker", get_moonraker_status) self._set_status_data("moonraker", get_moonraker_status)
self._set_status_data("mainsail", get_client_status, self.mainsail_data, True) self._set_status_data("mainsail", get_client_status, self.mainsail_data, True)
@@ -221,7 +221,7 @@ class UpdateMenu(BaseMenu):
return f"{color}{local_version or '-'}{RESET_FORMAT}" return f"{color}{local_version or '-'}{RESET_FORMAT}"
def _set_status_data(self, name: str, status_fn: callable, *args) -> None: def _set_status_data(self, name: str, status_fn: Callable, *args) -> None:
comp_status: ComponentStatus = status_fn(*args) comp_status: ComponentStatus = status_fn(*args)
self.status_data[name]["installed"] = True if comp_status.status == 2 else False self.status_data[name]["installed"] = True if comp_status.status == 2 else False

View File

@@ -8,8 +8,6 @@
# ======================================================================= # # ======================================================================= #
from __future__ import annotations from __future__ import annotations
from typing import Union
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import ( from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
NoOptionError, NoOptionError,
NoSectionError, NoSectionError,
@@ -61,10 +59,11 @@ class KiauhSettings:
def __new__(cls, *args, **kwargs) -> "KiauhSettings": def __new__(cls, *args, **kwargs) -> "KiauhSettings":
if cls._instance is None: if cls._instance is None:
cls._instance = super(KiauhSettings, cls).__new__(cls, *args, **kwargs) cls._instance = super(KiauhSettings, cls).__new__(cls, *args, **kwargs)
cls._instance.__initialized = False
return cls._instance return cls._instance
def __init__(self) -> None: def __init__(self) -> None:
if not hasattr(self, "__initialized"):
self.__initialized = False
if self.__initialized: if self.__initialized:
return return
self.__initialized = True self.__initialized = True
@@ -87,7 +86,7 @@ class KiauhSettings:
self._load_config() self._load_config()
def get(self, section: str, option: str) -> Union[str, int, bool]: def get(self, section: str, option: str) -> str | int | bool:
""" """
Get a value from the settings state by providing the section and option name as strings. Get a value from the settings state by providing the section and option name as strings.
Prefer direct access to the properties, as it is usually safer! Prefer direct access to the properties, as it is usually safer!
@@ -99,11 +98,11 @@ class KiauhSettings:
try: try:
section = getattr(self, section) section = getattr(self, section)
value = getattr(section, option) value = getattr(section, option)
return value return value # type: ignore
except AttributeError: except AttributeError:
raise raise
def set(self, section: str, option: str, value: Union[str, int, bool]) -> None: def set(self, section: str, option: str, value: str | int | bool) -> None:
""" """
Set a value in the settings state by providing the section and option name as strings. Set a value in the settings state by providing the section and option name as strings.
Prefer direct access to the properties, as it is usually safer! Prefer direct access to the properties, as it is usually safer!
@@ -113,7 +112,7 @@ class KiauhSettings:
""" """
try: try:
section = getattr(self, section) section = getattr(self, section)
section.option = value section.option = value # type: ignore
except AttributeError: except AttributeError:
raise raise
@@ -172,7 +171,7 @@ class KiauhSettings:
if v.isdigit() or v.lower() == "true" or v.lower() == "false": if v.isdigit() or v.lower() == "true" or v.lower() == "false":
raise ValueError raise ValueError
def _read_settings(self): def _read_settings(self) -> None:
self.kiauh.backup_before_update = self.config.getboolean( self.kiauh.backup_before_update = self.config.getboolean(
"kiauh", "backup_before_update" "kiauh", "backup_before_update"
) )
@@ -189,7 +188,7 @@ class KiauhSettings:
"fluidd", "unstable_releases" "fluidd", "unstable_releases"
) )
def _set_config_options(self): def _set_config_options(self) -> None:
self.config.set( self.config.set(
"kiauh", "kiauh",
"backup_before_update", "backup_before_update",

View File

@@ -6,13 +6,14 @@
# # # #
# 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 __future__ import annotations
import importlib import importlib
import inspect import inspect
import json import json
import textwrap import textwrap
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional, Type from typing import Dict, List, Type
from core.menus import Option from core.menus import Option
from core.menus.base_menu import BaseMenu from core.menus.base_menu import BaseMenu
@@ -25,17 +26,15 @@ from utils.logger import Logger
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class ExtensionsMenu(BaseMenu): class ExtensionsMenu(BaseMenu):
def __init__(self, previous_menu: Optional[Type[BaseMenu]] = None): def __init__(self, previous_menu: Type[BaseMenu] | None = None):
super().__init__() super().__init__()
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
self.extensions: Dict[str, BaseExtension] = self.discover_extensions() self.extensions: Dict[str, BaseExtension] = self.discover_extensions()
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from core.menus.main_menu import MainMenu from core.menus.main_menu import MainMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = previous_menu if previous_menu is not None else MainMenu
previous_menu if previous_menu is not None else MainMenu
)
def set_options(self) -> None: def set_options(self) -> None:
self.options = { self.options = {
@@ -80,7 +79,7 @@ class ExtensionsMenu(BaseMenu):
def extension_submenu(self, **kwargs): def extension_submenu(self, **kwargs):
ExtensionSubmenu(kwargs.get("opt_data"), self.__class__).run() ExtensionSubmenu(kwargs.get("opt_data"), self.__class__).run()
def print_menu(self): def print_menu(self) -> None:
header = " [ Extensions Menu ] " header = " [ Extensions Menu ] "
color = COLOR_CYAN color = COLOR_CYAN
line1 = f"{COLOR_YELLOW}Available Extensions:{RESET_FORMAT}" line1 = f"{COLOR_YELLOW}Available Extensions:{RESET_FORMAT}"
@@ -108,14 +107,14 @@ class ExtensionsMenu(BaseMenu):
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
class ExtensionSubmenu(BaseMenu): class ExtensionSubmenu(BaseMenu):
def __init__( def __init__(
self, extension: BaseExtension, previous_menu: Optional[Type[BaseMenu]] = None self, extension: BaseExtension, previous_menu: Type[BaseMenu] | None = None
): ):
super().__init__() super().__init__()
self.extension = extension self.extension = extension
self.previous_menu = previous_menu self.previous_menu: Type[BaseMenu] | None = previous_menu
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else ExtensionsMenu previous_menu if previous_menu is not None else ExtensionsMenu
) )

View File

@@ -45,8 +45,7 @@ class KlipperbackupExtension(BaseExtension):
subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"]) subprocess.run([str(KLIPPERBACKUP_DIR / "install.sh"), "check_updates"])
def remove_extension(self, **kwargs) -> None: def remove_extension(self, **kwargs) -> None:
def uninstall_service(service_name) -> bool:
def uninstall_service(service_name):
try: try:
subprocess.run(["sudo", "systemctl", "stop", service_name], check=True) subprocess.run(["sudo", "systemctl", "stop", service_name], check=True)
subprocess.run( subprocess.run(
@@ -59,7 +58,7 @@ class KlipperbackupExtension(BaseExtension):
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
return False return False
def check_crontab_entry(entry): def check_crontab_entry(entry) -> bool:
try: try:
crontab_content = subprocess.check_output( crontab_content = subprocess.check_output(
["crontab", "-l"], stderr=subprocess.DEVNULL, text=True ["crontab", "-l"], stderr=subprocess.DEVNULL, text=True

View File

@@ -6,12 +6,13 @@
# # # #
# 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 __future__ import annotations
import csv import csv
import shutil import shutil
import textwrap import textwrap
import urllib.request import urllib.request
from typing import List, Optional, Type, TypedDict, Union from typing import List, Type, TypedDict, Union
from components.klipper.klipper import Klipper from components.klipper.klipper import Klipper
from components.klipper.klipper_dialogs import ( from components.klipper.klipper_dialogs import (
@@ -81,10 +82,10 @@ class MainsailThemeInstallMenu(BaseMenu):
self.themes: List[ThemeData] = self.load_themes() self.themes: List[ThemeData] = self.load_themes()
self.instances = instances self.instances = instances
def set_previous_menu(self, previous_menu: Optional[Type[BaseMenu]]) -> None: def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None:
from extensions.extensions_menu import ExtensionsMenu from extensions.extensions_menu import ExtensionsMenu
self.previous_menu: Type[BaseMenu] = ( self.previous_menu = (
previous_menu if previous_menu is not None else ExtensionsMenu previous_menu if previous_menu is not None else ExtensionsMenu
) )

View File

@@ -33,6 +33,7 @@ OBICO_MACROS_CFG = "moonraker_obico_macros.cfg"
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
# todo: make this to a dataclass
class MoonrakerObico(BaseInstance): class MoonrakerObico(BaseInstance):
@classmethod @classmethod
def blacklist(cls) -> List[str]: def blacklist(cls) -> List[str]:
@@ -106,7 +107,7 @@ class MoonrakerObico(BaseInstance):
service_content = self._prep_service_file( service_content = self._prep_service_file(
service_template_path, env_file_target service_template_path, env_file_target
) )
command = ["sudo", "tee", service_file_target] command = ["sudo", "tee", service_file_target.as_posix()]
run( run(
command, command,
input=service_content.encode(), input=service_content.encode(),

View File

@@ -297,11 +297,11 @@ class ObicoExtension(BaseExtension):
def _patch_moonraker_conf(self, instances: List[Moonraker]) -> None: def _patch_moonraker_conf(self, instances: List[Moonraker]) -> None:
add_config_section(section=f"include {OBICO_UPDATE_CFG}", instances=instances) add_config_section(section=f"include {OBICO_UPDATE_CFG}", instances=instances)
def _link_obico_instances(self, unlinked_instances): def _link_obico_instances(self, unlinked_instances) -> None:
for obico in unlinked_instances: for obico in unlinked_instances:
obico.link() obico.link()
def _check_and_opt_link_instances(self): def _check_and_opt_link_instances(self) -> None:
Logger.print_status("Checking link status of Obico instances ...") Logger.print_status("Checking link status of Obico instances ...")
ob_im = InstanceManager(MoonrakerObico) ob_im = InstanceManager(MoonrakerObico)
ob_instances: List[MoonrakerObico] = ob_im.instances ob_instances: List[MoonrakerObico] = ob_im.instances

View File

@@ -23,6 +23,7 @@ TELEGRAM_BOT_REPO = "https://github.com/nlef/moonraker-telegram-bot.git"
# noinspection PyMethodMayBeStatic # noinspection PyMethodMayBeStatic
# todo: make this to a dataclass
class MoonrakerTelegramBot(BaseInstance): class MoonrakerTelegramBot(BaseInstance):
@classmethod @classmethod
def blacklist(cls) -> List[str]: def blacklist(cls) -> List[str]:
@@ -95,7 +96,7 @@ class MoonrakerTelegramBot(BaseInstance):
service_content = self._prep_service_file( service_content = self._prep_service_file(
service_template_path, env_file_target service_template_path, env_file_target
) )
command = ["sudo", "tee", service_file_target] command = ["sudo", "tee", service_file_target.as_posix()]
run( run(
command, command,
input=service_content.encode(), input=service_content.encode(),

View File

@@ -12,7 +12,7 @@ from core.settings.kiauh_settings import KiauhSettings
from utils.logger import Logger from utils.logger import Logger
def main(): def main() -> None:
try: try:
KiauhSettings() KiauhSettings()
MainMenu().run() MainMenu().run()

View File

@@ -96,12 +96,13 @@ def get_install_status(
for f in files: for f in files:
checks.append(f.exists()) checks.append(f.exists())
status: StatusCode
if all(checks): if all(checks):
status: StatusCode = 2 # installed status = 2 # installed
elif not any(checks): elif not any(checks):
status: StatusCode = 0 # not installed status = 0 # not installed
else: else:
status: StatusCode = 1 # incomplete status = 1 # incomplete
return ComponentStatus( return ComponentStatus(
status=status, status=status,
@@ -112,7 +113,7 @@ def get_install_status(
) )
def backup_printer_config_dir(): def backup_printer_config_dir() -> None:
# local import to prevent circular import # local import to prevent circular import
from core.backup_manager.backup_manager import BackupManager from core.backup_manager.backup_manager import BackupManager

View File

@@ -49,7 +49,7 @@ def add_config_section(
scp.write(cfg_file) scp.write(cfg_file)
def add_config_section_at_top(section: str, instances: List[B]): def add_config_section_at_top(section: str, instances: List[B]) -> None:
# TODO: this could be implemented natively in SimpleConfigParser # TODO: this could be implemented natively in SimpleConfigParser
for instance in instances: for instance in instances:
tmp_cfg = tempfile.NamedTemporaryFile(mode="w", delete=False) tmp_cfg = tempfile.NamedTemporaryFile(mode="w", delete=False)

View File

@@ -6,12 +6,14 @@
# # # #
# 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 __future__ import annotations
import warnings import warnings
from typing import Callable from typing import Callable
def deprecated(info: str = "", replaced_by: Callable = None) -> Callable: def deprecated(info: str = "", replaced_by: Callable | None = None) -> Callable:
def decorator(func): def decorator(func) -> Callable:
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
msg = f"{info}{replaced_by.__name__ if replaced_by else ''}" msg = f"{info}{replaced_by.__name__ if replaced_by else ''}"
warnings.warn(msg, category=DeprecationWarning, stacklevel=2) warnings.warn(msg, category=DeprecationWarning, stacklevel=2)

View File

@@ -36,7 +36,7 @@ def check_file_exist(file_path: Path, sudo=False) -> bool:
""" """
if sudo: if sudo:
try: try:
command = ["sudo", "find", file_path] command = ["sudo", "find", file_path.as_posix()]
check_output(command, stderr=DEVNULL) check_output(command, stderr=DEVNULL)
return True return True
except CalledProcessError: except CalledProcessError:
@@ -50,7 +50,7 @@ def check_file_exist(file_path: Path, sudo=False) -> bool:
def create_symlink(source: Path, target: Path, sudo=False) -> None: def create_symlink(source: Path, target: Path, sudo=False) -> None:
try: try:
cmd = ["ln", "-sf", source, target] cmd = ["ln", "-sf", source.as_posix(), target.as_posix()]
if sudo: if sudo:
cmd.insert(0, "sudo") cmd.insert(0, "sudo")
run(cmd, stderr=PIPE, check=True) run(cmd, stderr=PIPE, check=True)
@@ -61,7 +61,7 @@ def create_symlink(source: Path, target: Path, sudo=False) -> None:
def remove_with_sudo(file: Path) -> None: def remove_with_sudo(file: Path) -> None:
try: try:
cmd = ["sudo", "rm", "-rf", file] cmd = ["sudo", "rm", "-rf", file.as_posix()]
run(cmd, stderr=PIPE, check=True) run(cmd, stderr=PIPE, check=True)
except CalledProcessError as e: except CalledProcessError as e:
Logger.print_error(f"Failed to remove file: {e}") Logger.print_error(f"Failed to remove file: {e}")

View File

@@ -7,7 +7,7 @@ from http.client import HTTPResponse
from json import JSONDecodeError from json import JSONDecodeError
from pathlib import Path from pathlib import Path
from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run
from typing import List, Optional, Type from typing import List, Type
from core.instance_manager.base_instance import BaseInstance from core.instance_manager.base_instance import BaseInstance
from core.instance_manager.instance_manager import InstanceManager from core.instance_manager.instance_manager import InstanceManager
@@ -16,7 +16,7 @@ from utils.logger import Logger
def git_clone_wrapper( def git_clone_wrapper(
repo: str, target_dir: Path, branch: Optional[str] = None, force: bool = False repo: str, target_dir: Path, branch: str | None = None, force: bool = False
) -> None: ) -> None:
""" """
Clones a repository from the given URL and checks out the specified branch if given. Clones a repository from the given URL and checks out the specified branch if given.
@@ -75,7 +75,7 @@ def get_repo_name(repo: Path) -> str | None:
return "-" return "-"
try: try:
cmd = ["git", "-C", repo, "config", "--get", "remote.origin.url"] cmd = ["git", "-C", repo.as_posix(), "config", "--get", "remote.origin.url"]
result = check_output(cmd, stderr=DEVNULL) result = check_output(cmd, stderr=DEVNULL)
return "/".join(result.decode().strip().split("/")[-2:]) return "/".join(result.decode().strip().split("/")[-2:])
except CalledProcessError: except CalledProcessError:
@@ -159,7 +159,7 @@ def get_remote_commit(repo: Path) -> str | None:
def git_cmd_clone(repo: str, target_dir: Path) -> None: def git_cmd_clone(repo: str, target_dir: Path) -> None:
try: try:
command = ["git", "clone", repo, target_dir] command = ["git", "clone", repo, target_dir.as_posix()]
run(command, check=True) run(command, check=True)
Logger.print_ok("Clone successful!") Logger.print_ok("Clone successful!")
@@ -169,7 +169,7 @@ def git_cmd_clone(repo: str, target_dir: Path) -> None:
raise raise
def git_cmd_checkout(branch: str, target_dir: Path) -> None: def git_cmd_checkout(branch: str | None, target_dir: Path) -> None:
if branch is None: if branch is None:
return return

View File

@@ -9,7 +9,7 @@
from __future__ import annotations from __future__ import annotations
import re import re
from typing import Dict, List, Union from typing import Dict, List
from utils import INVALID_CHOICE from utils import INVALID_CHOICE
from utils.constants import COLOR_CYAN, RESET_FORMAT from utils.constants import COLOR_CYAN, RESET_FORMAT
@@ -53,9 +53,9 @@ def get_confirm(question: str, default_choice=True, allow_go_back=False) -> bool
def get_number_input( def get_number_input(
question: str, question: str,
min_count: int, min_count: int,
max_count=None, max_count: int | None = None,
default=None, default: int | None = None,
allow_go_back=False, allow_go_back: bool = False,
) -> int | None: ) -> int | None:
""" """
Helper method to get a number input from the user Helper method to get a number input from the user
@@ -73,7 +73,7 @@ def get_number_input(
if allow_go_back and _input in options_go_back: if allow_go_back and _input in options_go_back:
return None return None
if _input == "": if _input == "" and default is not None:
return default return default
try: try:
@@ -84,10 +84,10 @@ def get_number_input(
def get_string_input( def get_string_input(
question: str, question: str,
regex: Union[str, None] = None, regex: str | None = None,
exclude: Union[List, None] = None, exclude: List[str] | None = None,
allow_special_chars=False, allow_special_chars: bool = False,
default=None, default: str | None = None,
) -> str: ) -> str:
""" """
Helper method to get a string input from the user Helper method to get a string input from the user
@@ -157,7 +157,7 @@ def format_question(question: str, default=None) -> str:
return f"{COLOR_CYAN}###### {formatted_q}: {RESET_FORMAT}" return f"{COLOR_CYAN}###### {formatted_q}: {RESET_FORMAT}"
def validate_number_input(value: str, min_count: int, max_count: int) -> int: def validate_number_input(value: str, min_count: int, max_count: int | None) -> int:
""" """
Helper method for a simple number input validation. | Helper method for a simple number input validation. |
:param value: The value to validate :param value: The value to validate

View File

@@ -6,6 +6,8 @@
# # # #
# 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 __future__ import annotations
import textwrap import textwrap
from enum import Enum from enum import Enum
from typing import List from typing import List
@@ -44,17 +46,17 @@ LINE_WIDTH = 53
class Logger: class Logger:
@staticmethod @staticmethod
def info(msg): def info(msg) -> None:
# log to kiauh.log # log to kiauh.log
pass pass
@staticmethod @staticmethod
def warn(msg): def warn(msg) -> None:
# log to kiauh.log # log to kiauh.log
pass pass
@staticmethod @staticmethod
def error(msg): def error(msg) -> None:
# log to kiauh.log # log to kiauh.log
pass pass
@@ -88,8 +90,8 @@ class Logger:
title: DialogType, title: DialogType,
content: List[str], content: List[str],
center_content: bool = False, center_content: bool = False,
custom_title: str = None, custom_title: str | None = None,
custom_color: DialogCustomColor = None, custom_color: DialogCustomColor | None = None,
padding_top: int = 1, padding_top: int = 1,
padding_bottom: int = 1, padding_bottom: int = 1,
) -> None: ) -> None:
@@ -122,18 +124,23 @@ class Logger:
print("\n" * padding_bottom) print("\n" * padding_bottom)
@staticmethod @staticmethod
def _get_dialog_title(title: DialogType, custom_title: str = None) -> str: def _get_dialog_title(
title: DialogType, custom_title: str | None = None
) -> str | None:
if title == DialogType.CUSTOM and custom_title: if title == DialogType.CUSTOM and custom_title:
return f"[ {custom_title} ]" return f"[ {custom_title} ]"
return f"[ {title.value[0]} ]" if title.value[0] else None return f"[ {title.value[0]} ]" if title.value[0] else None
@staticmethod @staticmethod
def _get_dialog_color( def _get_dialog_color(
title: DialogType, custom_color: DialogCustomColor = None title: DialogType, custom_color: DialogCustomColor | None = None
) -> str: ) -> str:
if title == DialogType.CUSTOM and custom_color: if title == DialogType.CUSTOM and custom_color:
return str(custom_color.value) return str(custom_color.value)
return title.value[1] if title.value[1] else DialogCustomColor.WHITE.value
color: str = title.value[1] if title.value[1] else DialogCustomColor.WHITE.value
return color
@staticmethod @staticmethod
def _format_top_border(color: str) -> str: def _format_top_border(color: str) -> str:
@@ -146,7 +153,7 @@ class Logger:
) )
@staticmethod @staticmethod
def _format_dialog_title(title: str) -> str: def _format_dialog_title(title: str | None) -> str:
if title is not None: if title is not None:
return textwrap.dedent(f""" return textwrap.dedent(f"""
{title:^{LINE_WIDTH}} {title:^{LINE_WIDTH}}

View File

@@ -1,17 +1,18 @@
import sys import sys
import threading import threading
import time import time
from typing import List
class Spinner: class Spinner:
def __init__(self, message="Loading", delay=0.2): def __init__(self, message: str = "Loading", delay: float = 0.2) -> None:
self.message = f"{message} ..." self.message = f"{message} ..."
self.delay = delay self.delay = delay
self._stop_event = threading.Event() self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._animate) self._thread = threading.Thread(target=self._animate)
def _animate(self): def _animate(self) -> None:
animation = ["", "", "", ""] animation: List[str] = ["", "", "", ""]
while not self._stop_event.is_set(): while not self._stop_event.is_set():
for char in animation: for char in animation:
sys.stdout.write(f"\r{char} {self.message}") sys.stdout.write(f"\r{char} {self.message}")
@@ -22,12 +23,12 @@ class Spinner:
sys.stdout.write("\r" + " " * (len(self.message) + 1) + "\r") sys.stdout.write("\r" + " " * (len(self.message) + 1) + "\r")
sys.stdout.flush() sys.stdout.flush()
def start(self): def start(self) -> None:
self._stop_event.clear() self._stop_event.clear()
if not self._thread.is_alive(): if not self._thread.is_alive():
self._thread = threading.Thread(target=self._animate) self._thread = threading.Thread(target=self._animate)
self._thread.start() self._thread.start()
def stop(self): def stop(self) -> None:
self._stop_event.set() self._stop_event.set()
self._thread.join() self._thread.join()

View File

@@ -6,6 +6,7 @@
# # # #
# 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 __future__ import annotations
import os import os
import re import re
@@ -122,12 +123,13 @@ def update_python_pip(target: Path) -> None:
""" """
Logger.print_status("Updating pip ...") Logger.print_status("Updating pip ...")
try: try:
pip_location = target.joinpath("bin/pip") pip_location: Path = target.joinpath("bin/pip")
pip_exists = check_file_exist(pip_location) pip_exists: bool = check_file_exist(pip_location)
if not pip_exists: if not pip_exists:
raise FileNotFoundError("Error updating pip! Not found.") raise FileNotFoundError("Error updating pip! Not found.")
command = [pip_location, "install", "-U", "pip"] command = [pip_location.as_posix(), "install", "-U", "pip"]
result = run(command, stderr=PIPE, text=True) result = run(command, stderr=PIPE, text=True)
if result.returncode != 0 or result.stderr: if result.returncode != 0 or result.stderr:
Logger.print_error(f"{result.stderr}", False) Logger.print_error(f"{result.stderr}", False)
@@ -156,7 +158,7 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
Logger.print_status("Installing Python requirements ...") Logger.print_status("Installing Python requirements ...")
command = [ command = [
target.joinpath("bin/pip"), target.joinpath("bin/pip").as_posix(),
"install", "install",
"-r", "-r",
f"{requirements}", f"{requirements}",
@@ -182,8 +184,8 @@ def update_system_package_lists(silent: bool, rls_info_change=False) -> None:
:param rls_info_change: Flag for "--allow-releaseinfo-change" :param rls_info_change: Flag for "--allow-releaseinfo-change"
:return: None :return: None
""" """
cache_mtime = 0 cache_mtime: float = 0
cache_files = [ cache_files: List[Path] = [
Path("/var/lib/apt/periodic/update-success-stamp"), Path("/var/lib/apt/periodic/update-success-stamp"),
Path("/var/lib/apt/lists"), Path("/var/lib/apt/lists"),
] ]
@@ -269,7 +271,7 @@ def get_ipv4_addr() -> str:
try: try:
# doesn't even have to be reachable # doesn't even have to be reachable
s.connect(("192.255.255.255", 1)) s.connect(("192.255.255.255", 1))
return s.getsockname()[0] return str(s.getsockname()[0])
except Exception: except Exception:
return "127.0.0.1" return "127.0.0.1"
finally: finally:
@@ -329,9 +331,9 @@ def set_nginx_permissions() -> None:
""" """
cmd = f"ls -ld {Path.home()} | cut -d' ' -f1" cmd = f"ls -ld {Path.home()} | cut -d' ' -f1"
homedir_perm = run(cmd, shell=True, stdout=PIPE, text=True) homedir_perm = run(cmd, shell=True, stdout=PIPE, text=True)
homedir_perm = homedir_perm.stdout permissions = homedir_perm.stdout
if homedir_perm.count("x") < 3: if permissions.count("x") < 3:
Logger.print_status("Granting NGINX the required permissions ...") Logger.print_status("Granting NGINX the required permissions ...")
run(["chmod", "og+x", Path.home()]) run(["chmod", "og+x", Path.home()])
Logger.print_ok("Permissions granted.") Logger.print_ok("Permissions granted.")
@@ -363,7 +365,7 @@ def cmd_sysctl_manage(action: SysCtlManageAction) -> None:
raise raise
def service_instance_exists(name: str, exclude: List[str] = None) -> bool: def service_instance_exists(name: str, exclude: List[str] | None = None) -> bool:
""" """
Checks if a systemd service instance exists. Checks if a systemd service instance exists.
:param name: the service name :param name: the service name
@@ -387,6 +389,7 @@ def log_process(process: Popen) -> None:
:return: None :return: None
""" """
while True: while True:
if process.stdout is not None:
reads = [process.stdout.fileno()] reads = [process.stdout.fileno()]
ret = select.select(reads, [], []) ret = select.select(reads, [], [])
for fd in ret[0]: for fd in ret[0]:

View File

@@ -2,7 +2,7 @@
requires-python = ">=3.8" requires-python = ">=3.8"
[project.optional-dependencies] [project.optional-dependencies]
dev=["ruff"] dev=["ruff", "mypy"]
[tool.ruff] [tool.ruff]
required-version = ">=0.3.4" required-version = ">=0.3.4"
@@ -19,3 +19,14 @@ quote-style = "double"
[tool.ruff.lint] [tool.ruff.lint]
extend-select = ["I"] extend-select = ["I"]
[tool.mypy]
python_version = "3.8"
platform = "linux"
# strict = true # TODO: enable this once everything is else is handled
check_untyped_defs = true
ignore_missing_imports = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_return_any = true
warn_unreachable = true