Compare commits

...

8 Commits

Author SHA1 Message Date
Maksym Pyrozhok
985b66d41f chore: fix typos (#695)
Fix typo.
2025-07-12 19:36:38 +02:00
dw-0
f95d2586bf fix(webclient): add config.json to moonraker persistent files (#699)
fixes #694

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-06-28 10:12:28 +02:00
dw-0
f5141e7eff fix(mainsail): check for json configured as instanceDB (#698)
fix(mainsail): check for json configures as instanceDB

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-06-27 22:37:44 +02:00
dw-0
33113e72e9 fix: exception raised on pip warning (#688)
pip seems to write to stderr on warnings, caused by retries. even if the process exits with 0.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-05-31 17:44:02 +02:00
dw-0
6f59fd06aa fix: do not upgrade pip before installing packages (#680)
pip 25 seems to introduce some compatibility issues.

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-05-02 20:08:32 +02:00
dw-0
56ea43ccb6 refactor: improve typesafety KiauhSettings
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:19:38 +02:00
dw-0
25e22c993f chore(scp): update SimpleConfigParser
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:15:12 +02:00
dw-0
ead521b377 refactor: replace mypy with pyright
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2025-04-14 21:07:56 +02:00
14 changed files with 92 additions and 93 deletions

View File

@@ -102,6 +102,7 @@ def install_client(
section=f"update_manager {client.name}",
instances=mr_instances,
options=[
("persistent_files", ["config.json"]),
("type", "web"),
("channel", "stable"),
("repo", str(client.repo_path)),

View File

@@ -118,8 +118,8 @@ def enable_mainsail_remotemode() -> None:
c_json = MainsailData().client_dir.joinpath("config.json")
with open(c_json, "r") as f:
config_data = json.load(f)
if config_data["instancesDB"] == "browser":
if config_data["instancesDB"] == "browser" or config_data["instancesDB"] == "json":
Logger.print_info("Remote mode already configured. Skipped ...")
return

View File

@@ -10,7 +10,7 @@ from __future__ import annotations
import shutil
from dataclasses import dataclass, field
from typing import Any, List, Literal
from typing import Any, Callable, List, TypeVar
from components.klipper import KLIPPER_REPO_URL
from components.moonraker import MOONRAKER_REPO_URL
@@ -27,6 +27,8 @@ from kiauh import PROJECT_ROOT
DEFAULT_CFG = PROJECT_ROOT.joinpath("default.kiauh.cfg")
CUSTOM_CFG = PROJECT_ROOT.joinpath("kiauh.cfg")
T = TypeVar("T")
class InvalidValueError(Exception):
"""Raised when a value is invalid for an option"""
@@ -152,8 +154,7 @@ class KiauhSettings:
self.kiauh.backup_before_update = self.__read_from_cfg(
"kiauh",
"backup_before_update",
"bool",
False,
self.config.getboolean,
False,
)
@@ -161,16 +162,15 @@ class KiauhSettings:
self.klipper.use_python_binary = self.__read_from_cfg(
"klipper",
"use_python_binary",
"str",
self.config.getval,
None,
True,
)
kl_repos: List[str] = self.__read_from_cfg(
"klipper",
"repositories",
"list",
self.config.getvals,
[KLIPPER_REPO_URL],
False,
)
self.klipper.repositories = self.__set_repo_state("klipper", kl_repos)
@@ -178,23 +178,21 @@ class KiauhSettings:
self.moonraker.use_python_binary = self.__read_from_cfg(
"moonraker",
"use_python_binary",
"str",
self.config.getval,
None,
True,
)
self.moonraker.optional_speedups = self.__read_from_cfg(
"moonraker",
"optional_speedups",
"bool",
self.config.getboolean,
True,
False,
)
mr_repos: List[str] = self.__read_from_cfg(
"moonraker",
"repositories",
"list",
self.config.getvals,
[MOONRAKER_REPO_URL],
False,
)
self.moonraker.repositories = self.__set_repo_state("moonraker", mr_repos)
@@ -202,15 +200,13 @@ class KiauhSettings:
self.mainsail.port = self.__read_from_cfg(
"mainsail",
"port",
"int",
self.config.getint,
80,
False,
)
self.mainsail.unstable_releases = self.__read_from_cfg(
"mainsail",
"unstable_releases",
"bool",
False,
self.config.getboolean,
False,
)
@@ -218,49 +214,52 @@ class KiauhSettings:
self.fluidd.port = self.__read_from_cfg(
"fluidd",
"port",
"int",
self.config.getint,
80,
False,
)
self.fluidd.unstable_releases = self.__read_from_cfg(
"fluidd",
"unstable_releases",
"bool",
False,
self.config.getboolean,
False,
)
def __check_option_exists(
self, section: str, option: str, fallback: Any, silent: bool = False
) -> bool:
has_section = self.config.has_section(section)
has_option = self.config.has_option(section, option)
if not (has_section and has_option):
if not silent:
Logger.print_warn(
f"Option '{option}' in section '{section}' not defined. Falling back to '{fallback}'."
)
return False
return True
def __read_bool_from_cfg(
self,
section: str,
option: str,
fallback: bool | None = None,
silent: bool = False,
) -> bool | None:
if not self.__check_option_exists(section, option, fallback, silent):
return fallback
return self.config.getboolean(section, option, fallback)
def __read_from_cfg(
self,
section: str,
option: str,
value_type: Literal["str", "int", "bool", "list"] = "str",
fallback: str | int | bool | List[str] | None = None,
silent_fallback: bool = False,
) -> str | int | bool | List[str] | None:
has_section = self.config.has_section(section)
has_option = self.config.has_option(section, option)
if not (has_section and has_option):
if not silent_fallback:
Logger.print_warn(
f"Option '{option}' in section '{section}' not defined. Falling back to '{fallback}'."
)
return fallback
try:
return {
"int": self.config.getint,
"bool": self.config.getboolean,
"list": self.config.getval,
"str": self.config.getval,
}[value_type](section, option)
except (ValueError, TypeError) as e:
if not silent_fallback:
Logger.print_warn(
f"Failed to parse value of option '{option}' in section '{section}' as {value_type}. Falling back to '{fallback}'. Error: {e}"
)
getter: Callable[[str, str, T | None], T],
fallback: T = None,
silent: bool = False,
) -> T:
if not self.__check_option_exists(section, option, fallback, silent):
return fallback
return getter(section, option, fallback)
def __set_repo_state(self, section: str, repos: List[str]) -> List[Repository]:
_repos: List[Repository] = []

View File

@@ -314,9 +314,7 @@ class SimpleConfigParser:
elements.pop(i)
break
def getval(
self, section: str, option: str, fallback: str | _UNSET = _UNSET
) -> str | List[str]:
def getval(self, section: str, option: str, fallback: str | _UNSET = _UNSET) -> str:
"""
Return the value of the given option in the given section
@@ -329,22 +327,34 @@ class SimpleConfigParser:
if option not in self.get_options(section):
raise NoOptionError(option, section)
# Find the option in the elements list
for element in self.config[section]["elements"]:
if element["type"] in [LineType.OPTION.value, LineType.OPTION_BLOCK.value] and element["name"] == option:
raw_value = element["value"]
if isinstance(raw_value, str) and raw_value.endswith("\n"):
return raw_value[:-1].strip()
elif isinstance(raw_value, list):
values: List[str] = []
for i, val in enumerate(raw_value):
val = val.strip().strip("\n")
if len(val) < 1:
continue
values.append(val.strip())
return values
return str(raw_value)
raise NoOptionError(option, section)
if element["type"] is LineType.OPTION.value and element["name"] == option:
return str(element["value"].strip().replace("\n", ""))
return ""
except (NoSectionError, NoOptionError):
if fallback is _UNSET:
raise
return fallback
def getvals(self, section: str, option: str, fallback: List[str] | _UNSET = _UNSET) -> List[str]:
"""
Return the values of the given multi-line option in the given section
If the key is not found and 'fallback' is provided, it is used as
a fallback value.
"""
try:
if section not in self.get_sections():
raise NoSectionError(section)
if option not in self.get_options(section):
raise NoOptionError(option, section)
for element in self.config[section]["elements"]:
if element["type"] is LineType.OPTION_BLOCK.value and element["name"] == option:
return [val.strip() for val in element["value"] if val.strip()]
return []
except (NoSectionError, NoOptionError):
if fallback is _UNSET:
raise

View File

@@ -51,7 +51,7 @@ def test_getval(parser):
assert parser.getval("section_2", "option_2") == "value_2"
# test multiline option values
ml_val = parser.getval("section number 5", "multi_option")
ml_val = parser.getvals("section number 5", "multi_option")
assert isinstance(ml_val, list)
assert len(ml_val) > 0
@@ -164,7 +164,7 @@ def test_set_new_option(parser):
assert parser.getval("new_section", "very_new_option") == "very_new_value"
parser.set_option("section_2", "array_option", ["value_1", "value_2", "value_3"])
assert parser.getval("section_2", "array_option") == [
assert parser.getvals("section_2", "array_option") == [
"value_1",
"value_2",
"value_3",

View File

@@ -11,7 +11,7 @@ from __future__ import annotations
import shutil
import tempfile
from pathlib import Path
from typing import List, Tuple
from typing import List, Tuple, Union
from core.logger import Logger
from core.submodules.simple_config_parser.src.simple_config_parser.simple_config_parser import (
@@ -19,7 +19,7 @@ from core.submodules.simple_config_parser.src.simple_config_parser.simple_config
)
from utils.instance_type import InstanceType
ConfigOption = Tuple[str, str]
ConfigOption = Tuple[str, Union[str, List[str]]]
def add_config_section(

View File

@@ -184,9 +184,6 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
:return: None
"""
try:
# always update pip before installing requirements
update_python_pip(target)
Logger.print_status("Installing Python requirements ...")
command = [
target.joinpath("bin/pip").as_posix(),
@@ -196,7 +193,7 @@ def install_python_requirements(target: Path, requirements: Path) -> None:
]
result = run(command, stderr=PIPE, text=True)
if result.returncode != 0 or result.stderr:
if result.returncode != 0:
Logger.print_error(f"{result.stderr}", False)
raise VenvCreationFailedException("Installing Python requirements failed!")
@@ -216,9 +213,6 @@ def install_python_packages(target: Path, packages: List[str]) -> None:
:return: None
"""
try:
# always update pip before installing requirements
update_python_pip(target)
Logger.print_status("Installing Python requirements ...")
command = [
target.joinpath("bin/pip").as_posix(),
@@ -228,7 +222,7 @@ def install_python_packages(target: Path, packages: List[str]) -> None:
command.append(pkg)
result = run(command, stderr=PIPE, text=True)
if result.returncode != 0 or result.stderr:
if result.returncode != 0:
Logger.print_error(f"{result.stderr}", False)
raise VenvCreationFailedException("Installing Python requirements failed!")

View File

@@ -2,7 +2,7 @@
requires-python = ">=3.8"
[project.optional-dependencies]
dev=["ruff", "mypy"]
dev=["ruff", "pyright"]
[tool.ruff]
required-version = ">=0.9.10"
@@ -20,14 +20,3 @@ quote-style = "double"
[tool.ruff.lint]
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

6
pyrightconfig.json Normal file
View File

@@ -0,0 +1,6 @@
{
"pythonVersion": "3.8",
"pythonPlatform": "Linux",
"typeCheckingMode": "standard",
"venvPath": "./.kiauh-env"
}

2
requirements-dev.txt Normal file
View File

@@ -0,0 +1,2 @@
ruff (>=0.9.10)
pyright

View File

@@ -280,7 +280,6 @@ function create_klipper_virtualenv() {
status_msg "Installing $("python${python_version}" -V) virtual environment..."
if virtualenv -p "python${python_version}" "${KLIPPY_ENV}"; then
(( python_version == 3 )) && "${KLIPPY_ENV}"/bin/pip install -U pip
"${KLIPPY_ENV}"/bin/pip install -r "${KLIPPER_DIR}"/scripts/klippy-requirements.txt
else
log_error "failure while creating python3 klippy-env"

View File

@@ -126,7 +126,7 @@ function update_klipperscreen() {
git checkout -f master && ok_msg "Checkout successfull"
if [[ $(md5sum "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then
status_msg "New dependecies detected..."
status_msg "New dependencies detected..."
"${KLIPPERSCREEN_ENV}"/bin/pip install -r "${KLIPPERSCREEN_DIR}/scripts/KlipperScreen-requirements.txt"
ok_msg "Dependencies have been installed!"
fi

View File

@@ -133,7 +133,7 @@ function update_mobileraker() {
git checkout -f main && ok_msg "Checkout successfull"
if [[ $(md5sum "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt" | cut -d " " -f1) != "${old_md5}" ]]; then
status_msg "New dependecies detected..."
status_msg "New dependencies detected..."
"${MOBILERAKER_ENV}"/bin/pip install -r "${MOBILERAKER_DIR}/scripts/mobileraker-requirements.txt"
ok_msg "Dependencies have been installed!"
fi

View File

@@ -336,7 +336,6 @@ function create_moonraker_virtualenv() {
[[ -d ${MOONRAKER_ENV} ]] && rm -rf "${MOONRAKER_ENV}"
if virtualenv -p /usr/bin/python3 "${MOONRAKER_ENV}"; then
"${MOONRAKER_ENV}"/bin/pip install -U pip
"${MOONRAKER_ENV}"/bin/pip install -r "${MOONRAKER_DIR}/scripts/moonraker-requirements.txt"
else
log_error "failure while creating python3 moonraker-env"