From c67ac97f180f03aff13ff5c44e8865258a732e0a Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Thu, 15 May 2025 22:26:41 +1000 Subject: [PATCH] Ruff formatting (#3459) * Update * Update --- .flake8 | 2 +- .github/workflows/ruff-format.yaml | 12 + .../workflows/{ruff.yaml => ruff-lint.yaml} | 2 +- .pre-commit-config.yaml | 29 +- archinstall/__init__.py | 48 +- archinstall/__main__.py | 2 +- .../default_profiles/applications/pipewire.py | 31 +- archinstall/default_profiles/desktop.py | 39 +- .../default_profiles/desktops/__init__.py | 4 +- .../default_profiles/desktops/awesome.py | 36 +- .../default_profiles/desktops/bspwm.py | 12 +- .../default_profiles/desktops/budgie.py | 4 +- .../default_profiles/desktops/cinnamon.py | 4 +- .../default_profiles/desktops/cosmic.py | 2 +- .../default_profiles/desktops/cutefish.py | 4 +- .../default_profiles/desktops/deepin.py | 4 +- .../desktops/enlightenment.py | 4 +- .../default_profiles/desktops/gnome.py | 6 +- .../default_profiles/desktops/hyprland.py | 18 +- archinstall/default_profiles/desktops/i3.py | 20 +- .../default_profiles/desktops/labwc.py | 20 +- archinstall/default_profiles/desktops/lxqt.py | 4 +- archinstall/default_profiles/desktops/mate.py | 4 +- archinstall/default_profiles/desktops/niri.py | 22 +- .../default_profiles/desktops/plasma.py | 4 +- .../default_profiles/desktops/qtile.py | 6 +- .../default_profiles/desktops/river.py | 8 +- archinstall/default_profiles/desktops/sway.py | 22 +- .../default_profiles/desktops/xfce4.py | 4 +- .../default_profiles/desktops/xmonad.py | 12 +- archinstall/default_profiles/minimal.py | 2 +- archinstall/default_profiles/profile.py | 45 +- archinstall/default_profiles/server.py | 27 +- .../default_profiles/servers/cockpit.py | 8 +- .../default_profiles/servers/docker.py | 13 +- archinstall/default_profiles/servers/httpd.py | 8 +- .../default_profiles/servers/lighttpd.py | 8 +- .../default_profiles/servers/mariadb.py | 12 +- archinstall/default_profiles/servers/nginx.py | 8 +- .../default_profiles/servers/postgresql.py | 10 +- archinstall/default_profiles/servers/sshd.py | 8 +- .../default_profiles/servers/tomcat.py | 8 +- archinstall/default_profiles/tailored.py | 6 +- archinstall/default_profiles/xorg.py | 12 +- archinstall/lib/args.py | 182 ++--- archinstall/lib/boot.py | 42 +- archinstall/lib/configuration.py | 56 +- archinstall/lib/crypt.py | 46 +- archinstall/lib/disk/device_handler.py | 314 ++++--- archinstall/lib/disk/disk_menu.py | 44 +- archinstall/lib/disk/encryption_menu.py | 97 ++- archinstall/lib/disk/fido.py | 26 +- archinstall/lib/disk/filesystem.py | 63 +- archinstall/lib/disk/partitioning_menu.py | 213 +++-- archinstall/lib/disk/subvolume_menu.py | 20 +- archinstall/lib/disk/utils.py | 24 +- archinstall/lib/exceptions.py | 6 +- archinstall/lib/general.py | 67 +- archinstall/lib/global_menu.py | 226 +++--- archinstall/lib/hardware.py | 152 ++-- archinstall/lib/installer.py | 763 +++++++++--------- archinstall/lib/interactions/__init__.py | 44 +- archinstall/lib/interactions/disk_conf.py | 148 ++-- archinstall/lib/interactions/general_conf.py | 100 +-- .../lib/interactions/manage_users_conf.py | 34 +- archinstall/lib/interactions/network_menu.py | 64 +- archinstall/lib/interactions/system_conf.py | 36 +- archinstall/lib/locale/__init__.py | 14 +- archinstall/lib/locale/locale_menu.py | 36 +- archinstall/lib/locale/utils.py | 54 +- archinstall/lib/luks.py | 104 +-- archinstall/lib/menu/__init__.py | 6 +- archinstall/lib/menu/abstract_menu.py | 16 +- archinstall/lib/menu/list_manager.py | 26 +- archinstall/lib/menu/menu_helper.py | 8 +- archinstall/lib/mirrors.py | 152 ++-- archinstall/lib/models/__init__.py | 84 +- archinstall/lib/models/audio_configuration.py | 16 +- archinstall/lib/models/bootloader.py | 10 +- archinstall/lib/models/device_model.py | 604 +++++++------- archinstall/lib/models/locale.py | 38 +- archinstall/lib/models/mirrors.py | 118 +-- .../lib/models/network_configuration.py | 74 +- archinstall/lib/models/packages.py | 56 +- archinstall/lib/models/profile_model.py | 18 +- archinstall/lib/models/users.py | 88 +- archinstall/lib/networking.py | 55 +- archinstall/lib/output.py | 144 ++-- archinstall/lib/packages/__init__.py | 14 +- archinstall/lib/packages/packages.py | 34 +- archinstall/lib/pacman/__init__.py | 43 +- archinstall/lib/pacman/config.py | 12 +- archinstall/lib/plugins.py | 30 +- archinstall/lib/profile/profile_menu.py | 77 +- archinstall/lib/profile/profiles_handler.py | 94 +-- archinstall/lib/storage.py | 4 +- archinstall/lib/translationhandler.py | 28 +- archinstall/lib/utils/unicode.py | 6 +- archinstall/lib/utils/util.py | 28 +- archinstall/scripts/guided.py | 22 +- archinstall/scripts/list.py | 2 +- archinstall/scripts/minimal.py | 14 +- archinstall/scripts/only_hd.py | 16 +- archinstall/scripts/unattended.py | 6 +- archinstall/tui/__init__.py | 26 +- archinstall/tui/curses_menu.py | 178 ++-- archinstall/tui/help.py | 65 +- archinstall/tui/menu_item.py | 66 +- archinstall/tui/types.py | 8 +- docs/conf.py | 32 +- examples/auto_discovery_mounted.py | 2 +- examples/full_automated_installation.py | 28 +- examples/interactive_installation.py | 22 +- examples/mac_address_installation.py | 6 +- examples/minimal_installation.py | 14 +- examples/only_hd_installation.py | 16 +- tests/conftest.py | 32 +- tests/test_args.py | 289 ++++--- tests/test_configuration_output.py | 22 +- tests/test_mirrorlist.py | 22 +- 120 files changed, 3167 insertions(+), 3143 deletions(-) create mode 100644 .github/workflows/ruff-format.yaml rename .github/workflows/{ruff.yaml => ruff-lint.yaml} (91%) diff --git a/.flake8 b/.flake8 index 671719a6..6ca7c045 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] count = True -ignore = W191,W503 +ignore = W191,W503,E704,E203 max-complexity = 40 max-line-length = 160 show-source = True diff --git a/.github/workflows/ruff-format.yaml b/.github/workflows/ruff-format.yaml new file mode 100644 index 00000000..148ac82d --- /dev/null +++ b/.github/workflows/ruff-format.yaml @@ -0,0 +1,12 @@ +on: [ push, pull_request ] +name: ruff check formatting +jobs: + ruff_format_check: + runs-on: ubuntu-latest + container: + image: archlinux/archlinux:latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: astral-sh/ruff-action@84f83ecf9e1e15d26b7984c7ec9cf73d39ffc946 # v3.3.1 + - run: ruff check --select=COM812 --fix + - run: ruff format --diff diff --git a/.github/workflows/ruff.yaml b/.github/workflows/ruff-lint.yaml similarity index 91% rename from .github/workflows/ruff.yaml rename to .github/workflows/ruff-lint.yaml index 1e34fb7f..a4b018c8 100644 --- a/.github/workflows/ruff.yaml +++ b/.github/workflows/ruff-lint.yaml @@ -1,5 +1,5 @@ on: [ push, pull_request ] -name: ruff linting +name: ruff check linting jobs: ruff: runs-on: ubuntu-latest diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 789f534c..365abb06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,15 @@ default_stages: ['pre-commit'] repos: - - repo: https://github.com/pycqa/autoflake - rev: v2.3.1 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.11.9 hooks: - - id: autoflake - args: [ - '--in-place', - '--remove-all-unused-imports', - '--ignore-init-module-imports' - ] - files: \.py$ - require_serial: true - fail_fast: true + # fix unused imports and sort them + - id: ruff + args: ["--extend-select", "I", "--fix"] + # format the code + - id: ruff-format + # run the linter + - id: ruff - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: @@ -23,8 +21,6 @@ repos: - id: check-yaml # Attempts to load all yaml files to verify syntax - id: destroyed-symlinks # Detects symlinks which are changed to regular files - id: detect-private-key # Checks for the existence of private keys - - id: end-of-file-fixer # Makes sure files end in a newline and only a newline - - id: trailing-whitespace # Trims trailing whitespace # Python specific hooks: - id: check-ast # Simply check whether files parse as valid python - id: check-docstring-first # Checks for a common error of placing code before the docstring @@ -47,13 +43,6 @@ repos: - pydantic - pytest - cryptography - - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.9 - hooks: - - id: ruff - args: ["check", "--select", "I", "--fix"] - fail_fast: true - - id: ruff - repo: local hooks: - id: pylint diff --git a/archinstall/__init__.py b/archinstall/__init__.py index edbb3d30..d05a0d6f 100644 --- a/archinstall/__init__.py +++ b/archinstall/__init__.py @@ -46,7 +46,7 @@ def _fetch_arch_db() -> None: try: Pacman.run("-Sy") except Exception as e: - debug(f'Failed to sync Arch Linux package database: {e}') + debug(f"Failed to sync Arch Linux package database: {e}") exit(1) @@ -57,10 +57,10 @@ def _check_new_version() -> None: try: upgrade = Pacman.run("-Qu archinstall").decode() except Exception as e: - debug(f'Failed determine pacman version: {e}') + debug(f"Failed determine pacman version: {e}") if upgrade: - text = f'New version available: {upgrade}' + text = f"New version available: {upgrade}" info(text) time.sleep(3) @@ -71,7 +71,7 @@ def main() -> int: OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ - if '--help' in sys.argv or '-h' in sys.argv: + if "--help" in sys.argv or "-h" in sys.argv: arch_config_handler.print_help() return 0 @@ -89,7 +89,7 @@ def main() -> int: script = arch_config_handler.args.script - mod_name = f'archinstall.scripts.{script}' + mod_name = f"archinstall.scripts.{script}" # by loading the module we'll automatically run the script importlib.import_module(mod_name) @@ -109,13 +109,13 @@ def run_as_a_module() -> None: Tui.shutdown() if exc: - err = ''.join(traceback.format_exception(exc)) + err = "".join(traceback.format_exception(exc)) error(err) text = ( - 'Archinstall experienced the above error. If you think this is a bug, please report it to\n' + "Archinstall experienced the above error. If you think this is a bug, please report it to\n" 'https://github.com/archlinux/archinstall and include the log file "/var/log/archinstall/install.log".\n\n' - 'Hint: To extract the log from a live ISO \ncurl -F\'file=@/var/log/archinstall/install.log\' https://0x0.st\n' + "Hint: To extract the log from a live ISO \ncurl -F'file=@/var/log/archinstall/install.log' https://0x0.st\n" ) warn(text) @@ -125,20 +125,20 @@ def run_as_a_module() -> None: __all__ = [ - 'DeferredTranslation', - 'FormattedOutput', - 'Language', - 'Pacman', - 'SysInfo', - 'Tui', - 'arch_config_handler', - 'debug', - 'disk_layouts', - 'error', - 'info', - 'load_plugin', - 'log', - 'plugin', - 'translation_handler', - 'warn', + "DeferredTranslation", + "FormattedOutput", + "Language", + "Pacman", + "SysInfo", + "Tui", + "arch_config_handler", + "debug", + "disk_layouts", + "error", + "info", + "load_plugin", + "log", + "plugin", + "translation_handler", + "warn", ] diff --git a/archinstall/__main__.py b/archinstall/__main__.py index c8a4779b..ae61f2b3 100644 --- a/archinstall/__main__.py +++ b/archinstall/__main__.py @@ -1,4 +1,4 @@ import archinstall -if __name__ == '__main__': +if __name__ == "__main__": archinstall.run_as_a_module() diff --git a/archinstall/default_profiles/applications/pipewire.py b/archinstall/default_profiles/applications/pipewire.py index 15cc38f1..eb2f032f 100644 --- a/archinstall/default_profiles/applications/pipewire.py +++ b/archinstall/default_profiles/applications/pipewire.py @@ -9,23 +9,24 @@ if TYPE_CHECKING: class PipewireProfile(Profile): def __init__(self) -> None: - super().__init__('Pipewire', ProfileType.Application) + super().__init__("Pipewire", ProfileType.Application) @property @override def packages(self) -> list[str]: return [ - 'pipewire', - 'pipewire-alsa', - 'pipewire-jack', - 'pipewire-pulse', - 'gst-plugin-pipewire', - 'libpulse', - 'wireplumber' + "pipewire", + "pipewire-alsa", + "pipewire-jack", + "pipewire-pulse", + "gst-plugin-pipewire", + "libpulse", + "wireplumber", ] - def _enable_pipewire_for_all(self, install_session: 'Installer') -> None: + def _enable_pipewire_for_all(self, install_session: "Installer") -> None: from archinstall.lib.args import arch_config_handler + users: list[User] | None = arch_config_handler.config.users if users is None: @@ -37,20 +38,20 @@ class PipewireProfile(Profile): service_dir.mkdir(parents=True, exist_ok=True) # Set ownership of the entire user catalogue - install_session.arch_chroot(f'chown -R {user.username}:{user.username} /home/{user.username}') + install_session.arch_chroot(f"chown -R {user.username}:{user.username} /home/{user.username}") # symlink in the correct pipewire systemd items install_session.arch_chroot( - f'ln -sf /usr/lib/systemd/user/pipewire-pulse.service /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.service', - run_as=user.username + f"ln -sf /usr/lib/systemd/user/pipewire-pulse.service /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.service", + run_as=user.username, ) install_session.arch_chroot( - f'ln -sf /usr/lib/systemd/user/pipewire-pulse.socket /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.socket', - run_as=user.username + f"ln -sf /usr/lib/systemd/user/pipewire-pulse.socket /home/{user.username}/.config/systemd/user/default.target.wants/pipewire-pulse.socket", + run_as=user.username, ) @override - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: super().install(install_session) install_session.add_additional_packages(self.packages) self._enable_pipewire_for_all(install_session) diff --git a/archinstall/default_profiles/desktop.py b/archinstall/default_profiles/desktop.py index 64555342..3be732b0 100644 --- a/archinstall/default_profiles/desktop.py +++ b/archinstall/default_profiles/desktop.py @@ -15,26 +15,26 @@ if TYPE_CHECKING: class DesktopProfile(Profile): def __init__(self, current_selection: list[Profile] = []) -> None: super().__init__( - 'Desktop', + "Desktop", ProfileType.Desktop, current_selection=current_selection, - support_greeter=True + support_greeter=True, ) @property @override def packages(self) -> list[str]: return [ - 'nano', - 'vim', - 'openssh', - 'htop', - 'wget', - 'iwd', - 'wireless_tools', - 'wpa_supplicant', - 'smartmontools', - 'xdg-utils' + "nano", + "vim", + "openssh", + "htop", + "wget", + "iwd", + "wireless_tools", + "wpa_supplicant", + "smartmontools", + "xdg-utils", ] @property @@ -61,8 +61,9 @@ class DesktopProfile(Profile): MenuItem( p.name, value=p, - preview_action=lambda x: x.value.preview_text() - ) for p in profile_handler.get_desktop_profiles() + preview_action=lambda x: x.value.preview_text(), + ) + for p in profile_handler.get_desktop_profiles() ] group = MenuItemGroup(items, sort_items=True, sort_case_sensitive=False) @@ -74,8 +75,8 @@ class DesktopProfile(Profile): allow_reset=True, allow_skip=True, preview_style=PreviewStyle.RIGHT, - preview_size='auto', - preview_frame=FrameProperties.max('Info') + preview_size="auto", + preview_frame=FrameProperties.max("Info"), ).run() match result.type_: @@ -89,17 +90,17 @@ class DesktopProfile(Profile): return SelectResult.ResetCurrent @override - def post_install(self, install_session: 'Installer') -> None: + def post_install(self, install_session: "Installer") -> None: for profile in self.current_selection: profile.post_install(install_session) @override - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: # Install common packages for all desktop environments install_session.add_additional_packages(self.packages) for profile in self.current_selection: - info(f'Installing profile {profile.name}...') + info(f"Installing profile {profile.name}...") install_session.add_additional_packages(profile.packages) install_session.enable_service(profile.services) diff --git a/archinstall/default_profiles/desktops/__init__.py b/archinstall/default_profiles/desktops/__init__.py index d91a0f3a..2afa1161 100644 --- a/archinstall/default_profiles/desktops/__init__.py +++ b/archinstall/default_profiles/desktops/__init__.py @@ -2,5 +2,5 @@ from enum import Enum class SeatAccess(Enum): - seatd = 'seatd' - polkit = 'polkit' + seatd = "seatd" + polkit = "polkit" diff --git a/archinstall/default_profiles/desktops/awesome.py b/archinstall/default_profiles/desktops/awesome.py index 624700dc..1b1b62b2 100644 --- a/archinstall/default_profiles/desktops/awesome.py +++ b/archinstall/default_profiles/desktops/awesome.py @@ -9,27 +9,27 @@ if TYPE_CHECKING: class AwesomeProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Awesome', ProfileType.WindowMgr) + super().__init__("Awesome", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return super().packages + [ - 'awesome', - 'alacritty', - 'xorg-xinit', - 'xorg-xrandr', - 'xterm', - 'feh', - 'slock', - 'terminus-font', - 'gnu-free-fonts', - 'ttf-liberation', - 'xsel', + "awesome", + "alacritty", + "xorg-xinit", + "xorg-xrandr", + "xterm", + "feh", + "slock", + "terminus-font", + "gnu-free-fonts", + "ttf-liberation", + "xsel", ] @override - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: super().install(install_session) # TODO: Copy a full configuration to ~/.config/awesome/rc.lua instead. @@ -39,7 +39,7 @@ class AwesomeProfile(XorgProfile): # Replace xterm with alacritty for a smoother experience. awesome_lua = awesome_lua.replace('"xterm"', '"alacritty"') - with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", 'w') as fh: + with open(f"{install_session.target}/etc/xdg/awesome/rc.lua", "w") as fh: fh.write(awesome_lua) # TODO: Configure the right-click-menu to contain the above packages that were installed. (as a user config) @@ -49,7 +49,7 @@ class AwesomeProfile(XorgProfile): with open(f"{install_session.target}/etc/X11/xinit/xinitrc") as xinitrc: xinitrc_data = xinitrc.read() - for line in xinitrc_data.split('\n'): + for line in xinitrc_data.split("\n"): if "twm &" in line: xinitrc_data = xinitrc_data.replace(line, f"# {line}") if "xclock" in line: @@ -57,8 +57,8 @@ class AwesomeProfile(XorgProfile): if "xterm" in line: xinitrc_data = xinitrc_data.replace(line, f"# {line}") - xinitrc_data += '\n' - xinitrc_data += 'exec awesome\n' + xinitrc_data += "\n" + xinitrc_data += "exec awesome\n" - with open(f"{install_session.target}/etc/X11/xinit/xinitrc", 'w') as xinitrc: + with open(f"{install_session.target}/etc/X11/xinit/xinitrc", "w") as xinitrc: xinitrc.write(xinitrc_data) diff --git a/archinstall/default_profiles/desktops/bspwm.py b/archinstall/default_profiles/desktops/bspwm.py index 71fa6d56..8c520391 100644 --- a/archinstall/default_profiles/desktops/bspwm.py +++ b/archinstall/default_profiles/desktops/bspwm.py @@ -6,18 +6,18 @@ from archinstall.default_profiles.xorg import XorgProfile class BspwmProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Bspwm', ProfileType.WindowMgr) + super().__init__("Bspwm", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: # return super().packages + [ return [ - 'bspwm', - 'sxhkd', - 'dmenu', - 'xdo', - 'rxvt-unicode' + "bspwm", + "sxhkd", + "dmenu", + "xdo", + "rxvt-unicode", ] @property diff --git a/archinstall/default_profiles/desktops/budgie.py b/archinstall/default_profiles/desktops/budgie.py index bffb110d..8d5f273a 100644 --- a/archinstall/default_profiles/desktops/budgie.py +++ b/archinstall/default_profiles/desktops/budgie.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class BudgieProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Budgie', ProfileType.DesktopEnv) + super().__init__("Budgie", ProfileType.DesktopEnv) @property @override @@ -16,7 +16,7 @@ class BudgieProfile(XorgProfile): "budgie", "mate-terminal", "nemo", - "papirus-icon-theme" + "papirus-icon-theme", ] @property diff --git a/archinstall/default_profiles/desktops/cinnamon.py b/archinstall/default_profiles/desktops/cinnamon.py index 9eb15552..22d7c1a5 100644 --- a/archinstall/default_profiles/desktops/cinnamon.py +++ b/archinstall/default_profiles/desktops/cinnamon.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class CinnamonProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Cinnamon', ProfileType.DesktopEnv) + super().__init__("Cinnamon", ProfileType.DesktopEnv) @property @override @@ -22,7 +22,7 @@ class CinnamonProfile(XorgProfile): "gnome-screenshot", "gvfs-smb", "xed", - "xdg-user-dirs-gtk" + "xdg-user-dirs-gtk", ] @property diff --git a/archinstall/default_profiles/desktops/cosmic.py b/archinstall/default_profiles/desktops/cosmic.py index 69708725..eff327cd 100644 --- a/archinstall/default_profiles/desktops/cosmic.py +++ b/archinstall/default_profiles/desktops/cosmic.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class CosmicProfile(XorgProfile): def __init__(self) -> None: - super().__init__('cosmic-epoch', ProfileType.DesktopEnv, advanced=True) + super().__init__("cosmic-epoch", ProfileType.DesktopEnv, advanced=True) @property @override diff --git a/archinstall/default_profiles/desktops/cutefish.py b/archinstall/default_profiles/desktops/cutefish.py index 08327dca..2b49b095 100644 --- a/archinstall/default_profiles/desktops/cutefish.py +++ b/archinstall/default_profiles/desktops/cutefish.py @@ -6,14 +6,14 @@ from archinstall.default_profiles.xorg import XorgProfile class CutefishProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Cutefish', ProfileType.DesktopEnv) + super().__init__("Cutefish", ProfileType.DesktopEnv) @property @override def packages(self) -> list[str]: return [ "cutefish", - "noto-fonts" + "noto-fonts", ] @property diff --git a/archinstall/default_profiles/desktops/deepin.py b/archinstall/default_profiles/desktops/deepin.py index 403fabc5..e3eb2acd 100644 --- a/archinstall/default_profiles/desktops/deepin.py +++ b/archinstall/default_profiles/desktops/deepin.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class DeepinProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Deepin', ProfileType.DesktopEnv) + super().__init__("Deepin", ProfileType.DesktopEnv) @property @override @@ -14,7 +14,7 @@ class DeepinProfile(XorgProfile): return [ "deepin", "deepin-terminal", - "deepin-editor" + "deepin-editor", ] @property diff --git a/archinstall/default_profiles/desktops/enlightenment.py b/archinstall/default_profiles/desktops/enlightenment.py index 8c37568a..54c71a22 100644 --- a/archinstall/default_profiles/desktops/enlightenment.py +++ b/archinstall/default_profiles/desktops/enlightenment.py @@ -6,14 +6,14 @@ from archinstall.default_profiles.xorg import XorgProfile class EnlighenmentProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Enlightenment', ProfileType.WindowMgr) + super().__init__("Enlightenment", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return [ "enlightenment", - "terminology" + "terminology", ] @property diff --git a/archinstall/default_profiles/desktops/gnome.py b/archinstall/default_profiles/desktops/gnome.py index 9ee78da4..48c1a94c 100644 --- a/archinstall/default_profiles/desktops/gnome.py +++ b/archinstall/default_profiles/desktops/gnome.py @@ -6,14 +6,14 @@ from archinstall.default_profiles.xorg import XorgProfile class GnomeProfile(XorgProfile): def __init__(self) -> None: - super().__init__('GNOME', ProfileType.DesktopEnv) + super().__init__("GNOME", ProfileType.DesktopEnv) @property @override def packages(self) -> list[str]: return [ - 'gnome', - 'gnome-tweaks' + "gnome", + "gnome-tweaks", ] @property diff --git a/archinstall/default_profiles/desktops/hyprland.py b/archinstall/default_profiles/desktops/hyprland.py index 5c2c52b4..f19c04ff 100644 --- a/archinstall/default_profiles/desktops/hyprland.py +++ b/archinstall/default_profiles/desktops/hyprland.py @@ -18,9 +18,9 @@ if TYPE_CHECKING: class HyprlandProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Hyprland', ProfileType.DesktopEnv) + super().__init__("Hyprland", ProfileType.DesktopEnv) - self.custom_settings = {'seat_access': None} + self.custom_settings = {"seat_access": None} @property @override @@ -48,32 +48,32 @@ class HyprlandProfile(XorgProfile): @property @override def services(self) -> list[str]: - if pref := self.custom_settings.get('seat_access', None): + if pref := self.custom_settings.get("seat_access", None): return [pref] return [] def _ask_seat_access(self) -> None: # need to activate seat service and add to seat group - header = str(_('Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) - header += '\n' + str(_('Choose an option to give Hyprland access to your hardware')) + '\n' + header = str(_("Hyprland needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)")) + header += "\n" + str(_("Choose an option to give Hyprland access to your hardware")) + "\n" items = [MenuItem(s.value, value=s) for s in SeatAccess] group = MenuItemGroup(items, sort_items=True) - default = self.custom_settings.get('seat_access', None) + default = self.custom_settings.get("seat_access", None) group.set_default_by_value(default) result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, - frame=FrameProperties.min(str(_('Seat access'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("Seat access"))), + alignment=Alignment.CENTER, ).run() if result.type_ == ResultType.Selection: if result.item() is not None: - self.custom_settings['seat_access'] = result.get_value().value + self.custom_settings["seat_access"] = result.get_value().value @override def do_on_select(self) -> None: diff --git a/archinstall/default_profiles/desktops/i3.py b/archinstall/default_profiles/desktops/i3.py index 11e240f9..f0d68cec 100644 --- a/archinstall/default_profiles/desktops/i3.py +++ b/archinstall/default_profiles/desktops/i3.py @@ -6,21 +6,21 @@ from archinstall.default_profiles.xorg import XorgProfile class I3wmProfile(XorgProfile): def __init__(self) -> None: - super().__init__('i3-wm', ProfileType.WindowMgr) + super().__init__("i3-wm", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return [ - 'i3-wm', - 'i3lock', - 'i3status', - 'i3blocks', - 'xss-lock', - 'xterm', - 'lightdm-gtk-greeter', - 'lightdm', - 'dmenu' + "i3-wm", + "i3lock", + "i3status", + "i3blocks", + "xss-lock", + "xterm", + "lightdm-gtk-greeter", + "lightdm", + "dmenu", ] @property diff --git a/archinstall/default_profiles/desktops/labwc.py b/archinstall/default_profiles/desktops/labwc.py index cbfd3eea..71816a72 100644 --- a/archinstall/default_profiles/desktops/labwc.py +++ b/archinstall/default_profiles/desktops/labwc.py @@ -19,17 +19,17 @@ if TYPE_CHECKING: class LabwcProfile(XorgProfile): def __init__(self) -> None: super().__init__( - 'Labwc', + "Labwc", ProfileType.WindowMgr, ) - self.custom_settings = {'seat_access': None} + self.custom_settings = {"seat_access": None} @property @override def packages(self) -> list[str]: additional = [] - if seat := self.custom_settings.get('seat_access', None): + if seat := self.custom_settings.get("seat_access", None): additional = [seat] return [ @@ -45,32 +45,32 @@ class LabwcProfile(XorgProfile): @property @override def services(self) -> list[str]: - if pref := self.custom_settings.get('seat_access', None): + if pref := self.custom_settings.get("seat_access", None): return [pref] return [] def _ask_seat_access(self) -> None: # need to activate seat service and add to seat group - header = str(_('labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) - header += '\n' + str(_('Choose an option to give labwc access to your hardware')) + '\n' + header = str(_("labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)")) + header += "\n" + str(_("Choose an option to give labwc access to your hardware")) + "\n" items = [MenuItem(s.value, value=s) for s in SeatAccess] group = MenuItemGroup(items, sort_items=True) - default = self.custom_settings.get('seat_access', None) + default = self.custom_settings.get("seat_access", None) group.set_default_by_value(default) result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, - frame=FrameProperties.min(str(_('Seat access'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("Seat access"))), + alignment=Alignment.CENTER, ).run() if result.type_ == ResultType.Selection: if result.item() is not None: - self.custom_settings['seat_access'] = result.get_value().value + self.custom_settings["seat_access"] = result.get_value().value @override def do_on_select(self) -> None: diff --git a/archinstall/default_profiles/desktops/lxqt.py b/archinstall/default_profiles/desktops/lxqt.py index a483bc7e..15289796 100644 --- a/archinstall/default_profiles/desktops/lxqt.py +++ b/archinstall/default_profiles/desktops/lxqt.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class LxqtProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Lxqt', ProfileType.DesktopEnv) + super().__init__("Lxqt", ProfileType.DesktopEnv) # NOTE: SDDM is the only officially supported greeter for LXQt, so unlike other DEs, lightdm is not used here. # LXQt works with lightdm, but since this is not supported, we will not default to this. @@ -21,7 +21,7 @@ class LxqtProfile(XorgProfile): "xdg-utils", "ttf-freefont", "leafpad", - "slock" + "slock", ] @property diff --git a/archinstall/default_profiles/desktops/mate.py b/archinstall/default_profiles/desktops/mate.py index 48860dfa..53b4117f 100644 --- a/archinstall/default_profiles/desktops/mate.py +++ b/archinstall/default_profiles/desktops/mate.py @@ -6,14 +6,14 @@ from archinstall.default_profiles.xorg import XorgProfile class MateProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Mate', ProfileType.DesktopEnv) + super().__init__("Mate", ProfileType.DesktopEnv) @property @override def packages(self) -> list[str]: return [ "mate", - "mate-extra" + "mate-extra", ] @property diff --git a/archinstall/default_profiles/desktops/niri.py b/archinstall/default_profiles/desktops/niri.py index ef1a8e08..2d0a4c12 100644 --- a/archinstall/default_profiles/desktops/niri.py +++ b/archinstall/default_profiles/desktops/niri.py @@ -19,17 +19,17 @@ if TYPE_CHECKING: class NiriProfile(XorgProfile): def __init__(self) -> None: super().__init__( - 'Niri', + "Niri", ProfileType.WindowMgr, ) - self.custom_settings = {'seat_access': None} + self.custom_settings = {"seat_access": None} @property @override def packages(self) -> list[str]: additional = [] - if seat := self.custom_settings.get('seat_access', None): + if seat := self.custom_settings.get("seat_access", None): additional = [seat] return [ @@ -42,7 +42,7 @@ class NiriProfile(XorgProfile): "swaybg", "swayidle", "swaylock", - "xdg-desktop-portal-gnome" + "xdg-desktop-portal-gnome", ] + additional @property @@ -53,32 +53,32 @@ class NiriProfile(XorgProfile): @property @override def services(self) -> list[str]: - if pref := self.custom_settings.get('seat_access', None): + if pref := self.custom_settings.get("seat_access", None): return [pref] return [] def _ask_seat_access(self) -> None: # need to activate seat service and add to seat group - header = str(_('niri needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) - header += '\n' + str(_('Choose an option to give niri access to your hardware')) + '\n' + header = str(_("niri needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)")) + header += "\n" + str(_("Choose an option to give niri access to your hardware")) + "\n" items = [MenuItem(s.value, value=s) for s in SeatAccess] group = MenuItemGroup(items, sort_items=True) - default = self.custom_settings.get('seat_access', None) + default = self.custom_settings.get("seat_access", None) group.set_default_by_value(default) result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, - frame=FrameProperties.min(str(_('Seat access'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("Seat access"))), + alignment=Alignment.CENTER, ).run() if result.type_ == ResultType.Selection: if result.item() is not None: - self.custom_settings['seat_access'] = result.get_value().value + self.custom_settings["seat_access"] = result.get_value().value @override def do_on_select(self) -> None: diff --git a/archinstall/default_profiles/desktops/plasma.py b/archinstall/default_profiles/desktops/plasma.py index 305b8cb9..f963a010 100644 --- a/archinstall/default_profiles/desktops/plasma.py +++ b/archinstall/default_profiles/desktops/plasma.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class PlasmaProfile(XorgProfile): def __init__(self) -> None: - super().__init__('KDE Plasma', ProfileType.DesktopEnv) + super().__init__("KDE Plasma", ProfileType.DesktopEnv) @property @override @@ -17,7 +17,7 @@ class PlasmaProfile(XorgProfile): "kate", "dolphin", "ark", - "plasma-workspace" + "plasma-workspace", ] @property diff --git a/archinstall/default_profiles/desktops/qtile.py b/archinstall/default_profiles/desktops/qtile.py index 10a0a085..687084dd 100644 --- a/archinstall/default_profiles/desktops/qtile.py +++ b/archinstall/default_profiles/desktops/qtile.py @@ -6,14 +6,14 @@ from archinstall.default_profiles.xorg import XorgProfile class QtileProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Qtile', ProfileType.WindowMgr) + super().__init__("Qtile", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return [ - 'qtile', - 'alacritty' + "qtile", + "alacritty", ] @property diff --git a/archinstall/default_profiles/desktops/river.py b/archinstall/default_profiles/desktops/river.py index 8820d8e4..da645e0f 100644 --- a/archinstall/default_profiles/desktops/river.py +++ b/archinstall/default_profiles/desktops/river.py @@ -6,15 +6,15 @@ from archinstall.default_profiles.xorg import XorgProfile class RiverProfile(XorgProfile): def __init__(self) -> None: - super().__init__('River', ProfileType.WindowMgr) + super().__init__("River", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return [ - 'foot', - 'xdg-desktop-portal-wlr', - 'river' + "foot", + "xdg-desktop-portal-wlr", + "river", ] @property diff --git a/archinstall/default_profiles/desktops/sway.py b/archinstall/default_profiles/desktops/sway.py index fff11786..cd8c9c52 100644 --- a/archinstall/default_profiles/desktops/sway.py +++ b/archinstall/default_profiles/desktops/sway.py @@ -19,17 +19,17 @@ if TYPE_CHECKING: class SwayProfile(XorgProfile): def __init__(self) -> None: super().__init__( - 'Sway', + "Sway", ProfileType.WindowMgr, ) - self.custom_settings = {'seat_access': None} + self.custom_settings = {"seat_access": None} @property @override def packages(self) -> list[str]: additional = [] - if seat := self.custom_settings.get('seat_access', None): + if seat := self.custom_settings.get("seat_access", None): additional = [seat] return [ @@ -44,7 +44,7 @@ class SwayProfile(XorgProfile): "slurp", "pavucontrol", "foot", - "xorg-xwayland" + "xorg-xwayland", ] + additional @property @@ -55,32 +55,32 @@ class SwayProfile(XorgProfile): @property @override def services(self) -> list[str]: - if pref := self.custom_settings.get('seat_access', None): + if pref := self.custom_settings.get("seat_access", None): return [pref] return [] def _ask_seat_access(self) -> None: # need to activate seat service and add to seat group - header = str(_('Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)')) - header += '\n' + str(_('Choose an option to give Sway access to your hardware')) + '\n' + header = str(_("Sway needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)")) + header += "\n" + str(_("Choose an option to give Sway access to your hardware")) + "\n" items = [MenuItem(s.value, value=s) for s in SeatAccess] group = MenuItemGroup(items, sort_items=True) - default = self.custom_settings.get('seat_access', None) + default = self.custom_settings.get("seat_access", None) group.set_default_by_value(default) result = SelectMenu[SeatAccess]( group, header=header, allow_skip=False, - frame=FrameProperties.min(str(_('Seat access'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("Seat access"))), + alignment=Alignment.CENTER, ).run() if result.type_ == ResultType.Selection: if result.item() is not None: - self.custom_settings['seat_access'] = result.get_value().value + self.custom_settings["seat_access"] = result.get_value().value @override def do_on_select(self) -> None: diff --git a/archinstall/default_profiles/desktops/xfce4.py b/archinstall/default_profiles/desktops/xfce4.py index 640289fe..8c1b8bc8 100644 --- a/archinstall/default_profiles/desktops/xfce4.py +++ b/archinstall/default_profiles/desktops/xfce4.py @@ -6,7 +6,7 @@ from archinstall.default_profiles.xorg import XorgProfile class Xfce4Profile(XorgProfile): def __init__(self) -> None: - super().__init__('Xfce4', ProfileType.DesktopEnv) + super().__init__("Xfce4", ProfileType.DesktopEnv) @property @override @@ -16,7 +16,7 @@ class Xfce4Profile(XorgProfile): "xfce4-goodies", "pavucontrol", "gvfs", - "xarchiver" + "xarchiver", ] @property diff --git a/archinstall/default_profiles/desktops/xmonad.py b/archinstall/default_profiles/desktops/xmonad.py index b8b38213..495d932b 100644 --- a/archinstall/default_profiles/desktops/xmonad.py +++ b/archinstall/default_profiles/desktops/xmonad.py @@ -6,17 +6,17 @@ from archinstall.default_profiles.xorg import XorgProfile class XmonadProfile(XorgProfile): def __init__(self) -> None: - super().__init__('Xmonad', ProfileType.WindowMgr) + super().__init__("Xmonad", ProfileType.WindowMgr) @property @override def packages(self) -> list[str]: return [ - 'xmonad', - 'xmonad-contrib', - 'xmonad-extra', - 'xterm', - 'dmenu' + "xmonad", + "xmonad-contrib", + "xmonad-extra", + "xterm", + "dmenu", ] @property diff --git a/archinstall/default_profiles/minimal.py b/archinstall/default_profiles/minimal.py index 451e7b95..94c52a11 100644 --- a/archinstall/default_profiles/minimal.py +++ b/archinstall/default_profiles/minimal.py @@ -4,6 +4,6 @@ from archinstall.default_profiles.profile import Profile, ProfileType class MinimalProfile(Profile): def __init__(self) -> None: super().__init__( - 'Minimal', + "Minimal", ProfileType.Minimal, ) diff --git a/archinstall/default_profiles/profile.py b/archinstall/default_profiles/profile.py index fcc83428..2200ca76 100644 --- a/archinstall/default_profiles/profile.py +++ b/archinstall/default_profiles/profile.py @@ -16,30 +16,30 @@ if TYPE_CHECKING: class ProfileType(Enum): # top level default_profiles - Server = 'Server' - Desktop = 'Desktop' - Xorg = 'Xorg' - Minimal = 'Minimal' - Custom = 'Custom' + Server = "Server" + Desktop = "Desktop" + Xorg = "Xorg" + Minimal = "Minimal" + Custom = "Custom" # detailed selection default_profiles - ServerType = 'ServerType' - WindowMgr = 'Window Manager' - DesktopEnv = 'Desktop Environment' - CustomType = 'CustomType' + ServerType = "ServerType" + WindowMgr = "Window Manager" + DesktopEnv = "Desktop Environment" + CustomType = "CustomType" # special things - Tailored = 'Tailored' - Application = 'Application' + Tailored = "Tailored" + Application = "Application" class GreeterType(Enum): - Lightdm = 'lightdm-gtk-greeter' - LightdmSlick = 'lightdm-slick-greeter' - Sddm = 'sddm' - Gdm = 'gdm' - Ly = 'ly' + Lightdm = "lightdm-gtk-greeter" + LightdmSlick = "lightdm-slick-greeter" + Sddm = "sddm" + Gdm = "gdm" + Ly = "ly" # .. todo:: Remove when we un-hide cosmic behind --advanced - if '--advanced' in sys.argv: + if "--advanced" in sys.argv: CosmicSession = "cosmic-greeter" @@ -59,7 +59,7 @@ class Profile: services: list[str] = [], support_gfx_driver: bool = False, support_greeter: bool = False, - advanced: bool = False + advanced: bool = False, ) -> None: self.name = name self.profile_type = profile_type @@ -107,14 +107,15 @@ class Profile: Returns True if --advanced is given on a Profile(advanced=True) instance. """ from archinstall.lib.args import arch_config_handler + return self.advanced is False or arch_config_handler.args.advanced is True - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: """ Performs installation steps when this profile was selected """ - def post_install(self, install_session: 'Installer') -> None: + def post_install(self, install_session: "Installer") -> None: """ Hook that will be called when the installation process is finished and custom installation steps for specific default_profiles @@ -199,9 +200,9 @@ class Profile: if sub_profile.packages: packages.update(sub_profile.packages) - text = str(_('Installed packages')) + ':\n' + text = str(_("Installed packages")) + ":\n" for pkg in sorted(packages): - text += f'\t- {pkg}\n' + text += f"\t- {pkg}\n" return text diff --git a/archinstall/default_profiles/server.py b/archinstall/default_profiles/server.py index 60271020..e1832bea 100644 --- a/archinstall/default_profiles/server.py +++ b/archinstall/default_profiles/server.py @@ -15,9 +15,9 @@ if TYPE_CHECKING: class ServerProfile(Profile): def __init__(self, current_value: list[Profile] = []): super().__init__( - 'Server', + "Server", ProfileType.Server, - current_selection=current_value + current_selection=current_value, ) @override @@ -26,8 +26,9 @@ class ServerProfile(Profile): MenuItem( p.name, value=p, - preview_action=lambda x: x.value.preview_text() - ) for p in profile_handler.get_server_profiles() + preview_action=lambda x: x.value.preview_text(), + ) + for p in profile_handler.get_server_profiles() ] group = MenuItemGroup(items, sort_items=True) @@ -38,9 +39,9 @@ class ServerProfile(Profile): allow_reset=True, allow_skip=True, preview_style=PreviewStyle.RIGHT, - preview_size='auto', - preview_frame=FrameProperties.max('Info'), - multi=True + preview_size="auto", + preview_frame=FrameProperties.max("Info"), + multi=True, ).run() match result.type_: @@ -54,20 +55,20 @@ class ServerProfile(Profile): return SelectResult.ResetCurrent @override - def post_install(self, install_session: 'Installer') -> None: + def post_install(self, install_session: "Installer") -> None: for profile in self.current_selection: profile.post_install(install_session) @override - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: server_info = self.current_selection_names() - details = ', '.join(server_info) - info(f'Now installing the selected servers: {details}') + details = ", ".join(server_info) + info(f"Now installing the selected servers: {details}") for server in self.current_selection: - info(f'Installing {server.name}...') + info(f"Installing {server.name}...") install_session.add_additional_packages(server.packages) install_session.enable_service(server.services) server.install(install_session) - info('If your selections included multiple servers with the same port, you may have to reconfigure them.') + info("If your selections included multiple servers with the same port, you may have to reconfigure them.") diff --git a/archinstall/default_profiles/servers/cockpit.py b/archinstall/default_profiles/servers/cockpit.py index eda11ebe..a078e6af 100644 --- a/archinstall/default_profiles/servers/cockpit.py +++ b/archinstall/default_profiles/servers/cockpit.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class CockpitProfile(Profile): def __init__(self) -> None: super().__init__( - 'Cockpit', - ProfileType.ServerType + "Cockpit", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['cockpit', 'udisks2', 'packagekit'] + return ["cockpit", "udisks2", "packagekit"] @property @override def services(self) -> list[str]: - return ['cockpit.socket'] + return ["cockpit.socket"] diff --git a/archinstall/default_profiles/servers/docker.py b/archinstall/default_profiles/servers/docker.py index ceb0224b..9a5075bb 100644 --- a/archinstall/default_profiles/servers/docker.py +++ b/archinstall/default_profiles/servers/docker.py @@ -9,22 +9,23 @@ if TYPE_CHECKING: class DockerProfile(Profile): def __init__(self) -> None: super().__init__( - 'Docker', - ProfileType.ServerType + "Docker", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['docker'] + return ["docker"] @property @override def services(self) -> list[str]: - return ['docker'] + return ["docker"] @override - def post_install(self, install_session: 'Installer') -> None: + def post_install(self, install_session: "Installer") -> None: from archinstall.lib.args import arch_config_handler + for user in arch_config_handler.config.users: - install_session.arch_chroot(f'usermod -a -G docker {user.username}') + install_session.arch_chroot(f"usermod -a -G docker {user.username}") diff --git a/archinstall/default_profiles/servers/httpd.py b/archinstall/default_profiles/servers/httpd.py index 24121df6..f61274f3 100644 --- a/archinstall/default_profiles/servers/httpd.py +++ b/archinstall/default_profiles/servers/httpd.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class HttpdProfile(Profile): def __init__(self) -> None: super().__init__( - 'httpd', - ProfileType.ServerType + "httpd", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['apache'] + return ["apache"] @property @override def services(self) -> list[str]: - return ['httpd'] + return ["httpd"] diff --git a/archinstall/default_profiles/servers/lighttpd.py b/archinstall/default_profiles/servers/lighttpd.py index 923eac74..d69ff6dd 100644 --- a/archinstall/default_profiles/servers/lighttpd.py +++ b/archinstall/default_profiles/servers/lighttpd.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class LighttpdProfile(Profile): def __init__(self) -> None: super().__init__( - 'Lighttpd', - ProfileType.ServerType + "Lighttpd", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['lighttpd'] + return ["lighttpd"] @property @override def services(self) -> list[str]: - return ['lighttpd'] + return ["lighttpd"] diff --git a/archinstall/default_profiles/servers/mariadb.py b/archinstall/default_profiles/servers/mariadb.py index f02fd824..ed409b84 100644 --- a/archinstall/default_profiles/servers/mariadb.py +++ b/archinstall/default_profiles/servers/mariadb.py @@ -9,20 +9,20 @@ if TYPE_CHECKING: class MariadbProfile(Profile): def __init__(self) -> None: super().__init__( - 'Mariadb', - ProfileType.ServerType + "Mariadb", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['mariadb'] + return ["mariadb"] @property @override def services(self) -> list[str]: - return ['mariadb'] + return ["mariadb"] @override - def post_install(self, install_session: 'Installer') -> None: - install_session.arch_chroot('mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql') + def post_install(self, install_session: "Installer") -> None: + install_session.arch_chroot("mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql") diff --git a/archinstall/default_profiles/servers/nginx.py b/archinstall/default_profiles/servers/nginx.py index acf7fbf4..5c9e5570 100644 --- a/archinstall/default_profiles/servers/nginx.py +++ b/archinstall/default_profiles/servers/nginx.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class NginxProfile(Profile): def __init__(self) -> None: super().__init__( - 'Nginx', - ProfileType.ServerType + "Nginx", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['nginx'] + return ["nginx"] @property @override def services(self) -> list[str]: - return ['nginx'] + return ["nginx"] diff --git a/archinstall/default_profiles/servers/postgresql.py b/archinstall/default_profiles/servers/postgresql.py index 6f1a0512..a3d01849 100644 --- a/archinstall/default_profiles/servers/postgresql.py +++ b/archinstall/default_profiles/servers/postgresql.py @@ -9,20 +9,20 @@ if TYPE_CHECKING: class PostgresqlProfile(Profile): def __init__(self) -> None: super().__init__( - 'Postgresql', + "Postgresql", ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['postgresql'] + return ["postgresql"] @property @override def services(self) -> list[str]: - return ['postgresql'] + return ["postgresql"] @override - def post_install(self, install_session: 'Installer') -> None: - install_session.arch_chroot("initdb -D /var/lib/postgres/data", run_as='postgres') + def post_install(self, install_session: "Installer") -> None: + install_session.arch_chroot("initdb -D /var/lib/postgres/data", run_as="postgres") diff --git a/archinstall/default_profiles/servers/sshd.py b/archinstall/default_profiles/servers/sshd.py index 14b14bf2..2c164710 100644 --- a/archinstall/default_profiles/servers/sshd.py +++ b/archinstall/default_profiles/servers/sshd.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class SshdProfile(Profile): def __init__(self) -> None: super().__init__( - 'sshd', - ProfileType.ServerType + "sshd", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['openssh'] + return ["openssh"] @property @override def services(self) -> list[str]: - return ['sshd'] + return ["sshd"] diff --git a/archinstall/default_profiles/servers/tomcat.py b/archinstall/default_profiles/servers/tomcat.py index e25dc09c..b76d0b2e 100644 --- a/archinstall/default_profiles/servers/tomcat.py +++ b/archinstall/default_profiles/servers/tomcat.py @@ -6,16 +6,16 @@ from archinstall.default_profiles.profile import Profile, ProfileType class TomcatProfile(Profile): def __init__(self) -> None: super().__init__( - 'Tomcat', - ProfileType.ServerType + "Tomcat", + ProfileType.ServerType, ) @property @override def packages(self) -> list[str]: - return ['tomcat10'] + return ["tomcat10"] @property @override def services(self) -> list[str]: - return ['tomcat10'] + return ["tomcat10"] diff --git a/archinstall/default_profiles/tailored.py b/archinstall/default_profiles/tailored.py index 50763581..aa6d6d0f 100644 --- a/archinstall/default_profiles/tailored.py +++ b/archinstall/default_profiles/tailored.py @@ -9,14 +9,14 @@ if TYPE_CHECKING: class TailoredProfile(XorgProfile): def __init__(self) -> None: - super().__init__('52-54-00-12-34-56', ProfileType.Tailored) + super().__init__("52-54-00-12-34-56", ProfileType.Tailored) @property @override def packages(self) -> list[str]: - return ['nano', 'wget', 'git'] + return ["nano", "wget", "git"] @override - def install(self, install_session: 'Installer') -> None: + def install(self, install_session: "Installer") -> None: super().install(install_session) # do whatever you like here :) diff --git a/archinstall/default_profiles/xorg.py b/archinstall/default_profiles/xorg.py index b5c5bd61..c72936fd 100644 --- a/archinstall/default_profiles/xorg.py +++ b/archinstall/default_profiles/xorg.py @@ -13,22 +13,22 @@ if TYPE_CHECKING: class XorgProfile(Profile): def __init__( self, - name: str = 'Xorg', + name: str = "Xorg", profile_type: ProfileType = ProfileType.Xorg, - advanced: bool = False + advanced: bool = False, ): super().__init__( name, profile_type, support_gfx_driver=True, - advanced=advanced + advanced=advanced, ) @override def preview_text(self) -> str: - text = str(_('Environment type: {}')).format(self.profile_type.value) + text = str(_("Environment type: {}")).format(self.profile_type.value) if packages := self.packages_text(): - text += f'\n{packages}' + text += f"\n{packages}" return text @@ -36,5 +36,5 @@ class XorgProfile(Profile): @override def packages(self) -> list[str]: return [ - 'xorg-server' + "xorg-server", ] diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 391da0e3..027b0518 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -46,8 +46,8 @@ class Arguments: creds_decryption_key: str | None = None silent: bool = False dry_run: bool = False - script: str = 'guided' - mountpoint: Path = Path('/mnt') + script: str = "guided" + mountpoint: Path = Path("/mnt") skip_ntp: bool = False debug: bool = False offline: bool = False @@ -62,7 +62,7 @@ class Arguments: class ArchConfig: version: str | None = None locale_config: LocaleConfiguration | None = None - archinstall_language: Language = field(default_factory=lambda: translation_handler.get_language_by_abbr('en')) + archinstall_language: Language = field(default_factory=lambda: translation_handler.get_language_by_abbr("en")) disk_config: DiskLayoutConfiguration | None = None profile_config: ProfileConfiguration | None = None mirror_config: MirrorConfiguration | None = None @@ -70,13 +70,13 @@ class ArchConfig: bootloader: Bootloader = field(default=Bootloader.get_default()) uki: bool = False audio_config: AudioConfiguration | None = None - hostname: str = 'archlinux' - kernels: list[str] = field(default_factory=lambda: ['linux']) + hostname: str = "archlinux" + kernels: list[str] = field(default_factory=lambda: ["linux"]) ntp: bool = True packages: list[str] = field(default_factory=list) parallel_downloads: int = 0 swap: bool = True - timezone: str = 'UTC' + timezone: str = "UTC" services: list[str] = field(default_factory=list) custom_commands: list[str] = field(default_factory=list) @@ -87,133 +87,133 @@ class ArchConfig: def unsafe_json(self) -> dict[str, Any]: config = { - 'users': [user.json() for user in self.users], - 'root_enc_password': self.root_enc_password.enc_password if self.root_enc_password else None, + "users": [user.json() for user in self.users], + "root_enc_password": self.root_enc_password.enc_password if self.root_enc_password else None, } if self.disk_encryption and self.disk_encryption.encryption_password: - config['encryption_password'] = self.disk_encryption.encryption_password.plaintext + config["encryption_password"] = self.disk_encryption.encryption_password.plaintext return config def safe_json(self) -> dict[str, Any]: config: Any = { - 'version': self.version, - 'archinstall-language': self.archinstall_language.json(), - 'hostname': self.hostname, - 'kernels': self.kernels, - 'ntp': self.ntp, - 'packages': self.packages, - 'parallel_downloads': self.parallel_downloads, - 'swap': self.swap, - 'timezone': self.timezone, - 'services': self.services, - 'custom_commands': self.custom_commands, - 'bootloader': self.bootloader.json(), - 'audio_config': self.audio_config.json() if self.audio_config else None, + "version": self.version, + "archinstall-language": self.archinstall_language.json(), + "hostname": self.hostname, + "kernels": self.kernels, + "ntp": self.ntp, + "packages": self.packages, + "parallel_downloads": self.parallel_downloads, + "swap": self.swap, + "timezone": self.timezone, + "services": self.services, + "custom_commands": self.custom_commands, + "bootloader": self.bootloader.json(), + "audio_config": self.audio_config.json() if self.audio_config else None, } if self.locale_config: - config['locale_config'] = self.locale_config.json() + config["locale_config"] = self.locale_config.json() if self.disk_config: - config['disk_config'] = self.disk_config.json() + config["disk_config"] = self.disk_config.json() if self.disk_encryption: - config['disk_encryption'] = self.disk_encryption.json() + config["disk_encryption"] = self.disk_encryption.json() if self.profile_config: - config['profile_config'] = self.profile_config.json() + config["profile_config"] = self.profile_config.json() if self.mirror_config: - config['mirror_config'] = self.mirror_config.json() + config["mirror_config"] = self.mirror_config.json() if self.network_config: - config['network_config'] = self.network_config.json() + config["network_config"] = self.network_config.json() return config @classmethod - def from_config(cls, args_config: dict[str, Any]) -> 'ArchConfig': + def from_config(cls, args_config: dict[str, Any]) -> "ArchConfig": arch_config = ArchConfig() arch_config.locale_config = LocaleConfiguration.parse_arg(args_config) - if archinstall_lang := args_config.get('archinstall-language', None): + if archinstall_lang := args_config.get("archinstall-language", None): arch_config.archinstall_language = translation_handler.get_language_by_name(archinstall_lang) - if disk_config := args_config.get('disk_config', {}): + if disk_config := args_config.get("disk_config", {}): arch_config.disk_config = DiskLayoutConfiguration.parse_arg(disk_config) - if profile_config := args_config.get('profile_config', None): + if profile_config := args_config.get("profile_config", None): arch_config.profile_config = ProfileConfiguration.parse_arg(profile_config) - if mirror_config := args_config.get('mirror_config', None): + if mirror_config := args_config.get("mirror_config", None): backwards_compatible_repo = [] - if additional_repositories := args_config.get('additional-repositories', []): + if additional_repositories := args_config.get("additional-repositories", []): backwards_compatible_repo = [Repository(r) for r in additional_repositories] arch_config.mirror_config = MirrorConfiguration.parse_args( mirror_config, - backwards_compatible_repo + backwards_compatible_repo, ) - if net_config := args_config.get('network_config', None): + if net_config := args_config.get("network_config", None): arch_config.network_config = NetworkConfiguration.parse_arg(net_config) # DEPRECATED: backwards copatibility - if users := args_config.get('!users', None): + if users := args_config.get("!users", None): arch_config.users = User.parse_arguments(users) - if users := args_config.get('users', None): + if users := args_config.get("users", None): arch_config.users = User.parse_arguments(users) - if bootloader_config := args_config.get('bootloader', None): + if bootloader_config := args_config.get("bootloader", None): arch_config.bootloader = Bootloader.from_arg(bootloader_config) - if args_config.get('uki') and not arch_config.bootloader.has_uki_support(): + if args_config.get("uki") and not arch_config.bootloader.has_uki_support(): arch_config.uki = False - if audio_config := args_config.get('audio_config', None): + if audio_config := args_config.get("audio_config", None): arch_config.audio_config = AudioConfiguration.parse_arg(audio_config) - if args_config.get('disk_encryption', None) is not None and arch_config.disk_config is not None: + if args_config.get("disk_encryption", None) is not None and arch_config.disk_config is not None: arch_config.disk_encryption = DiskEncryption.parse_arg( arch_config.disk_config, - args_config['disk_encryption'], - Password(plaintext=args_config.get('encryption_password', '')) + args_config["disk_encryption"], + Password(plaintext=args_config.get("encryption_password", "")), ) - if hostname := args_config.get('hostname', ''): + if hostname := args_config.get("hostname", ""): arch_config.hostname = hostname - if kernels := args_config.get('kernels', []): + if kernels := args_config.get("kernels", []): arch_config.kernels = kernels - arch_config.ntp = args_config.get('ntp', True) + arch_config.ntp = args_config.get("ntp", True) - if packages := args_config.get('packages', []): + if packages := args_config.get("packages", []): arch_config.packages = packages - if parallel_downloads := args_config.get('parallel_downloads', 0): + if parallel_downloads := args_config.get("parallel_downloads", 0): arch_config.parallel_downloads = parallel_downloads - arch_config.swap = args_config.get('swap', True) + arch_config.swap = args_config.get("swap", True) - if timezone := args_config.get('timezone', 'UTC'): + if timezone := args_config.get("timezone", "UTC"): arch_config.timezone = timezone - if services := args_config.get('services', []): + if services := args_config.get("services", []): arch_config.services = services # DEPRECATED: backwards compatibility - if root_password := args_config.get('!root-password', None): + if root_password := args_config.get("!root-password", None): arch_config.root_enc_password = Password(plaintext=root_password) - if enc_password := args_config.get('root_enc_password', None): + if enc_password := args_config.get("root_enc_password", None): arch_config.root_enc_password = Password(enc_password=enc_password) - if custom_commands := args_config.get('custom_commands', []): + if custom_commands := args_config.get("custom_commands", []): arch_config.custom_commands = custom_commands return arch_config @@ -245,9 +245,9 @@ class ArchConfigHandler: def _get_version(self) -> str: try: - return version('archinstall') + return version("archinstall") except Exception: - return 'Archinstall version not found' + return "Archinstall version not found" def _define_arguments(self) -> ArgumentParser: parser = ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -256,119 +256,119 @@ class ArchConfigHandler: "--version", action="version", default=False, - version="%(prog)s " + self._get_version() + version="%(prog)s " + self._get_version(), ) parser.add_argument( "--config", type=Path, nargs="?", default=None, - help="JSON configuration file" + help="JSON configuration file", ) parser.add_argument( "--config-url", type=str, nargs="?", default=None, - help="Url to a JSON configuration file" + help="Url to a JSON configuration file", ) parser.add_argument( "--creds", type=Path, nargs="?", default=None, - help="JSON credentials configuration file" + help="JSON credentials configuration file", ) parser.add_argument( "--creds-url", type=str, nargs="?", default=None, - help="Url to a JSON credentials configuration file" + help="Url to a JSON credentials configuration file", ) parser.add_argument( "--creds-decryption-key", type=str, nargs="?", default=None, - help="Decryption key for credentials file" + help="Decryption key for credentials file", ) parser.add_argument( "--silent", action="store_true", default=False, - help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored" + help="WARNING: Disables all prompts for input and confirmation. If no configuration is provided, this is ignored", ) parser.add_argument( "--dry-run", "--dry_run", action="store_true", default=False, - help="Generates a configuration file and then exits instead of performing an installation" + help="Generates a configuration file and then exits instead of performing an installation", ) parser.add_argument( "--script", default="guided", nargs="?", help="Script to run for installation", - type=str + type=str, ) parser.add_argument( "--mount-point", "--mount_point", type=Path, nargs="?", - default=Path('/mnt'), - help="Define an alternate mount point for installation" + default=Path("/mnt"), + help="Define an alternate mount point for installation", ) parser.add_argument( "--skip-ntp", action="store_true", help="Disables NTP checks during installation", - default=False + default=False, ) parser.add_argument( "--debug", action="store_true", default=False, - help="Adds debug info into the log" + help="Adds debug info into the log", ) parser.add_argument( "--offline", action="store_true", default=False, - help="Disabled online upstream services such as package search and key-ring auto update." + help="Disabled online upstream services such as package search and key-ring auto update.", ) parser.add_argument( "--no-pkg-lookups", action="store_true", default=False, - help="Disabled package validation specifically prior to starting installation." + help="Disabled package validation specifically prior to starting installation.", ) parser.add_argument( "--plugin", nargs="?", type=str, default=None, - help='File path to a plugin to load' + help="File path to a plugin to load", ) parser.add_argument( "--skip-version-check", action="store_true", default=False, - help="Skip the version check when running archinstall" + help="Skip the version check when running archinstall", ) parser.add_argument( "--advanced", action="store_true", default=False, - help="Enabled advanced options" + help="Enabled advanced options", ) parser.add_argument( "--verbose", action="store_true", default=False, - help="Enabled verbose options" + help="Enabled verbose options", ) return parser @@ -390,8 +390,8 @@ class ArchConfigHandler: load_plugin(plugin_path) if args.creds_decryption_key is None: - if os.environ.get('ARCHINSTALL_CREDS_DECRYPTION_KEY'): - args.creds_decryption_key = os.environ.get('ARCHINSTALL_CREDS_DECRYPTION_KEY') + if os.environ.get("ARCHINSTALL_CREDS_DECRYPTION_KEY"): + args.creds_decryption_key = os.environ.get("ARCHINSTALL_CREDS_DECRYPTION_KEY") return args @@ -423,30 +423,30 @@ class ArchConfigHandler: return config def _process_creds_data(self, creds_data: str) -> dict[str, Any] | None: - if creds_data.startswith('$'): # encrypted data + if creds_data.startswith("$"): # encrypted data if self._args.creds_decryption_key is not None: try: creds_data = decrypt(creds_data, self._args.creds_decryption_key) return json.loads(creds_data) except ValueError as err: - if 'Invalid password' in str(err): - error(str(_('Incorrect credentials file decryption password'))) + if "Invalid password" in str(err): + error(str(_("Incorrect credentials file decryption password"))) exit(1) else: - debug(f'Error decrypting credentials file: {err}') + debug(f"Error decrypting credentials file: {err}") raise err from err else: incorrect_password = False with Tui(): while True: - header = str(_('Incorrect password')) if incorrect_password else None + header = str(_("Incorrect password")) if incorrect_password else None decryption_pwd = get_password( - text=str(_('Credentials file decryption password')), + text=str(_("Credentials file decryption password")), header=header, allow_skip=False, - skip_confirmation=True + skip_confirmation=True, ) if not decryption_pwd: @@ -456,11 +456,11 @@ class ArchConfigHandler: creds_data = decrypt(creds_data, decryption_pwd.plaintext) break except ValueError as err: - if 'Invalid password' in str(err): - debug('Incorrect credentials file decryption password') + if "Invalid password" in str(err): + debug("Incorrect credentials file decryption password") incorrect_password = True else: - debug(f'Error decrypting credentials file: {err}') + debug(f"Error decrypting credentials file: {err}") raise err from err return json.loads(creds_data) @@ -468,13 +468,13 @@ class ArchConfigHandler: def _fetch_from_url(self, url: str) -> str: if urllib.parse.urlparse(url).scheme: try: - req = Request(url, headers={'User-Agent': 'ArchInstall'}) + req = Request(url, headers={"User-Agent": "ArchInstall"}) with urlopen(req) as resp: - return resp.read().decode('utf-8') + return resp.read().decode("utf-8") except urllib.error.HTTPError as err: error(f"Could not fetch JSON from {url}: {err}") else: - error('Not a valid url') + error("Not a valid url") exit(1) diff --git a/archinstall/lib/boot.py b/archinstall/lib/boot.py index b9deb69d..c2b1c489 100644 --- a/archinstall/lib/boot.py +++ b/archinstall/lib/boot.py @@ -11,12 +11,12 @@ from .storage import storage class Boot: def __init__(self, installation: Installer): self.instance = installation - self.container_name = 'archinstall' + self.container_name = "archinstall" self.session: SysCommandWorker | None = None self.ready = False - def __enter__(self) -> 'Boot': - if (existing_session := storage.get('active_boot', None)) and existing_session.instance != self.instance: + def __enter__(self) -> "Boot": + if (existing_session := storage.get("active_boot", None)) and existing_session.instance != self.instance: raise KeyError("Archinstall only supports booting up one instance and another session is already active.") if existing_session: @@ -25,22 +25,26 @@ class Boot: else: # '-P' or --console=pipe could help us not having to do a bunch # of os.write() calls, but instead use pipes (stdin, stdout and stderr) as usual. - self.session = SysCommandWorker([ - 'systemd-nspawn', - '-D', str(self.instance.target), - '--timezone=off', - '-b', - '--no-pager', - '--machine', self.container_name - ]) + self.session = SysCommandWorker( + [ + "systemd-nspawn", + "-D", + str(self.instance.target), + "--timezone=off", + "-b", + "--no-pager", + "--machine", + self.container_name, + ] + ) if not self.ready and self.session: while self.session.is_alive(): - if b' login:' in self.session: + if b" login:" in self.session: self.ready = True break - storage['active_boot'] = self + storage["active_boot"] = self return self def __exit__(self, *args: str, **kwargs: str) -> None: @@ -50,14 +54,14 @@ class Boot: if len(args) >= 2 and args[1]: error( args[1], - f"The error above occurred in a temporary boot-up of the installation {self.instance}" + f"The error above occurred in a temporary boot-up of the installation {self.instance}", ) shutdown = None shutdown_exit_code: int | None = -1 try: - shutdown = SysCommand(f'systemd-run --machine={self.container_name} --pty shutdown now') + shutdown = SysCommand(f"systemd-run --machine={self.container_name} --pty shutdown now") except SysCallError as err: shutdown_exit_code = err.exit_code @@ -69,13 +73,13 @@ class Boot: shutdown_exit_code = shutdown.exit_code if self.session and (self.session.exit_code == 0 or shutdown_exit_code == 0): - storage['active_boot'] = None + storage["active_boot"] = None else: session_exit_code = self.session.exit_code if self.session else -1 raise SysCallError( f"Could not shut down temporary boot of {self.instance}: {session_exit_code}/{shutdown_exit_code}", - exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code])) + exit_code=next(filter(bool, [session_exit_code, shutdown_exit_code])), ) def __iter__(self) -> Iterator[bytes]: @@ -95,7 +99,7 @@ class Boot: return self.session.is_alive() def SysCommand(self, cmd: list[str], *args, **kwargs) -> SysCommand: - if cmd[0][0] != '/' and cmd[0][:2] != './': + if cmd[0][0] != "/" and cmd[0][:2] != "./": # This check is also done in SysCommand & SysCommandWorker. # However, that check is done for `machinectl` and not for our chroot command. # So this wrapper for SysCommand will do this additionally. @@ -105,7 +109,7 @@ class Boot: return SysCommand(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) def SysCommandWorker(self, cmd: list[str], *args, **kwargs) -> SysCommandWorker: - if cmd[0][0] != '/' and cmd[0][:2] != './': + if cmd[0][0] != "/" and cmd[0][:2] != "./": cmd[0] = locate_binary(cmd[0]) return SysCommandWorker(["systemd-run", f"--machine={self.container_name}", "--pty", *cmd], *args, **kwargs) diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index d3b7ba4a..6a1d8824 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -36,9 +36,9 @@ class ConfigurationOutput: """ self._config = config - self._default_save_path = storage.get('LOG_PATH', Path('.')) - self._user_config_file = Path('user_configuration.json') - self._user_creds_file = Path('user_credentials.json') + self._default_save_path = storage.get("LOG_PATH", Path(".")) + self._user_config_file = Path("user_configuration.json") + self._user_creds_file = Path("user_credentials.json") @property def user_configuration_file(self) -> Path: @@ -61,8 +61,8 @@ class ConfigurationOutput: debug(self.user_config_to_json()) def confirm_config(self) -> bool: - header = f'{_("The specified configuration will be applied")}. ' - header += str(_('Would you like to continue?')) + '\n' + header = f"{_('The specified configuration will be applied')}. " + header += str(_("Would you like to continue?")) + "\n" with Tui(): group = MenuItemGroup.yes_no() @@ -76,9 +76,9 @@ class ConfigurationOutput: columns=2, orientation=Orientation.HORIZONTAL, allow_skip=False, - preview_size='auto', + preview_size="auto", preview_style=PreviewStyle.BOTTOM, - preview_frame=FrameProperties.max(str(_('Configuration'))) + preview_frame=FrameProperties.max(str(_("Configuration"))), ).run() if result.item() != MenuItem.yes(): @@ -90,8 +90,8 @@ class ConfigurationOutput: dest_path_ok = dest_path.exists() and dest_path.is_dir() if not dest_path_ok: warn( - f'Destination directory {dest_path.resolve()} does not exist or is not a directory\n.', - 'Configuration files can not be saved' + f"Destination directory {dest_path.resolve()} does not exist or is not a directory\n.", + "Configuration files can not be saved", ) return dest_path_ok @@ -104,7 +104,7 @@ class ConfigurationOutput: def save_user_creds( self, dest_path: Path, - password: str | None = None + password: str | None = None, ) -> None: data = self.user_credentials_to_json() @@ -120,7 +120,7 @@ class ConfigurationOutput: self, dest_path: Path | None = None, creds: bool = False, - password: str | None = None + password: str | None = None, ) -> None: save_path = dest_path or self._default_save_path @@ -144,7 +144,7 @@ def save_config(config: ArchConfig) -> None: output = [str(config_output.user_configuration_file)] config_output.user_credentials_to_json() output.append(str(config_output.user_credentials_file)) - return '\n'.join(output) + return "\n".join(output) return None config_output = ConfigurationOutput(config) @@ -153,27 +153,27 @@ def save_config(config: ArchConfig) -> None: MenuItem( str(_("Save user configuration (including disk layout)")), value="user_config", - preview_action=preview + preview_action=preview, ), MenuItem( str(_("Save user credentials")), value="user_creds", - preview_action=preview + preview_action=preview, ), MenuItem( str(_("Save all")), value="all", - preview_action=preview - ) + preview_action=preview, + ), ] group = MenuItemGroup(items) result = SelectMenu[str]( group, allow_skip=True, - preview_frame=FrameProperties.max(str(_('Configuration'))), - preview_size='auto', - preview_style=PreviewStyle.RIGHT + preview_frame=FrameProperties.max(str(_("Configuration"))), + preview_size="auto", + preview_style=PreviewStyle.RIGHT, ).run() match result.type_: @@ -182,15 +182,15 @@ def save_config(config: ArchConfig) -> None: case ResultType.Selection: save_option = result.get_value() case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") readline.set_completer_delims("\t\n=") readline.parse_and_bind("tab: complete") dest_path = prompt_dir( - str(_('Directory')), - str(_('Enter a directory for the configuration(s) to be saved (tab completion enabled)')) + '\n', - allow_skip=True + str(_("Directory")), + str(_("Enter a directory for the configuration(s) to be saved (tab completion enabled)")) + "\n", + allow_skip=True, ) if not dest_path: @@ -207,7 +207,7 @@ def save_config(config: ArchConfig) -> None: allow_skip=False, alignment=Alignment.CENTER, columns=2, - orientation=Orientation.HORIZONTAL + orientation=Orientation.HORIZONTAL, ).run() match result.type_: @@ -217,7 +217,7 @@ def save_config(config: ArchConfig) -> None: debug(f"Saving configuration files to {dest_path.absolute()}") - header = str(_('Do you want to encrypt the user_credentials.json file?')) + header = str(_("Do you want to encrypt the user_credentials.json file?")) group = MenuItemGroup.yes_no() group.focus_item = MenuItem.no() @@ -228,7 +228,7 @@ def save_config(config: ArchConfig) -> None: allow_skip=False, alignment=Alignment.CENTER, columns=2, - orientation=Orientation.HORIZONTAL + orientation=Orientation.HORIZONTAL, ).run() enc_password: str | None = None @@ -236,8 +236,8 @@ def save_config(config: ArchConfig) -> None: case ResultType.Selection: if result.item() == MenuItem.yes(): password = get_password( - text=str(_('Credentials file encryption password')), - allow_skip=True + text=str(_("Credentials file encryption password")), + allow_skip=True, ) if password: diff --git a/archinstall/lib/crypt.py b/archinstall/lib/crypt.py index 1436bca3..e0e5df90 100644 --- a/archinstall/lib/crypt.py +++ b/archinstall/lib/crypt.py @@ -16,19 +16,19 @@ libcrypt.crypt.restype = ctypes.c_char_p libcrypt.crypt_gensalt.argtypes = [ctypes.c_char_p, ctypes.c_ulong, ctypes.c_char_p, ctypes.c_int] libcrypt.crypt_gensalt.restype = ctypes.c_char_p -LOGIN_DEFS = Path('/etc/login.defs') +LOGIN_DEFS = Path("/etc/login.defs") def _search_login_defs(key: str) -> str | None: defs = LOGIN_DEFS.read_text() - for line in defs.split('\n'): + for line in defs.split("\n"): line = line.strip() - if line.startswith('#'): + if line.startswith("#"): continue if line.startswith(key): - value = line.split(' ')[1] + value = line.split(" ")[1] return value return None @@ -36,12 +36,12 @@ def _search_login_defs(key: str) -> str | None: def crypt_gen_salt(prefix: str | bytes, rounds: int) -> bytes: if isinstance(prefix, str): - prefix = prefix.encode('utf-8') + prefix = prefix.encode("utf-8") setting = libcrypt.crypt_gensalt(prefix, rounds, None, 0) if setting is None: - raise ValueError(f'crypt_gensalt() returned NULL for prefix {prefix!r} and rounds {rounds}') + raise ValueError(f"crypt_gensalt() returned NULL for prefix {prefix!r} and rounds {rounds}") return setting @@ -53,7 +53,7 @@ def crypt_yescrypt(plaintext: str) -> str: shows that the hashing rounds are determined from YESCRYPT_COST_FACTOR in /etc/login.defs If no value was specified (or commented out) a default of 5 is choosen """ - value = _search_login_defs('YESCRYPT_COST_FACTOR') + value = _search_login_defs("YESCRYPT_COST_FACTOR") if value is not None: rounds = int(value) if rounds < 3: @@ -63,17 +63,17 @@ def crypt_yescrypt(plaintext: str) -> str: else: rounds = 5 - debug(f'Creating yescrypt hash with rounds {rounds}') + debug(f"Creating yescrypt hash with rounds {rounds}") - enc_plaintext = plaintext.encode('utf-8') - salt = crypt_gen_salt('$y$', rounds) + enc_plaintext = plaintext.encode("utf-8") + salt = crypt_gen_salt("$y$", rounds) crypt_hash = libcrypt.crypt(enc_plaintext, salt) if crypt_hash is None: - raise ValueError('crypt() returned NULL') + raise ValueError("crypt() returned NULL") - return crypt_hash.decode('utf-8') + return crypt_hash.decode("utf-8") def _get_fernet(salt: bytes, password: str) -> Fernet: @@ -90,8 +90,8 @@ def _get_fernet(salt: bytes, password: str) -> Fernet: key = base64.urlsafe_b64encode( kdf.derive( - password.encode('utf-8') - ) + password.encode("utf-8"), + ), ) return Fernet(key) @@ -100,26 +100,26 @@ def _get_fernet(salt: bytes, password: str) -> Fernet: def encrypt(password: str, data: str) -> str: salt = os.urandom(16) f = _get_fernet(salt, password) - token = f.encrypt(data.encode('utf-8')) + token = f.encrypt(data.encode("utf-8")) - encoded_token = base64.urlsafe_b64encode(token).decode('utf-8') - encoded_salt = base64.urlsafe_b64encode(salt).decode('utf-8') + encoded_token = base64.urlsafe_b64encode(token).decode("utf-8") + encoded_salt = base64.urlsafe_b64encode(salt).decode("utf-8") - return f'$argon2id${encoded_salt}${encoded_token}' + return f"$argon2id${encoded_salt}${encoded_token}" def decrypt(data: str, password: str): - _, algo, encoded_salt, encoded_token = data.split('$') + _, algo, encoded_salt, encoded_token = data.split("$") salt = base64.urlsafe_b64decode(encoded_salt) token = base64.urlsafe_b64decode(encoded_token) - if algo != 'argon2id': - raise ValueError(f'Unsupported algorithm {algo!r}') + if algo != "argon2id": + raise ValueError(f"Unsupported algorithm {algo!r}") f = _get_fernet(salt, password) try: decrypted = f.decrypt(token) except InvalidToken: - raise ValueError('Invalid password') + raise ValueError("Invalid password") - return decrypted.decode('utf-8') + return decrypted.decode("utf-8") diff --git a/archinstall/lib/disk/device_handler.py b/archinstall/lib/disk/device_handler.py index e9150075..65137456 100644 --- a/archinstall/lib/disk/device_handler.py +++ b/archinstall/lib/disk/device_handler.py @@ -50,7 +50,7 @@ from .utils import ( class DeviceHandler: - _TMP_BTRFS_MOUNT = Path('/mnt/arch_btrfs') + _TMP_BTRFS_MOUNT = Path("/mnt/arch_btrfs") def __init__(self) -> None: self._devices: dict[Path, BDevice] = {} @@ -73,16 +73,16 @@ class DeviceHandler: devices = getAllDevices() devices.extend(self.get_loop_devices()) - archiso_mountpoint = Path('/run/archiso/airootfs') + archiso_mountpoint = Path("/run/archiso/airootfs") for device in devices: dev_lsblk_info = find_lsblk_info(device.path, all_lsblk_info) if not dev_lsblk_info: - debug(f'Device lsblk info not found: {device.path}') + debug(f"Device lsblk info not found: {device.path}") continue - if dev_lsblk_info.type == 'rom': + if dev_lsblk_info.type == "rom": continue # exclude archiso loop device @@ -95,7 +95,7 @@ class DeviceHandler: else: disk = freshDisk(device, self.partition_table.value) except DiskException as err: - debug(f'Unable to get disk from {device.path}: {err}') + debug(f"Unable to get disk from {device.path}: {err}") continue device_info = _DeviceInfo.from_disk(disk) @@ -105,7 +105,7 @@ class DeviceHandler: lsblk_info = find_lsblk_info(partition.path, dev_lsblk_info.children) if not lsblk_info: - debug(f'Partition lsblk info not found: {partition.path}') + debug(f"Partition lsblk info not found: {partition.path}") continue fs_type = self._determine_fs_type(partition, lsblk_info) @@ -119,8 +119,8 @@ class DeviceHandler: partition, lsblk_info, fs_type, - subvol_infos - ) + subvol_infos, + ), ) block_device = BDevice(disk, device_info, partition_infos) @@ -133,20 +133,20 @@ class DeviceHandler: devices = [] try: - loop_devices = SysCommand(['losetup', '-a']) + loop_devices = SysCommand(["losetup", "-a"]) except SysCallError as err: - debug(f'Failed to get loop devices: {err}') + debug(f"Failed to get loop devices: {err}") else: for ld_info in str(loop_devices).splitlines(): try: - loop_device_path, _ = ld_info.split(':', maxsplit=1) + loop_device_path, _ = ld_info.split(":", maxsplit=1) except ValueError: continue try: loop_device = getDevice(loop_device_path) except IOException as err: - debug(f'Failed to get loop device: {err}') + debug(f"Failed to get loop device: {err}") else: devices.append(loop_device) @@ -155,7 +155,7 @@ class DeviceHandler: def _determine_fs_type( self, partition: Partition, - lsblk_info: LsblkInfo | None = None + lsblk_info: LsblkInfo | None = None, ) -> FilesystemType | None: try: if partition.fileSystem: @@ -166,7 +166,7 @@ class DeviceHandler: return FilesystemType(lsblk_info.fstype) if lsblk_info.fstype else None return None except ValueError: - debug(f'Could not determine the filesystem: {partition.fileSystem}') + debug(f"Could not determine the filesystem: {partition.fileSystem}") return None @@ -189,15 +189,12 @@ class DeviceHandler: def get_parent_device_path(self, dev_path: Path) -> Path: lsblk = get_lsblk_info(dev_path) - return Path(f'/dev/{lsblk.pkname}') + return Path(f"/dev/{lsblk.pkname}") def get_unique_path_for_device(self, dev_path: Path) -> Path | None: - paths = Path('/dev/disk/by-id').glob('*') + paths = Path("/dev/disk/by-id").glob("*") linked_targets = {p.resolve(): p for p in paths} - linked_wwn_targets = { - p: linked_targets[p] for p in linked_targets - if p.name.startswith('wwn-') or p.name.startswith('nvme-eui.') - } + linked_wwn_targets = {p: linked_targets[p] for p in linked_targets if p.name.startswith("wwn-") or p.name.startswith("nvme-eui.")} if dev_path in linked_wwn_targets: return linked_wwn_targets[dev_path] @@ -214,7 +211,7 @@ class DeviceHandler: def get_btrfs_info( self, dev_path: Path, - lsblk_info: LsblkInfo | None = None + lsblk_info: LsblkInfo | None = None, ) -> list[_BtrfsSubvolumeInfo]: if not lsblk_info: lsblk_info = get_lsblk_info(dev_path) @@ -237,9 +234,9 @@ class DeviceHandler: mountpoint = Path(common_path) try: - result = SysCommand(f'btrfs subvolume list {mountpoint}').decode() + result = SysCommand(f"btrfs subvolume list {mountpoint}").decode() except SysCallError as err: - debug(f'Failed to read btrfs subvolume information: {err}') + debug(f"Failed to read btrfs subvolume information: {err}") return subvol_infos # It is assumed that lsblk will contain the fields as @@ -253,8 +250,8 @@ class DeviceHandler: for line in result.splitlines(): # expected output format: # ID 257 gen 8 top level 5 path @home - name = Path(line.split(' ')[-1]) - sub_vol_mountpoint = btrfs_subvol_info.get('/' / name, None) + name = Path(line.split(" ")[-1]) + sub_vol_mountpoint = btrfs_subvol_info.get("/" / name, None) subvol_infos.append(_BtrfsSubvolumeInfo(name, sub_vol_mountpoint)) if not lsblk_info.mountpoint: @@ -266,7 +263,7 @@ class DeviceHandler: self, fs_type: FilesystemType, path: Path, - additional_parted_options: list[str] = [] + additional_parted_options: list[str] = [], ) -> None: mkfs_type = fs_type.value command = None @@ -275,33 +272,33 @@ class DeviceHandler: match fs_type: case FilesystemType.Btrfs | FilesystemType.F2fs | FilesystemType.Xfs: # Force overwrite - options.append('-f') + options.append("-f") case FilesystemType.Ext2 | FilesystemType.Ext3 | FilesystemType.Ext4: # Force create - options.append('-F') + options.append("-F") case FilesystemType.Fat12 | FilesystemType.Fat16 | FilesystemType.Fat32: - mkfs_type = 'fat' + mkfs_type = "fat" # Set FAT size - options.extend(('-F', fs_type.value.removeprefix(mkfs_type))) + options.extend(("-F", fs_type.value.removeprefix(mkfs_type))) case FilesystemType.Ntfs: # Skip zeroing and bad sector check - options.append('--fast') + options.append("--fast") case FilesystemType.LinuxSwap: command = "mkswap" case _: raise UnknownFilesystemFormat(f'Filetype "{fs_type.value}" is not supported') if not command: - command = f'mkfs.{mkfs_type}' + command = f"mkfs.{mkfs_type}" cmd = [command, *options, *additional_parted_options, str(path)] - debug('Formatting filesystem:', ' '.join(cmd)) + debug("Formatting filesystem:", " ".join(cmd)) try: SysCommand(cmd) except SysCallError as err: - msg = f'Could not format {path} with {fs_type.value}: {err.message}' + msg = f"Could not format {path} with {fs_type.value}: {err.message}" error(msg) raise DiskError(msg) from err @@ -310,12 +307,12 @@ class DeviceHandler: dev_path: Path, mapper_name: str | None, enc_password: Password | None, - lock_after_create: bool = True + lock_after_create: bool = True, ) -> Luks2: luks_handler = Luks2( dev_path, mapper_name=mapper_name, - password=enc_password + password=enc_password, ) key_file = luks_handler.encrypt() @@ -325,10 +322,10 @@ class DeviceHandler: luks_handler.unlock(key_file=key_file) if not luks_handler.mapper_dev: - raise DiskError('Failed to unlock luks device') + raise DiskError("Failed to unlock luks device") if lock_after_create: - debug(f'luks2 locking device: {dev_path}') + debug(f"luks2 locking device: {dev_path}") luks_handler.lock() return luks_handler @@ -338,15 +335,15 @@ class DeviceHandler: dev_path: Path, mapper_name: str | None, fs_type: FilesystemType, - enc_conf: DiskEncryption + enc_conf: DiskEncryption, ) -> None: if not enc_conf.encryption_password: - raise ValueError('No encryption password provided') + raise ValueError("No encryption password provided") luks_handler = Luks2( dev_path, mapper_name=mapper_name, - password=enc_conf.encryption_password + password=enc_conf.encryption_password, ) key_file = luks_handler.encrypt() @@ -356,72 +353,69 @@ class DeviceHandler: luks_handler.unlock(key_file=key_file) if not luks_handler.mapper_dev: - raise DiskError('Failed to unlock luks device') + raise DiskError("Failed to unlock luks device") - info(f'luks2 formatting mapper dev: {luks_handler.mapper_dev}') + info(f"luks2 formatting mapper dev: {luks_handler.mapper_dev}") self.format(fs_type, luks_handler.mapper_dev) - info(f'luks2 locking device: {dev_path}') + info(f"luks2 locking device: {dev_path}") luks_handler.lock() def _lvm_info( self, cmd: str, - info_type: Literal['lv', 'vg', 'pvseg'] + info_type: Literal["lv", "vg", "pvseg"], ) -> LvmVolumeInfo | LvmGroupInfo | LvmPVInfo | None: - raw_info = SysCommand(cmd).decode().split('\n') + raw_info = SysCommand(cmd).decode().split("\n") # for whatever reason the output sometimes contains # "File descriptor X leaked leaked on vgs invocation - data = '\n'.join([raw for raw in raw_info if 'File descriptor' not in raw]) + data = "\n".join([raw for raw in raw_info if "File descriptor" not in raw]) - debug(f'LVM info: {data}') + debug(f"LVM info: {data}") reports = json.loads(data) - for report in reports['report']: + for report in reports["report"]: if len(report[info_type]) != 1: - raise ValueError('Report does not contain any entry') + raise ValueError("Report does not contain any entry") entry = report[info_type][0] match info_type: - case 'pvseg': + case "pvseg": return LvmPVInfo( - pv_name=Path(entry['pv_name']), - lv_name=entry['lv_name'], - vg_name=entry['vg_name'], + pv_name=Path(entry["pv_name"]), + lv_name=entry["lv_name"], + vg_name=entry["vg_name"], ) - case 'lv': + case "lv": return LvmVolumeInfo( - lv_name=entry['lv_name'], - vg_name=entry['vg_name'], - lv_size=Size(int(entry['lv_size'][:-1]), Unit.B, SectorSize.default()) + lv_name=entry["lv_name"], + vg_name=entry["vg_name"], + lv_size=Size(int(entry["lv_size"][:-1]), Unit.B, SectorSize.default()), ) - case 'vg': + case "vg": return LvmGroupInfo( - vg_uuid=entry['vg_uuid'], - vg_size=Size(int(entry['vg_size'][:-1]), Unit.B, SectorSize.default()) + vg_uuid=entry["vg_uuid"], + vg_size=Size(int(entry["vg_size"][:-1]), Unit.B, SectorSize.default()), ) return None @overload - def _lvm_info_with_retry(self, cmd: str, info_type: Literal['lv']) -> LvmVolumeInfo | None: - ... + def _lvm_info_with_retry(self, cmd: str, info_type: Literal["lv"]) -> LvmVolumeInfo | None: ... @overload - def _lvm_info_with_retry(self, cmd: str, info_type: Literal['vg']) -> LvmGroupInfo | None: - ... + def _lvm_info_with_retry(self, cmd: str, info_type: Literal["vg"]) -> LvmGroupInfo | None: ... @overload - def _lvm_info_with_retry(self, cmd: str, info_type: Literal['pvseg']) -> LvmPVInfo | None: - ... + def _lvm_info_with_retry(self, cmd: str, info_type: Literal["pvseg"]) -> LvmPVInfo | None: ... def _lvm_info_with_retry( self, cmd: str, - info_type: Literal['lv', 'vg', 'pvseg'] + info_type: Literal["lv", "vg", "pvseg"], ) -> LvmVolumeInfo | LvmGroupInfo | LvmPVInfo | None: while True: try: @@ -430,77 +424,63 @@ class DeviceHandler: time.sleep(3) def lvm_vol_info(self, lv_name: str) -> LvmVolumeInfo | None: - cmd = ( - 'lvs --reportformat json ' - '--unit B ' - f'-S lv_name={lv_name}' - ) + cmd = f"lvs --reportformat json --unit B -S lv_name={lv_name}" - return self._lvm_info_with_retry(cmd, 'lv') + return self._lvm_info_with_retry(cmd, "lv") def lvm_group_info(self, vg_name: str) -> LvmGroupInfo | None: - cmd = ( - 'vgs --reportformat json ' - '--unit B ' - '-o vg_name,vg_uuid,vg_size ' - f'-S vg_name={vg_name}' - ) + cmd = f"vgs --reportformat json --unit B -o vg_name,vg_uuid,vg_size -S vg_name={vg_name}" - return self._lvm_info_with_retry(cmd, 'vg') + return self._lvm_info_with_retry(cmd, "vg") def lvm_pvseg_info(self, vg_name: str, lv_name: str) -> LvmPVInfo | None: - cmd = ( - 'pvs ' - '--segments -o+lv_name,vg_name ' - f'-S vg_name={vg_name},lv_name={lv_name} ' - '--reportformat json ' - ) + cmd = f"pvs --segments -o+lv_name,vg_name -S vg_name={vg_name},lv_name={lv_name} --reportformat json " - return self._lvm_info_with_retry(cmd, 'pvseg') + return self._lvm_info_with_retry(cmd, "pvseg") def lvm_vol_change(self, vol: LvmVolume, activate: bool) -> None: - active_flag = 'y' if activate else 'n' - cmd = f'lvchange -a {active_flag} {vol.safe_dev_path}' + active_flag = "y" if activate else "n" + cmd = f"lvchange -a {active_flag} {vol.safe_dev_path}" - debug(f'lvchange volume: {cmd}') + debug(f"lvchange volume: {cmd}") SysCommand(cmd) def lvm_export_vg(self, vg: LvmVolumeGroup) -> None: - cmd = f'vgexport {vg.name}' + cmd = f"vgexport {vg.name}" - debug(f'vgexport: {cmd}') + debug(f"vgexport: {cmd}") SysCommand(cmd) def lvm_import_vg(self, vg: LvmVolumeGroup) -> None: - cmd = f'vgimport {vg.name}' + cmd = f"vgimport {vg.name}" - debug(f'vgimport: {cmd}') + debug(f"vgimport: {cmd}") SysCommand(cmd) def lvm_vol_reduce(self, vol_path: Path, amount: Size) -> None: val = amount.format_size(Unit.B, include_unit=False) - cmd = f'lvreduce -L -{val}B {vol_path}' + cmd = f"lvreduce -L -{val}B {vol_path}" - debug(f'Reducing LVM volume size: {cmd}') + debug(f"Reducing LVM volume size: {cmd}") SysCommand(cmd) def lvm_pv_create(self, pvs: Iterable[Path]) -> None: - cmd = 'pvcreate ' + ' '.join([str(pv) for pv in pvs]) - debug(f'Creating LVM PVS: {cmd}') + cmd = "pvcreate " + " ".join([str(pv) for pv in pvs]) + debug(f"Creating LVM PVS: {cmd}") worker = SysCommandWorker(cmd) worker.poll() - worker.write(b'y\n', line_ending=False) + worker.write(b"y\n", line_ending=False) def lvm_vg_create(self, pvs: Iterable[Path], vg_name: str) -> None: - pvs_str = ' '.join([str(pv) for pv in pvs]) - cmd = f'vgcreate --yes {vg_name} {pvs_str}' + pvs_str = " ".join([str(pv) for pv in pvs]) + cmd = f"vgcreate --yes {vg_name} {pvs_str}" - debug(f'Creating LVM group: {cmd}') + debug(f"Creating LVM group: {cmd}") worker = SysCommandWorker(cmd) worker.poll() - worker.write(b'y\n', line_ending=False) + worker.write(b"y\n", line_ending=False) def lvm_vol_create(self, vg_name: str, volume: LvmVolume, offset: Size | None = None) -> None: if offset is not None: @@ -509,32 +489,32 @@ class DeviceHandler: length = volume.length length_str = length.format_size(Unit.B, include_unit=False) - cmd = f'lvcreate --yes -L {length_str}B {vg_name} -n {volume.name}' + cmd = f"lvcreate --yes -L {length_str}B {vg_name} -n {volume.name}" - debug(f'Creating volume: {cmd}') + debug(f"Creating volume: {cmd}") worker = SysCommandWorker(cmd) worker.poll() - worker.write(b'y\n', line_ending=False) + worker.write(b"y\n", line_ending=False) volume.vg_name = vg_name - volume.dev_path = Path(f'/dev/{vg_name}/{volume.name}') + volume.dev_path = Path(f"/dev/{vg_name}/{volume.name}") def _setup_partition( self, part_mod: PartitionModification, block_device: BDevice, disk: Disk, - requires_delete: bool + requires_delete: bool, ) -> None: # when we require a delete and the partition to be (re)created # already exists then we have to delete it first if requires_delete and part_mod.status in [ModificationStatus.Modify, ModificationStatus.Delete]: - info(f'Delete existing partition: {part_mod.safe_dev_path}') + info(f"Delete existing partition: {part_mod.safe_dev_path}") part_info = self.find_partition(part_mod.safe_dev_path) if not part_info: - raise DiskError(f'No partition for dev path found: {part_mod.safe_dev_path}') + raise DiskError(f"No partition for dev path found: {part_mod.safe_dev_path}") disk.deletePartition(part_info.partition) @@ -543,18 +523,18 @@ class DeviceHandler: start_sector = part_mod.start.convert( Unit.sectors, - block_device.device_info.sector_size + block_device.device_info.sector_size, ) length_sector = part_mod.length.convert( Unit.sectors, - block_device.device_info.sector_size + block_device.device_info.sector_size, ) geometry = Geometry( device=block_device.disk.device, start=start_sector.value, - length=length_sector.value + length=length_sector.value, ) fs_value = part_mod.safe_fs_type.parted_value @@ -564,20 +544,20 @@ class DeviceHandler: disk=disk, type=part_mod.type.get_partition_code(), fs=filesystem, - geometry=geometry + geometry=geometry, ) for flag in part_mod.flags: partition.setFlag(flag.flag_id) - debug(f'\tType: {part_mod.type.value}') - debug(f'\tFilesystem: {fs_value}') - debug(f'\tGeometry: {start_sector.value} start sector, {length_sector.value} length') + debug(f"\tType: {part_mod.type.value}") + debug(f"\tFilesystem: {fs_value}") + debug(f"\tGeometry: {start_sector.value} start sector, {length_sector.value} length") try: disk.addPartition(partition=partition, constraint=disk.device.optimalAlignedConstraint) except PartitionException as ex: - raise DiskError(f'Unable to add partition, most likely due to overlapping sectors: {ex}') from ex + raise DiskError(f"Unable to add partition, most likely due to overlapping sectors: {ex}") from ex if disk.type == PartitionTable.GPT.value: if part_mod.is_root(): @@ -592,18 +572,18 @@ class DeviceHandler: lsblk_info = get_lsblk_info(path) if not lsblk_info.partn: - debug(f'Unable to determine new partition number: {path}\n{lsblk_info}') - raise DiskError(f'Unable to determine new partition number: {path}') + debug(f"Unable to determine new partition number: {path}\n{lsblk_info}") + raise DiskError(f"Unable to determine new partition number: {path}") if not lsblk_info.partuuid: - debug(f'Unable to determine new partition uuid: {path}\n{lsblk_info}') - raise DiskError(f'Unable to determine new partition uuid: {path}') + debug(f"Unable to determine new partition uuid: {path}\n{lsblk_info}") + raise DiskError(f"Unable to determine new partition uuid: {path}") if not lsblk_info.uuid: - debug(f'Unable to determine new uuid: {path}\n{lsblk_info}') - raise DiskError(f'Unable to determine new uuid: {path}') + debug(f"Unable to determine new uuid: {path}\n{lsblk_info}") + raise DiskError(f"Unable to determine new uuid: {path}") - debug(f'partition information found: {lsblk_info.model_dump_json()}') + debug(f"partition information found: {lsblk_info.model_dump_json()}") return lsblk_info @@ -611,14 +591,14 @@ class DeviceHandler: self, path: Path, btrfs_subvols: list[SubvolumeModification], - mount_options: list[str] + mount_options: list[str], ) -> None: - info(f'Creating subvolumes: {path}') + info(f"Creating subvolumes: {path}") self.mount(path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True) for sub_vol in sorted(btrfs_subvols, key=lambda x: x.name): - debug(f'Creating subvolume: {sub_vol.name}') + debug(f"Creating subvolume: {sub_vol.name}") subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name @@ -626,38 +606,38 @@ class DeviceHandler: if BtrfsMountOption.nodatacow.value in mount_options: try: - SysCommand(f'chattr +C {subvol_path}') + SysCommand(f"chattr +C {subvol_path}") except SysCallError as err: - raise DiskError(f'Could not set nodatacow attribute at {subvol_path}: {err}') + raise DiskError(f"Could not set nodatacow attribute at {subvol_path}: {err}") if BtrfsMountOption.compress.value in mount_options: try: - SysCommand(f'chattr +c {subvol_path}') + SysCommand(f"chattr +c {subvol_path}") except SysCallError as err: - raise DiskError(f'Could not set compress attribute at {subvol_path}: {err}') + raise DiskError(f"Could not set compress attribute at {subvol_path}: {err}") umount(path) def create_btrfs_volumes( self, part_mod: PartitionModification, - enc_conf: DiskEncryption | None = None + enc_conf: DiskEncryption | None = None, ) -> None: - info(f'Creating subvolumes: {part_mod.safe_dev_path}') + info(f"Creating subvolumes: {part_mod.safe_dev_path}") # unlock the partition first if it's encrypted if enc_conf is not None and part_mod in enc_conf.partitions: if not part_mod.mapper_name: - raise ValueError('No device path specified for modification') + raise ValueError("No device path specified for modification") luks_handler = self.unlock_luks2_dev( part_mod.safe_dev_path, part_mod.mapper_name, - enc_conf.encryption_password + enc_conf.encryption_password, ) if not luks_handler.mapper_dev: - raise DiskError('Failed to unlock luks device') + raise DiskError("Failed to unlock luks device") dev_path = luks_handler.mapper_dev else: @@ -668,11 +648,11 @@ class DeviceHandler: dev_path, self._TMP_BTRFS_MOUNT, create_target_mountpoint=True, - options=part_mod.mount_options + options=part_mod.mount_options, ) for sub_vol in sorted(part_mod.btrfs_subvols, key=lambda x: x.name): - debug(f'Creating subvolume: {sub_vol.name}') + debug(f"Creating subvolume: {sub_vol.name}") subvol_path = self._TMP_BTRFS_MOUNT / sub_vol.name @@ -687,7 +667,7 @@ class DeviceHandler: self, dev_path: Path, mapper_name: str, - enc_password: Password | None + enc_password: Password | None, ) -> Luks2: luks_handler = Luks2(dev_path, mapper_name=mapper_name, password=enc_password) @@ -695,17 +675,17 @@ class DeviceHandler: luks_handler.unlock() if not luks_handler.is_unlocked(): - raise DiskError(f'Failed to unlock luks2 device: {dev_path}') + raise DiskError(f"Failed to unlock luks2 device: {dev_path}") return luks_handler def umount_all_existing(self, device_path: Path) -> None: - debug(f'Unmounting all existing partitions: {device_path}') + debug(f"Unmounting all existing partitions: {device_path}") existing_partitions = self._devices[device_path].partition_infos for partition in existing_partitions: - debug(f'Unmounting: {partition.path}') + debug(f"Unmounting: {partition.path}") # un-mount for existing encrypted partitions if partition.fs_type == FilesystemType.Crypto_luks: @@ -716,7 +696,7 @@ class DeviceHandler: def partition( self, modification: DeviceModification, - partition_table: PartitionTable | None = None + partition_table: PartitionTable | None = None, ) -> None: """ Create a partition table on the block device and create all partitions. @@ -726,15 +706,15 @@ class DeviceHandler: # WARNING: the entire device will be wiped and all data lost if modification.wipe: if partition_table.is_mbr() and len(modification.partitions) > 3: - raise DiskError('Too many partitions on disk, MBR disks can only have 3 primary partitions') + raise DiskError("Too many partitions on disk, MBR disks can only have 3 primary partitions") self.wipe_dev(modification.device) disk = freshDisk(modification.device.disk.device, partition_table.value) else: - info(f'Use existing device: {modification.device_path}') + info(f"Use existing device: {modification.device_path}") disk = modification.device.disk - info(f'Creating partitions: {modification.device_path}') + info(f"Creating partitions: {modification.device_path}") # don't touch existing partitions filtered_part = [p for p in modification.partitions if not p.exists()] @@ -750,9 +730,9 @@ class DeviceHandler: @staticmethod def swapon(path: Path) -> None: try: - SysCommand(['swapon', str(path)]) + SysCommand(["swapon", str(path)]) except SysCallError as err: - raise DiskError(f'Could not enable swap {path}:\n{err.message}') + raise DiskError(f"Could not enable swap {path}:\n{err.message}") def mount( self, @@ -760,36 +740,36 @@ class DeviceHandler: target_mountpoint: Path, mount_fs: str | None = None, create_target_mountpoint: bool = True, - options: list[str] = [] + options: list[str] = [], ) -> None: if create_target_mountpoint and not target_mountpoint.exists(): target_mountpoint.mkdir(parents=True, exist_ok=True) if not target_mountpoint.exists(): - raise ValueError('Target mountpoint does not exist') + raise ValueError("Target mountpoint does not exist") lsblk_info = get_lsblk_info(dev_path) if target_mountpoint in lsblk_info.mountpoints: - info(f'Device already mounted at {target_mountpoint}') + info(f"Device already mounted at {target_mountpoint}") return - cmd = ['mount'] + cmd = ["mount"] if len(options): - cmd.extend(('-o', ','.join(options))) + cmd.extend(("-o", ",".join(options))) if mount_fs: - cmd.extend(('-t', mount_fs)) + cmd.extend(("-t", mount_fs)) cmd.extend((str(dev_path), str(target_mountpoint))) - command = ' '.join(cmd) + command = " ".join(cmd) - debug(f'Mounting {dev_path}: {command}') + debug(f"Mounting {dev_path}: {command}") try: SysCommand(command) except SysCallError as err: - raise DiskError(f'Could not mount {dev_path}: {command}\n{err.message}') + raise DiskError(f"Could not mount {dev_path}: {command}\n{err.message}") def detect_pre_mounted_mods(self, base_mountpoint: Path) -> list[DeviceModification]: part_mods: dict[Path, list[PartitionModification]] = {} @@ -819,15 +799,15 @@ class DeviceHandler: def partprobe(self, path: Path | None = None) -> None: if path is not None: - command = f'partprobe {path}' + command = f"partprobe {path}" else: - command = 'partprobe' + command = "partprobe" try: - debug(f'Calling partprobe: {command}') + debug(f"Calling partprobe: {command}") SysCommand(command) except SysCallError as err: - if 'have been written, but we have been unable to inform the kernel of the change' in str(err): + if "have been written, but we have been unable to inform the kernel of the change" in str(err): log(f"Partprobe was not able to inform the kernel of the new disk state (ignoring error): {err}", fg="gray", level=logging.INFO) else: error(f'"{command}" failed to run (continuing anyway): {err}') @@ -838,7 +818,7 @@ class DeviceHandler: @param dev_path: Device path of the partition to be wiped. @type dev_path: str """ - with open(dev_path, 'wb') as p: + with open(dev_path, "wb") as p: p.write(bytearray(1024)) def wipe_dev(self, block_device: BDevice) -> None: @@ -847,7 +827,7 @@ class DeviceHandler: This is not intended to be secure, but rather to ensure that auto-discovery tools don't recognize anything here. """ - info(f'Wiping partitions and metadata: {block_device.device_info.path}') + info(f"Wiping partitions and metadata: {block_device.device_info.path}") for partition in block_device.partition_infos: luks = Luks2(partition.path) @@ -861,9 +841,9 @@ class DeviceHandler: @staticmethod def udev_sync() -> None: try: - SysCommand('udevadm settle') + SysCommand("udevadm settle") except SysCallError as err: - debug(f'Failed to synchronize with udev: {err}') + debug(f"Failed to synchronize with udev: {err}") device_handler = DeviceHandler() diff --git a/archinstall/lib/disk/disk_menu.py b/archinstall/lib/disk/disk_menu.py index d9db86ea..bfbc7562 100644 --- a/archinstall/lib/disk/disk_menu.py +++ b/archinstall/lib/disk/disk_menu.py @@ -29,7 +29,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): else: self._disk_menu_config = DiskMenuConfig( disk_config=disk_layout_config, - lvm_config=disk_layout_config.lvm_config + lvm_config=disk_layout_config.lvm_config, ) menu_optioons = self._define_menu_options() @@ -38,25 +38,25 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): super().__init__( self._item_group, self._disk_menu_config, - allow_reset=True + allow_reset=True, ) def _define_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Partitioning')), + text=str(_("Partitioning")), action=self._select_disk_layout_config, value=self._disk_menu_config.disk_config, preview_action=self._prev_disk_layouts, - key='disk_config' + key="disk_config", ), MenuItem( - text='LVM (BETA)', + text="LVM (BETA)", action=self._select_lvm_config, value=self._disk_menu_config.lvm_config, preview_action=self._prev_lvm_config, dependencies=[self._check_dep_lvm], - key='lvm_config' + key="lvm_config", ), ] @@ -71,7 +71,7 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): return None def _check_dep_lvm(self) -> bool: - disk_layout_conf: DiskLayoutConfiguration | None = self._menu_item_group.find_by_key('disk_config').value + disk_layout_conf: DiskLayoutConfiguration | None = self._menu_item_group.find_by_key("disk_config").value if disk_layout_conf and disk_layout_conf.config_type == DiskLayoutType.Default: return True @@ -80,17 +80,17 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): def _select_disk_layout_config( self, - preset: DiskLayoutConfiguration | None + preset: DiskLayoutConfiguration | None, ) -> DiskLayoutConfiguration | None: disk_config = select_disk_config(preset) if disk_config != preset: - self._menu_item_group.find_by_key('lvm_config').value = None + self._menu_item_group.find_by_key("lvm_config").value = None return disk_config def _select_lvm_config(self, preset: LvmConfiguration | None) -> LvmConfiguration | None: - disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value + disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key("disk_config").value if disk_config: return select_lvm_config(disk_config, preset=preset) @@ -104,28 +104,28 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): disk_layout_conf = item.get_value() if disk_layout_conf.config_type == DiskLayoutType.Pre_mount: - msg = str(_('Configuration type: {}')).format(disk_layout_conf.config_type.display_msg()) + '\n' - msg += str(_('Mountpoint')) + ': ' + str(disk_layout_conf.mountpoint) + msg = str(_("Configuration type: {}")).format(disk_layout_conf.config_type.display_msg()) + "\n" + msg += str(_("Mountpoint")) + ": " + str(disk_layout_conf.mountpoint) return msg device_mods = [d for d in disk_layout_conf.device_modifications if d.partitions] if device_mods: - output_partition = '{}: {}\n'.format(str(_('Configuration')), disk_layout_conf.config_type.display_msg()) - output_btrfs = '' + output_partition = "{}: {}\n".format(str(_("Configuration")), disk_layout_conf.config_type.display_msg()) + output_btrfs = "" for mod in device_mods: # create partition table partition_table = FormattedOutput.as_table(mod.partitions) - output_partition += f'{mod.device_path}: {mod.device.device_info.model}\n' - output_partition += '{}: {}\n'.format(str(_('Wipe')), mod.wipe) - output_partition += partition_table + '\n' + output_partition += f"{mod.device_path}: {mod.device.device_info.model}\n" + output_partition += "{}: {}\n".format(str(_("Wipe")), mod.wipe) + output_partition += partition_table + "\n" # create btrfs table btrfs_partitions = [p for p in mod.partitions if p.btrfs_subvols] for partition in btrfs_partitions: - output_btrfs += FormattedOutput.as_table(partition.btrfs_subvols) + '\n' + output_btrfs += FormattedOutput.as_table(partition.btrfs_subvols) + "\n" output = output_partition + output_btrfs return output.rstrip() @@ -138,16 +138,16 @@ class DiskLayoutConfigurationMenu(AbstractSubMenu[DiskLayoutConfiguration]): lvm_config: LvmConfiguration = item.value - output = '{}: {}\n'.format(str(_('Configuration')), lvm_config.config_type.display_msg()) + output = "{}: {}\n".format(str(_("Configuration")), lvm_config.config_type.display_msg()) for vol_gp in lvm_config.vol_groups: pv_table = FormattedOutput.as_table(vol_gp.pvs) - output += '{}:\n{}'.format(str(_('Physical volumes')), pv_table) + output += "{}:\n{}".format(str(_("Physical volumes")), pv_table) - output += f'\nVolume Group: {vol_gp.name}' + output += f"\nVolume Group: {vol_gp.name}" lvm_volumes = FormattedOutput.as_table(vol_gp.volumes) - output += '\n\n{}:\n{}'.format(str(_('Volumes')), lvm_volumes) + output += "\n\n{}:\n{}".format(str(_("Volumes")), lvm_volumes) return output diff --git a/archinstall/lib/disk/encryption_menu.py b/archinstall/lib/disk/encryption_menu.py index 9ca5ec88..eaa43c8c 100644 --- a/archinstall/lib/disk/encryption_menu.py +++ b/archinstall/lib/disk/encryption_menu.py @@ -35,7 +35,7 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): def __init__( self, disk_config: DiskLayoutConfiguration, - preset: DiskEncryption | None = None + preset: DiskEncryption | None = None, ): if preset: self._enc_config = preset @@ -50,49 +50,49 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): super().__init__( self._item_group, self._enc_config, - allow_reset=True + allow_reset=True, ) def _define_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Encryption type')), + text=str(_("Encryption type")), action=lambda x: select_encryption_type(self._disk_config, x), value=self._enc_config.encryption_type, preview_action=self._preview, - key='encryption_type' + key="encryption_type", ), MenuItem( - text=str(_('Encryption password')), + text=str(_("Encryption password")), action=lambda x: select_encrypted_password(), value=self._enc_config.encryption_password, dependencies=[self._check_dep_enc_type], preview_action=self._preview, - key='encryption_password' + key="encryption_password", ), MenuItem( - text=str(_('Partitions')), + text=str(_("Partitions")), action=lambda x: select_partitions_to_encrypt(self._disk_config.device_modifications, x), value=self._enc_config.partitions, dependencies=[self._check_dep_partitions], preview_action=self._preview, - key='partitions' + key="partitions", ), MenuItem( - text=str(_('LVM volumes')), + text=str(_("LVM volumes")), action=self._select_lvm_vols, value=self._enc_config.lvm_volumes, dependencies=[self._check_dep_lvm_vols], preview_action=self._preview, - key='lvm_volumes' + key="lvm_volumes", ), MenuItem( - text=str(_('HSM')), + text=str(_("HSM")), action=select_hsm, value=self._enc_config.hsm_device, dependencies=[self._check_dep_enc_type], preview_action=self._preview, - key='hsm_device' + key="hsm_device", ), ] @@ -102,19 +102,19 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): return [] def _check_dep_enc_type(self) -> bool: - enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value + enc_type: EncryptionType | None = self._item_group.find_by_key("encryption_type").value if enc_type and enc_type != EncryptionType.NoEncryption: return True return False def _check_dep_partitions(self) -> bool: - enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value + enc_type: EncryptionType | None = self._item_group.find_by_key("encryption_type").value if enc_type and enc_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks]: return True return False def _check_dep_lvm_vols(self) -> bool: - enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value + enc_type: EncryptionType | None = self._item_group.find_by_key("encryption_type").value if enc_type and enc_type == EncryptionType.LuksOnLvm: return True return False @@ -123,10 +123,10 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): def run(self) -> DiskEncryption | None: super().run() - enc_type: EncryptionType | None = self._item_group.find_by_key('encryption_type').value - enc_password: Password | None = self._item_group.find_by_key('encryption_password').value - enc_partitions = self._item_group.find_by_key('partitions').value - enc_lvm_vols = self._item_group.find_by_key('lvm_volumes').value + enc_type: EncryptionType | None = self._item_group.find_by_key("encryption_type").value + enc_password: Password | None = self._item_group.find_by_key("encryption_password").value + enc_partitions = self._item_group.find_by_key("partitions").value + enc_lvm_vols = self._item_group.find_by_key("lvm_volumes").value assert enc_type is not None assert enc_partitions is not None @@ -144,28 +144,28 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): encryption_type=enc_type, partitions=enc_partitions, lvm_volumes=enc_lvm_vols, - hsm_device=self._enc_config.hsm_device + hsm_device=self._enc_config.hsm_device, ) return None def _preview(self, item: MenuItem) -> str | None: - output = '' + output = "" if (enc_type := self._prev_type()) is not None: output += enc_type if (enc_pwd := self._prev_password()) is not None: - output += f'\n{enc_pwd}' + output += f"\n{enc_pwd}" if (fido_device := self._prev_hsm()) is not None: - output += f'\n{fido_device}' + output += f"\n{fido_device}" if (partitions := self._prev_partitions()) is not None: - output += f'\n\n{partitions}' + output += f"\n\n{partitions}" if (lvm := self._prev_lvm_vols()) is not None: - output += f'\n\n{lvm}' + output += f"\n\n{lvm}" if not output: return None @@ -173,51 +173,51 @@ class DiskEncryptionMenu(AbstractSubMenu[DiskEncryption]): return output def _prev_type(self) -> str | None: - enc_type = self._item_group.find_by_key('encryption_type').value + enc_type = self._item_group.find_by_key("encryption_type").value if enc_type: enc_text = EncryptionType.type_to_text(enc_type) - return f'{_("Encryption type")}: {enc_text}' + return f"{_('Encryption type')}: {enc_text}" return None def _prev_password(self) -> str | None: - enc_pwd = self._item_group.find_by_key('encryption_password').value + enc_pwd = self._item_group.find_by_key("encryption_password").value if enc_pwd: - return f'{_("Encryption password")}: {enc_pwd.hidden()}' + return f"{_('Encryption password')}: {enc_pwd.hidden()}" return None def _prev_partitions(self) -> str | None: - partitions: list[PartitionModification] | None = self._item_group.find_by_key('partitions').value + partitions: list[PartitionModification] | None = self._item_group.find_by_key("partitions").value if partitions: - output = str(_('Partitions to be encrypted')) + '\n' + output = str(_("Partitions to be encrypted")) + "\n" output += FormattedOutput.as_table(partitions) return output.rstrip() return None def _prev_lvm_vols(self) -> str | None: - volumes: list[PartitionModification] | None = self._item_group.find_by_key('lvm_volumes').value + volumes: list[PartitionModification] | None = self._item_group.find_by_key("lvm_volumes").value if volumes: - output = str(_('LVM volumes to be encrypted')) + '\n' + output = str(_("LVM volumes to be encrypted")) + "\n" output += FormattedOutput.as_table(volumes) return output.rstrip() return None def _prev_hsm(self) -> str | None: - fido_device: Fido2Device | None = self._item_group.find_by_key('hsm_device').value + fido_device: Fido2Device | None = self._item_group.find_by_key("hsm_device").value if not fido_device: return None output = str(fido_device.path) - output += f' ({fido_device.manufacturer}, {fido_device.product})' - return f'{_("HSM device")}: {output}' + output += f" ({fido_device.manufacturer}, {fido_device.product})" + return f"{_('HSM device')}: {output}" def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: EncryptionType) -> EncryptionType | None: @@ -238,7 +238,7 @@ def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: Encrypt allow_skip=True, allow_reset=True, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Encryption type'))) + frame=FrameProperties.min(str(_("Encryption type"))), ).run() match result.type_: @@ -251,18 +251,18 @@ def select_encryption_type(disk_config: DiskLayoutConfiguration, preset: Encrypt def select_encrypted_password() -> Password | None: - header = str(_('Enter disk encryption password (leave blank for no encryption)')) + '\n' + header = str(_("Enter disk encryption password (leave blank for no encryption)")) + "\n" password = get_password( - text=str(_('Disk encryption password')), + text=str(_("Disk encryption password")), header=header, - allow_skip=True + allow_skip=True, ) return password def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None: - header = str(_('Select a FIDO2 device to use for HSM')) + '\n' + header = str(_("Select a FIDO2 device to use for HSM")) + "\n" try: fido_devices = Fido2.get_fido2_devices() @@ -276,7 +276,7 @@ def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None: group, header=header, alignment=Alignment.CENTER, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -292,16 +292,13 @@ def select_hsm(preset: Fido2Device | None = None) -> Fido2Device | None: def select_partitions_to_encrypt( modification: list[DeviceModification], - preset: list[PartitionModification] + preset: list[PartitionModification], ) -> list[PartitionModification]: partitions: list[PartitionModification] = [] # do not allow encrypting the boot partition for mod in modification: - partitions += [ - p for p in mod.partitions - if p.mountpoint != Path('/boot') and not p.is_swap() - ] + partitions += [p for p in mod.partitions if p.mountpoint != Path("/boot") and not p.is_swap()] # do not allow encrypting existing partitions that are not marked as wipe avail_partitions = [p for p in partitions if not p.exists()] @@ -314,7 +311,7 @@ def select_partitions_to_encrypt( group, alignment=Alignment.CENTER, multi=True, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -331,7 +328,7 @@ def select_partitions_to_encrypt( def select_lvm_vols_to_encrypt( lvm_config: LvmConfiguration, - preset: list[LvmVolume] + preset: list[LvmVolume], ) -> list[LvmVolume]: volumes: list[LvmVolume] = lvm_config.get_all_volumes() @@ -341,7 +338,7 @@ def select_lvm_vols_to_encrypt( result = SelectMenu[LvmVolume]( group, alignment=Alignment.CENTER, - multi=True + multi=True, ).run() match result.type_: diff --git a/archinstall/lib/disk/fido.py b/archinstall/lib/disk/fido.py index c689c5d3..151ea030 100644 --- a/archinstall/lib/disk/fido.py +++ b/archinstall/lib/disk/fido.py @@ -42,8 +42,8 @@ class Fido2: try: ret = SysCommand("systemd-cryptenroll --fido2-device=list").decode() except SysCallError: - error('fido2 support is most likely not installed') - raise ValueError('HSM devices can not be detected, is libfido2 installed?') + error("fido2 support is most likely not installed") + raise ValueError("HSM devices can not be detected, is libfido2 installed?") fido_devices = clear_vt100_escape_codes_from_str(ret) @@ -51,10 +51,10 @@ class Fido2: product_pos = 0 devices = [] - for line in fido_devices.split('\r\n'): - if '/dev' not in line: - manufacturer_pos = line.find('MANUFACTURER') - product_pos = line.find('PRODUCT') + for line in fido_devices.split("\r\n"): + if "/dev" not in line: + manufacturer_pos = line.find("MANUFACTURER") + product_pos = line.find("PRODUCT") continue path = line[:manufacturer_pos].rstrip() @@ -62,7 +62,7 @@ class Fido2: product = line[product_pos:] devices.append( - Fido2Device(Path(path), manufacturer, product) + Fido2Device(Path(path), manufacturer, product), ) cls._loaded = True @@ -75,7 +75,7 @@ class Fido2: cls, hsm_device: Fido2Device, dev_path: Path, - password: Password + password: Password, ) -> None: worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {dev_path}", peek_output=True) pw_inputted = False @@ -83,12 +83,12 @@ class Fido2: while worker.is_alive(): if pw_inputted is False: - if bytes(f"please enter current passphrase for disk {dev_path}", 'UTF-8') in worker._trace_log.lower(): - worker.write(bytes(password.plaintext, 'UTF-8')) + if bytes(f"please enter current passphrase for disk {dev_path}", "UTF-8") in worker._trace_log.lower(): + worker.write(bytes(password.plaintext, "UTF-8")) pw_inputted = True elif pin_inputted is False: - if bytes("please enter security token pin", 'UTF-8') in worker._trace_log.lower(): - worker.write(bytes(getpass.getpass(" "), 'UTF-8')) + if bytes("please enter security token pin", "UTF-8") in worker._trace_log.lower(): + worker.write(bytes(getpass.getpass(" "), "UTF-8")) pin_inputted = True - info('You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds') + info("You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds") diff --git a/archinstall/lib/disk/filesystem.py b/archinstall/lib/disk/filesystem.py index b180f588..c99b92a8 100644 --- a/archinstall/lib/disk/filesystem.py +++ b/archinstall/lib/disk/filesystem.py @@ -37,23 +37,23 @@ class FilesystemHandler: def __init__( self, disk_config: DiskLayoutConfiguration, - enc_conf: DiskEncryption | None = None + enc_conf: DiskEncryption | None = None, ): self._disk_config = disk_config self._enc_config = enc_conf def perform_filesystem_operations(self, show_countdown: bool = True) -> None: if self._disk_config.config_type == DiskLayoutType.Pre_mount: - debug('Disk layout configuration is set to pre-mount, not performing any operations') + debug("Disk layout configuration is set to pre-mount, not performing any operations") return device_mods = [d for d in self._disk_config.device_modifications if d.partitions] if not device_mods: - debug('No modifications required') + debug("No modifications required") return - device_paths = ', '.join([str(mod.device.device_info.path) for mod in device_mods]) + device_paths = ", ".join([str(mod.device.device_info.path) for mod in device_mods]) if show_countdown: self._final_warning(device_paths) @@ -73,10 +73,10 @@ class FilesystemHandler: if self._disk_config.lvm_config: for mod in device_mods: if boot_part := mod.get_boot_partition(): - debug(f'Formatting boot partition: {boot_part.dev_path}') + debug(f"Formatting boot partition: {boot_part.dev_path}") self._format_partitions( [boot_part], - mod.device_path + mod.device_path, ) self.perform_lvm_operations() @@ -84,7 +84,7 @@ class FilesystemHandler: for mod in device_mods: self._format_partitions( mod.partitions, - mod.device_path + mod.device_path, ) for part_mod in mod.partitions: @@ -94,7 +94,7 @@ class FilesystemHandler: def _format_partitions( self, partitions: list[PartitionModification], - device_path: Path + device_path: Path, ) -> None: """ Format can be given an overriding path, for instance /dev/null to test @@ -113,7 +113,7 @@ class FilesystemHandler: part_mod.safe_dev_path, part_mod.mapper_name, part_mod.safe_fs_type, - self._enc_config + self._enc_config, ) else: device_handler.format(part_mod.safe_fs_type, part_mod.safe_dev_path) @@ -130,12 +130,11 @@ class FilesystemHandler: def _validate_partitions(self, partitions: list[PartitionModification]) -> None: checks = { # verify that all partitions have a path set (which implies that they have been created) - lambda x: x.dev_path is None: ValueError('When formatting, all partitions must have a path set'), + lambda x: x.dev_path is None: ValueError("When formatting, all partitions must have a path set"), # crypto luks is not a valid file system type - lambda x: x.fs_type is FilesystemType.Crypto_luks: ValueError( - 'Crypto luks cannot be set as a filesystem type'), + lambda x: x.fs_type is FilesystemType.Crypto_luks: ValueError("Crypto luks cannot be set as a filesystem type"), # file system type must be set - lambda x: x.fs_type is None: ValueError('File system type must be set for modification') + lambda x: x.fs_type is None: ValueError("File system type must be set for modification"), } for check, exc in checks.items(): @@ -144,7 +143,7 @@ class FilesystemHandler: raise exc def perform_lvm_operations(self) -> None: - info('Setting up LVM config...') + info("Setting up LVM config...") if not self._disk_config.lvm_config: return @@ -152,7 +151,7 @@ class FilesystemHandler: if self._enc_config: self._setup_lvm_encrypted( self._disk_config.lvm_config, - self._enc_config + self._enc_config, ) else: self._setup_lvm(self._disk_config.lvm_config) @@ -190,7 +189,7 @@ class FilesystemHandler: def _setup_lvm( self, lvm_config: LvmConfiguration, - enc_mods: dict[PartitionModification, Luks2] = {} + enc_mods: dict[PartitionModification, Luks2] = {}, ) -> None: self._lvm_create_pvs(lvm_config, enc_mods) @@ -203,7 +202,7 @@ class FilesystemHandler: vg_info = device_handler.lvm_group_info(vg.name) if not vg_info: - raise ValueError('Unable to fetch VG info') + raise ValueError("Unable to fetch VG info") # the actual available LVM Group size will be smaller than the # total PVs size due to reserved metadata storage etc. @@ -221,11 +220,11 @@ class FilesystemHandler: for lv in vg.volumes: offset = max_vol_offset if lv == max_vol else None - debug(f'vg: {vg.name}, vol: {lv.name}, offset: {offset}') + debug(f"vg: {vg.name}, vol: {lv.name}, offset: {offset}") device_handler.lvm_vol_create(vg.name, lv, offset) while True: - debug('Fetching LVM volume info') + debug("Fetching LVM volume info") lv_info = device_handler.lvm_vol_info(lv.name) if lv_info is not None: break @@ -237,12 +236,12 @@ class FilesystemHandler: def _format_lvm_vols( self, lvm_config: LvmConfiguration, - enc_vols: dict[LvmVolume, Luks2] = {} + enc_vols: dict[LvmVolume, Luks2] = {}, ) -> None: for vol in lvm_config.get_all_volumes(): if enc_vol := enc_vols.get(vol, None): if not enc_vol.mapper_dev: - raise ValueError('No mapper device defined') + raise ValueError("No mapper device defined") path = enc_vol.mapper_dev else: path = vol.safe_dev_path @@ -257,7 +256,7 @@ class FilesystemHandler: def _lvm_create_pvs( self, lvm_config: LvmConfiguration, - enc_mods: dict[PartitionModification, Luks2] = {} + enc_mods: dict[PartitionModification, Luks2] = {}, ) -> None: pv_paths: set[Path] = set() @@ -269,7 +268,7 @@ class FilesystemHandler: def _get_all_pv_dev_paths( self, pvs: list[PartitionModification], - enc_mods: dict[PartitionModification, Luks2] = {} + enc_mods: dict[PartitionModification, Luks2] = {}, ) -> set[Path]: pv_paths: set[Path] = set() @@ -286,7 +285,7 @@ class FilesystemHandler: self, lvm_config: LvmConfiguration, enc_config: DiskEncryption, - lock_after_create: bool = True + lock_after_create: bool = True, ) -> dict[LvmVolume, Luks2]: enc_vols: dict[LvmVolume, Luks2] = {} @@ -296,7 +295,7 @@ class FilesystemHandler: vol.safe_dev_path, vol.mapper_name, enc_config.encryption_password, - lock_after_create + lock_after_create, ) enc_vols[vol] = luks_handler @@ -306,7 +305,7 @@ class FilesystemHandler: def _encrypt_partitions( self, enc_config: DiskEncryption, - lock_after_create: bool = True + lock_after_create: bool = True, ) -> dict[PartitionModification, Luks2]: enc_mods: dict[PartitionModification, Luks2] = {} @@ -326,7 +325,7 @@ class FilesystemHandler: part_mod.safe_dev_path, part_mod.mapper_name, enc_config.encryption_password, - lock_after_create=lock_after_create + lock_after_create=lock_after_create, ) enc_mods[part_mod] = luks_handler @@ -342,19 +341,19 @@ class FilesystemHandler: device_handler.lvm_vol_reduce( largest_vol.safe_dev_path, - Size(256, Unit.MiB, SectorSize.default()) + Size(256, Unit.MiB, SectorSize.default()), ) def _final_warning(self, device_paths: str) -> bool: # Issue a final warning before we continue with something un-revertable. # We mention the drive one last time, and count from 5 to 0. - out = str(_(' ! Formatting {} in ')).format(device_paths) - Tui.print(out, row=0, endl='', clear_screen=True) + out = str(_(" ! Formatting {} in ")).format(device_paths) + Tui.print(out, row=0, endl="", clear_screen=True) try: - countdown = '\n5...4...3...2...1\n' + countdown = "\n5...4...3...2...1\n" for c in countdown: - Tui.print(c, row=0, endl='') + Tui.print(c, row=0, endl="") time.sleep(0.25) except KeyboardInterrupt: with Tui(): diff --git a/archinstall/lib/disk/partitioning_menu.py b/archinstall/lib/disk/partitioning_menu.py index 84a62915..7331796b 100644 --- a/archinstall/lib/disk/partitioning_menu.py +++ b/archinstall/lib/disk/partitioning_menu.py @@ -49,9 +49,9 @@ class FreeSpace: Called for displaying data in table format """ return { - 'Start': self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False), - 'End': self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False), - 'Size': self.length.format_highest(), + "Start": self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + "End": self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + "Size": self.length.format_highest(), } @@ -73,7 +73,7 @@ class DiskSegment: length=self.segment.length, ) data = part_mod.table_data() - data.update({'Status': 'free', 'Type': '', 'FS type': ''}) + data.update({"Status": "free", "Type": "", "FS type": ""}) return data @@ -81,7 +81,7 @@ class PartitioningList(ListManager[DiskSegment]): def __init__( self, device_mod: DeviceModification, - partition_table: PartitionTable + partition_table: PartitionTable, ) -> None: device = device_mod.device @@ -91,24 +91,28 @@ class PartitioningList(ListManager[DiskSegment]): self._using_gpt = device_mod.using_gpt(partition_table) self._actions = { - 'suggest_partition_layout': str(_('Suggest partition layout')), - 'remove_added_partitions': str(_('Remove all newly added partitions')), - 'assign_mountpoint': str(_('Assign mountpoint')), - 'mark_formatting': str(_('Mark/Unmark to be formatted (wipes data)')), - 'mark_bootable': str(_('Mark/Unmark as bootable')), + "suggest_partition_layout": str(_("Suggest partition layout")), + "remove_added_partitions": str(_("Remove all newly added partitions")), + "assign_mountpoint": str(_("Assign mountpoint")), + "mark_formatting": str(_("Mark/Unmark to be formatted (wipes data)")), + "mark_bootable": str(_("Mark/Unmark as bootable")), } if self._using_gpt: - self._actions.update({ - 'mark_esp': str(_('Mark/Unmark as ESP')), - 'mark_xbootldr': str(_('Mark/Unmark as XBOOTLDR')) - }) - self._actions.update({ - 'set_filesystem': str(_('Change filesystem')), - 'btrfs_mark_compressed': str(_('Mark/Unmark as compressed')), # btrfs only - 'btrfs_mark_nodatacow': str(_('Mark/Unmark as nodatacow')), # btrfs only - 'btrfs_set_subvolumes': str(_('Set subvolumes')), # btrfs only - 'delete_partition': str(_('Delete partition')) - }) + self._actions.update( + { + "mark_esp": str(_("Mark/Unmark as ESP")), + "mark_xbootldr": str(_("Mark/Unmark as XBOOTLDR")), + } + ) + self._actions.update( + { + "set_filesystem": str(_("Change filesystem")), + "btrfs_mark_compressed": str(_("Mark/Unmark as compressed")), # btrfs only + "btrfs_mark_nodatacow": str(_("Mark/Unmark as nodatacow")), # btrfs only + "btrfs_set_subvolumes": str(_("Set subvolumes")), # btrfs only + "delete_partition": str(_("Delete partition")), + } + ) device_partitions = [] @@ -116,25 +120,25 @@ class PartitioningList(ListManager[DiskSegment]): # we'll display the existing partitions of the device for partition in device.partition_infos: device_partitions.append( - PartitionModification.from_existing_partition(partition) + PartitionModification.from_existing_partition(partition), ) else: device_partitions = device_mod.partitions - prompt = str(_('Partition management: {}')).format(device.device_info.path) + '\n' - prompt += str(_('Total length: {}')).format(device.device_info.total_size.format_size(Unit.MiB)) - self._info = prompt + '\n' + prompt = str(_("Partition management: {}")).format(device.device_info.path) + "\n" + prompt += str(_("Total length: {}")).format(device.device_info.total_size.format_size(Unit.MiB)) + self._info = prompt + "\n" display_actions = list(self._actions.values()) super().__init__( self.as_segments(device_partitions), display_actions[:1], display_actions[2:], - self._info + self.wipe_str() + self._info + self.wipe_str(), ) def wipe_str(self) -> str: - return '{}: {}'.format(str(_('Wipe')), self._wipe) + return "{}: {}".format(str(_("Wipe")), self._wipe) def as_segments(self, device_partitions: list[PartitionModification]) -> list[DiskSegment]: end = self._device.device_info.total_size @@ -157,9 +161,9 @@ class PartitioningList(ListManager[DiskSegment]): return segments first_part_index, first_partition = next( - (i, disk_segment) for i, disk_segment in enumerate(segments) - if isinstance(disk_segment.segment, PartitionModification) - and not disk_segment.segment.is_delete() + (i, disk_segment) + for i, disk_segment in enumerate(segments) + if isinstance(disk_segment.segment, PartitionModification) and not disk_segment.segment.is_delete() ) prev_partition = first_partition @@ -193,10 +197,7 @@ class PartitioningList(ListManager[DiskSegment]): @staticmethod def get_part_mods(disk_segments: list[DiskSegment]) -> list[PartitionModification]: - return [ - s.segment for s in disk_segments - if isinstance(s.segment, PartitionModification) - ] + return [s.segment for s in disk_segments if isinstance(s.segment, PartitionModification)] def get_device_mod(self) -> DeviceModification: disk_segments = super().run() @@ -207,7 +208,7 @@ class PartitioningList(ListManager[DiskSegment]): def _run_actions_on_entry(self, entry: DiskSegment) -> None: # Do not create a menu when the segment is free space if isinstance(entry.segment, FreeSpace): - self._data = self.handle_action('', entry, self._data) + self._data = self.handle_action("", entry, self._data) else: super()._run_actions_on_entry(entry) @@ -215,18 +216,18 @@ class PartitioningList(ListManager[DiskSegment]): def selected_action_display(self, selection: DiskSegment) -> str: if isinstance(selection.segment, PartitionModification): if selection.segment.status == ModificationStatus.Create: - return str(_('Partition - New')) + return str(_("Partition - New")) elif selection.segment.is_delete() and selection.segment.dev_path: - title = str(_('Partition')) + '\n\n' - title += 'status: delete\n' - title += f'device: {selection.segment.dev_path}\n' + title = str(_("Partition")) + "\n\n" + title += "status: delete\n" + title += f"device: {selection.segment.dev_path}\n" for part in self._device.partition_infos: if part.path == selection.segment.dev_path: if part.partuuid: - title += f'partuuid: {part.partuuid}' + title += f"partuuid: {part.partuuid}" return title return str(selection.segment.dev_path) - return '' + return "" @override def filter_options(self, selection: DiskSegment, options: list[str]) -> list[str]: @@ -237,7 +238,7 @@ class PartitioningList(ListManager[DiskSegment]): not_filter = list(self._actions.values()) # only display formatting if the partition exists already elif not selection.segment.exists(): - not_filter += [self._actions['mark_formatting']] + not_filter += [self._actions["mark_formatting"]] else: # only allow options if the existing partition # was marked as formatting, otherwise we run into issues where @@ -245,29 +246,29 @@ class PartitioningList(ListManager[DiskSegment]): # 2. Switch back to old filesystem -> should unmark wipe now, but # how do we know it was the original one? not_filter += [ - self._actions['set_filesystem'], - self._actions['mark_bootable'] + self._actions["set_filesystem"], + self._actions["mark_bootable"], ] if self._using_gpt: not_filter += [ - self._actions['mark_esp'], - self._actions['mark_xbootldr'] + self._actions["mark_esp"], + self._actions["mark_xbootldr"], ] not_filter += [ - self._actions['btrfs_mark_compressed'], - self._actions['btrfs_mark_nodatacow'], - self._actions['btrfs_set_subvolumes'] + self._actions["btrfs_mark_compressed"], + self._actions["btrfs_mark_nodatacow"], + self._actions["btrfs_set_subvolumes"], ] # non btrfs partitions shouldn't get btrfs options if selection.segment.fs_type != FilesystemType.Btrfs: not_filter += [ - self._actions['btrfs_mark_compressed'], - self._actions['btrfs_mark_nodatacow'], - self._actions['btrfs_set_subvolumes'] + self._actions["btrfs_mark_compressed"], + self._actions["btrfs_mark_nodatacow"], + self._actions["btrfs_set_subvolumes"], ] else: - not_filter += [self._actions['assign_mountpoint']] + not_filter += [self._actions["assign_mountpoint"]] return [o for o in options if o not in not_filter] @@ -276,30 +277,26 @@ class PartitioningList(ListManager[DiskSegment]): self, action: str, entry: DiskSegment | None, - data: list[DiskSegment] + data: list[DiskSegment], ) -> list[DiskSegment]: if not entry: action_key = [k for k, v in self._actions.items() if v == action][0] match action_key: - case 'suggest_partition_layout': + case "suggest_partition_layout": part_mods = self.get_part_mods(data) device_mod = self._suggest_partition_layout(part_mods) if device_mod and device_mod.partitions: data = self.as_segments(device_mod.partitions) self._wipe = device_mod.wipe self._prompt = self._info + self.wipe_str() - case 'remove_added_partitions': + case "remove_added_partitions": if self._reset_confirmation(): - data = [ - s for s in data - if isinstance(s.segment, PartitionModification) - and s.segment.is_exists_or_modify() - ] + data = [s for s in data if isinstance(s.segment, PartitionModification) and s.segment.is_exists_or_modify()] elif isinstance(entry.segment, PartitionModification): partition = entry.segment action_key = [k for k, v in self._actions.items() if v == action][0] match action_key: - case 'assign_mountpoint': + case "assign_mountpoint": new_mountpoint = self._prompt_mountpoint() if not partition.is_swap(): if partition.is_home(): @@ -315,22 +312,22 @@ class PartitioningList(ListManager[DiskSegment]): if partition.is_home(): partition.flags = [] partition.set_flag(PartitionFlag.LINUX_HOME) - case 'mark_formatting': + case "mark_formatting": self._prompt_formatting(partition) - case 'mark_bootable': + case "mark_bootable": if not partition.is_swap(): partition.invert_flag(PartitionFlag.BOOT) - case 'mark_esp': + case "mark_esp": if not partition.is_root() and not partition.is_home() and not partition.is_swap(): if PartitionFlag.XBOOTLDR in partition.flags: partition.invert_flag(PartitionFlag.XBOOTLDR) partition.invert_flag(PartitionFlag.ESP) - case 'mark_xbootldr': + case "mark_xbootldr": if not partition.is_root() and not partition.is_home() and not partition.is_swap(): if PartitionFlag.ESP in partition.flags: partition.invert_flag(PartitionFlag.ESP) partition.invert_flag(PartitionFlag.XBOOTLDR) - case 'set_filesystem': + case "set_filesystem": fs_type = self._prompt_partition_fs_type() if fs_type: if partition.is_swap(): @@ -343,13 +340,13 @@ class PartitioningList(ListManager[DiskSegment]): # btrfs subvolumes will define mountpoints if fs_type == FilesystemType.Btrfs: partition.mountpoint = None - case 'btrfs_mark_compressed': + case "btrfs_mark_compressed": self._toggle_mount_option(partition, BtrfsMountOption.compress) - case 'btrfs_mark_nodatacow': + case "btrfs_mark_nodatacow": self._toggle_mount_option(partition, BtrfsMountOption.nodatacow) - case 'btrfs_set_subvolumes': + case "btrfs_set_subvolumes": self._set_btrfs_subvolumes(partition) - case 'delete_partition': + case "delete_partition": data = self._delete_partition(partition, data) else: part_mods = self.get_part_mods(data) @@ -362,47 +359,35 @@ class PartitioningList(ListManager[DiskSegment]): def _delete_partition( self, entry: PartitionModification, - data: list[DiskSegment] + data: list[DiskSegment], ) -> list[DiskSegment]: if entry.is_exists_or_modify(): entry.status = ModificationStatus.Delete part_mods = self.get_part_mods(data) else: - part_mods = [ - d.segment for d in data - if isinstance(d.segment, PartitionModification) - and d.segment != entry - ] + part_mods = [d.segment for d in data if isinstance(d.segment, PartitionModification) and d.segment != entry] return self.as_segments(part_mods) def _toggle_mount_option( self, partition: PartitionModification, - option: BtrfsMountOption + option: BtrfsMountOption, ) -> None: if option.value not in partition.mount_options: if option == BtrfsMountOption.compress: - partition.mount_options = [ - o for o in partition.mount_options - if o != BtrfsMountOption.nodatacow.value - ] + partition.mount_options = [o for o in partition.mount_options if o != BtrfsMountOption.nodatacow.value] - partition.mount_options = [ - o for o in partition.mount_options - if not o.startswith(BtrfsMountOption.compress.name) - ] + partition.mount_options = [o for o in partition.mount_options if not o.startswith(BtrfsMountOption.compress.name)] partition.mount_options.append(option.value) else: - partition.mount_options = [ - o for o in partition.mount_options if o != option.value - ] + partition.mount_options = [o for o in partition.mount_options if o != option.value] def _set_btrfs_subvolumes(self, partition: PartitionModification) -> None: partition.btrfs_subvols = SubvolumeMenu( partition.btrfs_subvols, - None + None, ).run() def _prompt_formatting(self, partition: PartitionModification) -> None: @@ -417,7 +402,7 @@ class PartitioningList(ListManager[DiskSegment]): # without asking the user which inner-filesystem they want to use. Since the flag 'encrypted' = True is already set, # it's safe to change the filesystem for this partition. if partition.fs_type == FilesystemType.Crypto_luks: - prompt = str(_('This partition is currently encrypted, to format it a filesystem has to be specified')) + '\n' + prompt = str(_("This partition is currently encrypted, to format it a filesystem has to be specified")) + "\n" fs_type = self._prompt_partition_fs_type(prompt) partition.fs_type = fs_type @@ -425,8 +410,8 @@ class PartitioningList(ListManager[DiskSegment]): partition.mountpoint = None def _prompt_mountpoint(self) -> Path: - header = str(_('Partition mount-points are relative to inside the installation, the boot would be /boot as an example.')) + '\n' - prompt = str(_('Mountpoint')) + header = str(_("Partition mount-points are relative to inside the installation, the boot would be /boot as an example.")) + "\n" + prompt = str(_("Mountpoint")) mountpoint = prompt_dir(prompt, header, validate=False, allow_skip=False) assert mountpoint @@ -442,30 +427,30 @@ class PartitioningList(ListManager[DiskSegment]): group, header=prompt, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Filesystem'))), - allow_skip=False + frame=FrameProperties.min(str(_("Filesystem"))), + allow_skip=False, ).run() match result.type_: case ResultType.Selection: return result.get_value() case _: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def _validate_value( self, sector_size: SectorSize, max_size: Size, - text: str + text: str, ) -> Size | None: - match = re.match(r'([0-9]+)([a-zA-Z|%]*)', text, re.I) + match = re.match(r"([0-9]+)([a-zA-Z|%]*)", text, re.I) if not match: return None str_value, unit = match.groups() - if unit == '%': + if unit == "%": value = int(max_size.value * (int(str_value) / 100)) unit = max_size.unit.name else: @@ -488,32 +473,32 @@ class PartitioningList(ListManager[DiskSegment]): def validate(value: str) -> str | None: size = self._validate_value(sector_size, max_size, value) if not size: - return str(_('Invalid size')) + return str(_("Invalid size")) return None device_info = self._device.device_info sector_size = device_info.sector_size - text = str(_('Selected free space segment on device {}:')).format(device_info.path) + '\n\n' + text = str(_("Selected free space segment on device {}:")).format(device_info.path) + "\n\n" free_space_table = FormattedOutput.as_table([free_space]) - prompt = text + free_space_table + '\n' + prompt = text + free_space_table + "\n" max_sectors = free_space.length.format_size(Unit.sectors, sector_size) max_bytes = free_space.length.format_size(Unit.B) - prompt += str(_('Size: {} / {}')).format(max_sectors, max_bytes) + '\n\n' - prompt += str(_('All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB...')) + '\n' - prompt += str(_('If no unit is provided, the value is interpreted as sectors')) + '\n' + prompt += str(_("Size: {} / {}")).format(max_sectors, max_bytes) + "\n\n" + prompt += str(_("All entered values can be suffixed with a unit: %, B, KB, KiB, MB, MiB...")) + "\n" + prompt += str(_("If no unit is provided, the value is interpreted as sectors")) + "\n" max_size = free_space.length - title = str(_('Size (default: {}): ')).format(max_size.format_highest()) + title = str(_("Size (default: {}): ")).format(max_size.format_highest()) result = EditMenu( title, - header=f'{prompt}\b', + header=f"{prompt}\b", allow_skip=True, - validator=validate + validator=validate, ).input() size: Size | None = None @@ -547,10 +532,10 @@ class PartitioningList(ListManager[DiskSegment]): start=free_space.start, length=length, fs_type=fs_type, - mountpoint=mountpoint + mountpoint=mountpoint, ) - if partition.mountpoint == Path('/boot'): + if partition.mountpoint == Path("/boot"): partition.set_flag(PartitionFlag.BOOT) if self._using_gpt: partition.set_flag(PartitionFlag.ESP) @@ -562,7 +547,7 @@ class PartitioningList(ListManager[DiskSegment]): return partition def _reset_confirmation(self) -> bool: - prompt = str(_('This will remove all newly added partitions, continue?')) + '\n' + prompt = str(_("This will remove all newly added partitions, continue?")) + "\n" result = SelectMenu[bool]( MenuItemGroup.yes_no(), @@ -571,14 +556,14 @@ class PartitioningList(ListManager[DiskSegment]): orientation=Orientation.HORIZONTAL, columns=2, reset_warning_msg=prompt, - allow_skip=False + allow_skip=False, ).run() return result.item() == MenuItem.yes() def _suggest_partition_layout( self, - data: list[PartitionModification] + data: list[PartitionModification], ) -> DeviceModification | None: # if modifications have been done already, inform the user # that this operation will erase those modifications @@ -593,7 +578,7 @@ class PartitioningList(ListManager[DiskSegment]): def manual_partitioning( device_mod: DeviceModification, - partition_table: PartitionTable + partition_table: PartitionTable, ) -> DeviceModification | None: menu_list = PartitioningList(device_mod, partition_table) mod = menu_list.get_device_mod() diff --git a/archinstall/lib/disk/subvolume_menu.py b/archinstall/lib/disk/subvolume_menu.py index f56e8538..e8b0ab17 100644 --- a/archinstall/lib/disk/subvolume_menu.py +++ b/archinstall/lib/disk/subvolume_menu.py @@ -21,19 +21,19 @@ class SubvolumeMenu(ListManager[SubvolumeModification]): def __init__( self, btrfs_subvols: list[SubvolumeModification], - prompt: str | None = None + prompt: str | None = None, ): self._actions = [ - str(_('Add subvolume')), - str(_('Edit subvolume')), - str(_('Delete subvolume')) + str(_("Add subvolume")), + str(_("Edit subvolume")), + str(_("Delete subvolume")), ] super().__init__( btrfs_subvols, [self._actions[0]], self._actions[1:], - prompt + prompt, ) @override @@ -42,10 +42,10 @@ class SubvolumeMenu(ListManager[SubvolumeModification]): def _add_subvolume(self, preset: SubvolumeModification | None = None) -> SubvolumeModification | None: result = EditMenu( - str(_('Subvolume name')), + str(_("Subvolume name")), alignment=Alignment.CENTER, allow_skip=True, - default_text=str(preset.name) if preset else None + default_text=str(preset.name) if preset else None, ).input() match result.type_: @@ -54,7 +54,7 @@ class SubvolumeMenu(ListManager[SubvolumeModification]): case ResultType.Selection: name = result.text() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") case _: assert_never(result.type_) @@ -64,7 +64,7 @@ class SubvolumeMenu(ListManager[SubvolumeModification]): str(_("Subvolume mountpoint")), header=header, allow_skip=True, - validate=False + validate=False, ) if not path: @@ -77,7 +77,7 @@ class SubvolumeMenu(ListManager[SubvolumeModification]): self, action: str, entry: SubvolumeModification | None, - data: list[SubvolumeModification] + data: list[SubvolumeModification], ) -> list[SubvolumeModification]: if action == self._actions[0]: # add new_subvolume = self._add_subvolume() diff --git a/archinstall/lib/disk/utils.py b/archinstall/lib/disk/utils.py index 5321ec54..2cfdaf57 100644 --- a/archinstall/lib/disk/utils.py +++ b/archinstall/lib/disk/utils.py @@ -15,15 +15,15 @@ class LsblkOutput(BaseModel): def _fetch_lsblk_info( dev_path: Path | str | None = None, reverse: bool = False, - full_dev_path: bool = False + full_dev_path: bool = False, ) -> LsblkOutput: - cmd = ['lsblk', '--json', '--bytes', '--output', ','.join(LsblkInfo.fields())] + cmd = ["lsblk", "--json", "--bytes", "--output", ",".join(LsblkInfo.fields())] if reverse: - cmd.append('--inverse') + cmd.append("--inverse") if full_dev_path: - cmd.append('--paths') + cmd.append("--paths") if dev_path: cmd.append(str(dev_path)) @@ -33,7 +33,7 @@ def _fetch_lsblk_info( except SysCallError as err: # Get the output minus the message/info from lsblk if it returns a non-zero exit code. if err.worker_log: - debug(f'Error calling lsblk: {err.worker_log.decode()}') + debug(f"Error calling lsblk: {err.worker_log.decode()}") if dev_path: raise DiskError(f'Failed to read disk "{dev_path}" with lsblk') @@ -47,7 +47,7 @@ def _fetch_lsblk_info( def get_lsblk_info( dev_path: Path | str, reverse: bool = False, - full_dev_path: bool = False + full_dev_path: bool = False, ) -> LsblkInfo: infos = _fetch_lsblk_info(dev_path, reverse=reverse, full_dev_path=full_dev_path) @@ -67,7 +67,7 @@ def get_lsblk_output() -> LsblkOutput: def find_lsblk_info( dev_path: Path | str, - info: list[LsblkInfo] + info: list[LsblkInfo], ) -> LsblkInfo | None: if isinstance(dev_path, str): dev_path = Path(dev_path) @@ -105,7 +105,7 @@ def disk_layouts() -> str: lsblk_output = get_lsblk_output() except SysCallError as err: warn(f"Could not return disk layouts: {err}") - return '' + return "" return lsblk_output.model_dump_json(indent=4) @@ -116,13 +116,13 @@ def umount(mountpoint: Path, recursive: bool = False) -> None: if not lsblk_info.mountpoints: return - debug(f'Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}') + debug(f"Partition {mountpoint} is currently mounted at: {[str(m) for m in lsblk_info.mountpoints]}") - cmd = ['umount'] + cmd = ["umount"] if recursive: - cmd.append('-R') + cmd.append("-R") for path in lsblk_info.mountpoints: - debug(f'Unmounting mountpoint: {path}') + debug(f"Unmounting mountpoint: {path}") SysCommand(cmd + [str(path)]) diff --git a/archinstall/lib/exceptions.py b/archinstall/lib/exceptions.py index f398b224..65691d33 100644 --- a/archinstall/lib/exceptions.py +++ b/archinstall/lib/exceptions.py @@ -11,7 +11,7 @@ class UnknownFilesystemFormat(Exception): class SysCallError(Exception): - def __init__(self, message: str, exit_code: int | None = None, worker_log: bytes = b'') -> None: + def __init__(self, message: str, exit_code: int | None = None, worker_log: bytes = b"") -> None: super().__init__(message) self.message = message self.exit_code = exit_code @@ -35,6 +35,6 @@ class Deprecated(Exception): class DownloadTimeout(Exception): - ''' + """ Download timeout exception raised by DownloadTimer. - ''' + """ diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 5fda5994..e087fe65 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -23,13 +23,13 @@ from .output import debug, error from .storage import storage # https://stackoverflow.com/a/43627833/929999 -_VT100_ESCAPE_REGEX = r'\x1B\[[?0-9;]*[a-zA-Z]' +_VT100_ESCAPE_REGEX = r"\x1B\[[?0-9;]*[a-zA-Z]" _VT100_ESCAPE_REGEX_BYTES = _VT100_ESCAPE_REGEX.encode() def generate_password(length: int = 64) -> str: haystack = string.printable # digits, ascii_letters, punctuation (!"#$[] etc) and whitespace - return ''.join(secrets.choice(haystack) for _ in range(length)) + return "".join(secrets.choice(haystack) for _ in range(length)) def locate_binary(name: str) -> str: @@ -39,11 +39,11 @@ def locate_binary(name: str) -> str: def clear_vt100_escape_codes(data: bytes) -> bytes: - return re.sub(_VT100_ESCAPE_REGEX_BYTES, b'', data) + return re.sub(_VT100_ESCAPE_REGEX_BYTES, b"", data) def clear_vt100_escape_codes_from_str(data: str) -> str: - return re.sub(_VT100_ESCAPE_REGEX, '', data) + return re.sub(_VT100_ESCAPE_REGEX, "", data) def jsonify(obj: object, safe: bool = True) -> object: @@ -57,12 +57,11 @@ def jsonify(obj: object, safe: bool = True) -> object: return { key: jsonify(value, safe) for key, value in obj.items() - if isinstance(key, compatible_types) - and not (isinstance(key, str) and key.startswith("!") and safe) + if isinstance(key, compatible_types) and not (isinstance(key, str) and key.startswith("!") and safe) } if isinstance(obj, Enum): return obj.value - if hasattr(obj, 'json'): + if hasattr(obj, "json"): # json() is a friendly name for json-helper, it should return # a dictionary representation of the object so that it can be # processed by the json library. @@ -105,26 +104,26 @@ class SysCommandWorker: cmd: str | list[str], peek_output: bool | None = False, environment_vars: dict[str, str] | None = None, - working_directory: str | None = './', - remove_vt100_escape_codes_from_lines: bool = True + working_directory: str | None = "./", + remove_vt100_escape_codes_from_lines: bool = True, ): if isinstance(cmd, str): cmd = shlex.split(cmd) - if cmd and not cmd[0].startswith(('/', './')): # Path() does not work well + if cmd and not cmd[0].startswith(("/", "./")): # Path() does not work well cmd[0] = locate_binary(cmd[0]) self.cmd = cmd self.peek_output = peek_output # define the standard locale for command outputs. For now the C ascii one. Can be overridden - self.environment_vars = {'LC_ALL': 'C'} + self.environment_vars = {"LC_ALL": "C"} if environment_vars: self.environment_vars.update(environment_vars) self.working_directory = working_directory self.exit_code: int | None = None - self._trace_log = b'' + self._trace_log = b"" self._trace_log_pos = 0 self.poll_object = epoll() self.child_fd: int | None = None @@ -147,13 +146,13 @@ class SysCommandWorker: return False def __iter__(self, *args: str, **kwargs: dict[str, Any]) -> Iterator[bytes]: - last_line = self._trace_log.rfind(b'\n') - lines = filter(None, self._trace_log[self._trace_log_pos:last_line].splitlines()) + last_line = self._trace_log.rfind(b"\n") + lines = filter(None, self._trace_log[self._trace_log_pos : last_line].splitlines()) for line in lines: if self.remove_vt100_escape_codes_from_lines: line = clear_vt100_escape_codes(line) - yield line + b'\n' + yield line + b"\n" self._trace_log_pos = last_line @@ -165,11 +164,11 @@ class SysCommandWorker: @override def __str__(self) -> str: try: - return self._trace_log.decode('utf-8') + return self._trace_log.decode("utf-8") except UnicodeDecodeError: return str(self._trace_log) - def __enter__(self) -> 'SysCommandWorker': + def __enter__(self) -> "SysCommandWorker": return self def __exit__(self, *args: str) -> None: @@ -195,7 +194,7 @@ class SysCommandWorker: raise SysCallError( f"{self.cmd} exited with abnormal exit code [{self.exit_code}]: {str(self)[-500:]}", self.exit_code, - worker_log=self._trace_log + worker_log=self._trace_log, ) def is_alive(self) -> bool: @@ -212,7 +211,7 @@ class SysCommandWorker: self.make_sure_we_are_executing() if self.child_fd: - return os.write(self.child_fd, data + (b'\n' if line_ending else b'')) + return os.write(self.child_fd, data + (b"\n" if line_ending else b"")) return 0 @@ -234,7 +233,7 @@ class SysCommandWorker: if self.peek_output: if isinstance(output, bytes): try: - output = output.decode('UTF-8') + output = output.decode("UTF-8") except UnicodeDecodeError: return False @@ -314,7 +313,7 @@ class SysCommandWorker: return True - def decode(self, encoding: str = 'UTF-8') -> str: + def decode(self, encoding: str = "UTF-8") -> str: return self._trace_log.decode(encoding) @@ -324,9 +323,9 @@ class SysCommand: cmd: str | list[str], peek_output: bool | None = False, environment_vars: dict[str, str] | None = None, - working_directory: str | None = './', - remove_vt100_escape_codes_from_lines: bool = True): - + working_directory: str | None = "./", + remove_vt100_escape_codes_from_lines: bool = True, + ): self.cmd = cmd self.peek_output = peek_output self.environment_vars = environment_vars @@ -363,7 +362,7 @@ class SysCommand: @override def __repr__(self, *args: list[Any], **kwargs: dict[str, Any]) -> str: - return self.decode('UTF-8', errors='backslashreplace') or '' + return self.decode("UTF-8", errors="backslashreplace") or "" def create_session(self) -> bool: """ @@ -379,22 +378,22 @@ class SysCommand: peek_output=self.peek_output, environment_vars=self.environment_vars, remove_vt100_escape_codes_from_lines=self.remove_vt100_escape_codes_from_lines, - working_directory=self.working_directory) as session: - + working_directory=self.working_directory, + ) as session: self.session = session while not self.session.ended: self.session.poll() if self.peek_output: - sys.stdout.write('\n') + sys.stdout.write("\n") sys.stdout.flush() return True - def decode(self, encoding: str = 'utf-8', errors: str = 'backslashreplace', strip: bool = True) -> str: + def decode(self, encoding: str = "utf-8", errors: str = "backslashreplace", strip: bool = True) -> str: if not self.session: - raise ValueError('No session available to decode') + raise ValueError("No session available to decode") val = self.session._trace_log.decode(encoding, errors=errors) @@ -404,10 +403,10 @@ class SysCommand: def output(self, remove_cr: bool = True) -> bytes: if not self.session: - raise ValueError('No session available') + raise ValueError("No session available") if remove_cr: - return self.session._trace_log.replace(b'\r\n', b'\n') + return self.session._trace_log.replace(b"\r\n", b"\n") return self.session._trace_log @@ -454,12 +453,12 @@ def run( input=input_data, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - check=True + check=True, ) def _pid_exists(pid: int) -> bool: try: - return any(subprocess.check_output(['ps', '--no-headers', '-o', 'pid', '-p', str(pid)]).strip()) + return any(subprocess.check_output(["ps", "--no-headers", "-o", "pid", "-p", str(pid)]).strip()) except subprocess.CalledProcessError: return False diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index 9daae3fc..c7764cf0 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -54,7 +54,7 @@ class GlobalMenu(AbstractMenu[None]): self._item_group = MenuItemGroup( menu_optioons, sort_items=False, - checkmarks=True + checkmarks=True, ) super().__init__(self._item_group, config=arch_config) @@ -62,152 +62,152 @@ class GlobalMenu(AbstractMenu[None]): def _get_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Archinstall language')), + text=str(_("Archinstall language")), action=self._select_archinstall_language, - display_action=lambda x: x.display_name if x else '', - key='archinstall_language' + display_action=lambda x: x.display_name if x else "", + key="archinstall_language", ), MenuItem( - text=str(_('Locales')), + text=str(_("Locales")), action=self._locale_selection, preview_action=self._prev_locale, - key='locale_config' + key="locale_config", ), MenuItem( - text=str(_('Mirrors and repositories')), + text=str(_("Mirrors and repositories")), action=self._mirror_configuration, preview_action=self._prev_mirror_config, - key='mirror_config' + key="mirror_config", ), MenuItem( - text=str(_('Disk configuration')), + text=str(_("Disk configuration")), action=self._select_disk_config, preview_action=self._prev_disk_config, mandatory=True, - key='disk_config' + key="disk_config", ), MenuItem( - text=str(_('Disk encryption')), + text=str(_("Disk encryption")), action=self._disk_encryption, preview_action=self._prev_disk_encryption, - dependencies=['disk_config'], - key='disk_encryption' + dependencies=["disk_config"], + key="disk_encryption", ), MenuItem( - text=str(_('Swap')), + text=str(_("Swap")), value=True, action=ask_for_swap, preview_action=self._prev_swap, - key='swap', + key="swap", ), MenuItem( - text=str(_('Bootloader')), + text=str(_("Bootloader")), value=Bootloader.get_default(), action=self._select_bootloader, preview_action=self._prev_bootloader, mandatory=True, - key='bootloader', + key="bootloader", ), MenuItem( - text=str(_('Unified kernel images')), + text=str(_("Unified kernel images")), value=False, enabled=SysInfo.has_uefi(), action=ask_for_uki, preview_action=self._prev_uki, - key='uki', + key="uki", ), MenuItem( - text=str(_('Hostname')), - value='archlinux', + text=str(_("Hostname")), + value="archlinux", action=ask_hostname, preview_action=self._prev_hostname, - key='hostname', + key="hostname", ), MenuItem( - text=str(_('Root password')), + text=str(_("Root password")), action=self._set_root_password, preview_action=self._prev_root_pwd, - key='root_enc_password', + key="root_enc_password", ), MenuItem( - text=str(_('User account')), + text=str(_("User account")), action=self._create_user_account, preview_action=self._prev_users, - key='users' + key="users", ), MenuItem( - text=str(_('Profile')), + text=str(_("Profile")), action=self._select_profile, preview_action=self._prev_profile, - key='profile_config' + key="profile_config", ), MenuItem( - text=str(_('Audio')), + text=str(_("Audio")), action=ask_for_audio_selection, preview_action=self._prev_audio, - key='audio_config' + key="audio_config", ), MenuItem( - text=str(_('Kernels')), - value=['linux'], + text=str(_("Kernels")), + value=["linux"], action=select_kernel, preview_action=self._prev_kernel, mandatory=True, - key='kernels' + key="kernels", ), MenuItem( - text=str(_('Network configuration')), + text=str(_("Network configuration")), action=ask_to_configure_network, value={}, preview_action=self._prev_network_config, - key='network_config' + key="network_config", ), MenuItem( - text=str(_('Parallel Downloads')), + text=str(_("Parallel Downloads")), action=add_number_of_parallel_downloads, value=0, preview_action=self._prev_parallel_dw, - key='parallel_downloads' + key="parallel_downloads", ), MenuItem( - text=str(_('Additional packages')), + text=str(_("Additional packages")), action=self._select_additional_packages, value=[], preview_action=self._prev_additional_pkgs, - key='packages' + key="packages", ), MenuItem( - text=str(_('Timezone')), + text=str(_("Timezone")), action=ask_for_a_timezone, - value='UTC', + value="UTC", preview_action=self._prev_tz, - key='timezone' + key="timezone", ), MenuItem( - text=str(_('Automatic time sync (NTP)')), + text=str(_("Automatic time sync (NTP)")), action=ask_ntp, value=True, preview_action=self._prev_ntp, - key='ntp' + key="ntp", ), MenuItem( - text='' + text="", ), MenuItem( - text=str(_('Save configuration')), + text=str(_("Save configuration")), action=lambda x: self._safe_config(), - key=f'{CONFIG_KEY}_save' + key=f"{CONFIG_KEY}_save", ), MenuItem( - text=str(_('Install')), + text=str(_("Install")), preview_action=self._prev_install_invalid_config, - key=f'{CONFIG_KEY}_install' + key=f"{CONFIG_KEY}_install", ), MenuItem( - text=str(_('Abort')), + text=str(_("Abort")), action=lambda x: exit(1), - key=f'{CONFIG_KEY}_abort' - ) + key=f"{CONFIG_KEY}_abort", + ), ] def _safe_config(self) -> None: @@ -225,7 +225,7 @@ class GlobalMenu(AbstractMenu[None]): return item.has_value() def has_superuser() -> bool: - item = self._item_group.find_by_key('users') + item = self._item_group.find_by_key("users") if item.has_value(): users = item.value @@ -236,10 +236,10 @@ class GlobalMenu(AbstractMenu[None]): missing = set() for item in self._item_group.items: - if item.key in ['root_enc_password', 'users']: - if not check('root_enc_password') and not has_superuser(): + if item.key in ["root_enc_password", "users"]: + if not check("root_enc_password") and not has_superuser(): missing.add( - str(_('Either root-password or at least 1 user with sudo privileges must be specified')) + str(_("Either root-password or at least 1 user with sudo privileges must be specified")), ) elif item.mandatory: if not check(item.key): @@ -258,6 +258,7 @@ class GlobalMenu(AbstractMenu[None]): def _select_archinstall_language(self, preset: Language) -> Language: from .interactions.general_conf import select_archinstall_language + language = select_archinstall_language(translation_handler.translated_languages, preset) translation_handler.activate(language) @@ -277,11 +278,11 @@ class GlobalMenu(AbstractMenu[None]): self._item_group.find_by_key(o.key).text = o.text def _disk_encryption(self, preset: DiskEncryption | None) -> DiskEncryption | None: - disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value + disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key("disk_config").value if not disk_config: # this should not happen as the encryption menu has the disk_config as dependency - raise ValueError('No disk layout specified') + raise ValueError("No disk layout specified") if not DiskEncryption.validate_enc(disk_config): return None @@ -306,26 +307,26 @@ class GlobalMenu(AbstractMenu[None]): if network_config.type == NicType.MANUAL: output = FormattedOutput.as_table(network_config.nics) else: - output = f'{_('Network configuration')}:\n{network_config.type.display_msg()}' + output = f"{_('Network configuration')}:\n{network_config.type.display_msg()}" return output return None def _prev_additional_pkgs(self, item: MenuItem) -> str | None: if item.value: - output = '\n'.join(sorted(item.value)) + output = "\n".join(sorted(item.value)) return output return None def _prev_tz(self, item: MenuItem) -> str | None: if item.value: - return f'{_("Timezone")}: {item.value}' + return f"{_('Timezone')}: {item.value}" return None def _prev_ntp(self, item: MenuItem) -> str | None: if item.value is not None: - output = f'{_("NTP")}: ' - output += str(_('Enabled')) if item.value else str(_('Disabled')) + output = f"{_('NTP')}: " + output += str(_("Enabled")) if item.value else str(_("Disabled")) return output return None @@ -333,13 +334,13 @@ class GlobalMenu(AbstractMenu[None]): disk_layout_conf: DiskLayoutConfiguration | None = item.value if disk_layout_conf: - output = str(_('Configuration type: {}')).format(disk_layout_conf.config_type.display_msg()) + '\n' + output = str(_("Configuration type: {}")).format(disk_layout_conf.config_type.display_msg()) + "\n" if disk_layout_conf.config_type == DiskLayoutType.Pre_mount: - output += str(_('Mountpoint')) + ': ' + str(disk_layout_conf.mountpoint) + output += str(_("Mountpoint")) + ": " + str(disk_layout_conf.mountpoint) if disk_layout_conf.lvm_config: - output += '{}: {}'.format(str(_('LVM configuration type')), disk_layout_conf.lvm_config.config_type.display_msg()) + output += "{}: {}".format(str(_("LVM configuration type")), disk_layout_conf.lvm_config.config_type.display_msg()) return output @@ -347,72 +348,72 @@ class GlobalMenu(AbstractMenu[None]): def _prev_swap(self, item: MenuItem) -> str | None: if item.value is not None: - output = f'{_("Swap on zram")}: ' - output += str(_('Enabled')) if item.value else str(_('Disabled')) + output = f"{_('Swap on zram')}: " + output += str(_("Enabled")) if item.value else str(_("Disabled")) return output return None def _prev_uki(self, item: MenuItem) -> str | None: if item.value is not None: - output = f'{_('Unified kernel images')}: ' - output += str(_('Enabled')) if item.value else str(_('Disabled')) + output = f"{_('Unified kernel images')}: " + output += str(_("Enabled")) if item.value else str(_("Disabled")) return output return None def _prev_hostname(self, item: MenuItem) -> str | None: if item.value is not None: - return f'{_("Hostname")}: {item.value}' + return f"{_('Hostname')}: {item.value}" return None def _prev_root_pwd(self, item: MenuItem) -> str | None: if item.value is not None: password: Password = item.value - return f'{_("Root password")}: {password.hidden()}' + return f"{_('Root password')}: {password.hidden()}" return None def _prev_audio(self, item: MenuItem) -> str | None: if item.value is not None: config: AudioConfiguration = item.value - return f'{_("Audio")}: {config.audio.value}' + return f"{_('Audio')}: {config.audio.value}" return None def _prev_parallel_dw(self, item: MenuItem) -> str | None: if item.value is not None: - return f'{_("Parallel Downloads")}: {item.value}' + return f"{_('Parallel Downloads')}: {item.value}" return None def _prev_kernel(self, item: MenuItem) -> str | None: if item.value: - kernel = ', '.join(item.value) - return f'{_("Kernel")}: {kernel}' + kernel = ", ".join(item.value) + return f"{_('Kernel')}: {kernel}" return None def _prev_bootloader(self, item: MenuItem) -> str | None: if item.value is not None: - return f'{_("Bootloader")}: {item.value.value}' + return f"{_('Bootloader')}: {item.value.value}" return None def _prev_disk_encryption(self, item: MenuItem) -> str | None: - disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key('disk_config').value + disk_config: DiskLayoutConfiguration | None = self._item_group.find_by_key("disk_config").value enc_config: DiskEncryption | None = item.value if disk_config and not DiskEncryption.validate_enc(disk_config): - return str(_('LVM disk encryption with more than 2 partitions is currently not supported')) + return str(_("LVM disk encryption with more than 2 partitions is currently not supported")) if enc_config: enc_type = EncryptionType.type_to_text(enc_config.encryption_type) - output = str(_('Encryption type')) + f': {enc_type}\n' + output = str(_("Encryption type")) + f": {enc_type}\n" if enc_config.encryption_password: - output += str(_('Password')) + f': {enc_config.encryption_password.hidden()}\n' + output += str(_("Password")) + f": {enc_config.encryption_password.hidden()}\n" if enc_config.partitions: - output += f'Partitions: {len(enc_config.partitions)} selected\n' + output += f"Partitions: {len(enc_config.partitions)} selected\n" elif enc_config.lvm_volumes: - output += f'LVM volumes: {len(enc_config.lvm_volumes)} selected\n' + output += f"LVM volumes: {len(enc_config.lvm_volumes)} selected\n" if enc_config.hsm_device: - output += f'HSM: {enc_config.hsm_device.manufacturer}' + output += f"HSM: {enc_config.hsm_device.manufacturer}" return output @@ -429,12 +430,12 @@ class GlobalMenu(AbstractMenu[None]): XXX: The caller is responsible for wrapping the string with the translation shim if necessary. """ - bootloader = self._item_group.find_by_key('bootloader').value + bootloader = self._item_group.find_by_key("bootloader").value root_partition: PartitionModification | None = None boot_partition: PartitionModification | None = None efi_partition: PartitionModification | None = None - if disk_config := self._item_group.find_by_key('disk_config').value: + if disk_config := self._item_group.find_by_key("disk_config").value: for layout in disk_config.device_modifications: if root_partition := layout.get_root_partition(): break @@ -469,9 +470,9 @@ class GlobalMenu(AbstractMenu[None]): def _prev_install_invalid_config(self, item: MenuItem) -> str | None: if missing := self._missing_configs(): - text = str(_('Missing configurations:\n')) + text = str(_("Missing configurations:\n")) for m in missing: - text += f'- {m}\n' + text += f"- {m}\n" return text[:-1] # remove last new line if error := self._validate_bootloader(): @@ -490,34 +491,34 @@ class GlobalMenu(AbstractMenu[None]): profile_config: ProfileConfiguration | None = item.value if profile_config and profile_config.profile: - output = str(_('Profiles')) + ': ' + output = str(_("Profiles")) + ": " if profile_names := profile_config.profile.current_selection_names(): - output += ', '.join(profile_names) + '\n' + output += ", ".join(profile_names) + "\n" else: - output += profile_config.profile.name + '\n' + output += profile_config.profile.name + "\n" if profile_config.gfx_driver: - output += str(_('Graphics driver')) + ': ' + profile_config.gfx_driver.value + '\n' + output += str(_("Graphics driver")) + ": " + profile_config.gfx_driver.value + "\n" if profile_config.greeter: - output += str(_('Greeter')) + ': ' + profile_config.greeter.value + '\n' + output += str(_("Greeter")) + ": " + profile_config.greeter.value + "\n" return output return None def _set_root_password(self, preset: str | None = None) -> Password | None: - password = get_password(text=str(_('Root password')), allow_skip=True) + password = get_password(text=str(_("Root password")), allow_skip=True) return password def _select_disk_config( self, - preset: DiskLayoutConfiguration | None = None + preset: DiskLayoutConfiguration | None = None, ) -> DiskLayoutConfiguration | None: disk_config = DiskLayoutConfigurationMenu(preset).run() if disk_config != preset: - self._menu_item_group.find_by_key('disk_encryption').value = None + self._menu_item_group.find_by_key("disk_encryption").value = None return disk_config @@ -525,7 +526,7 @@ class GlobalMenu(AbstractMenu[None]): bootloader = ask_for_bootloader(preset) if bootloader: - uki = self._item_group.find_by_key('uki') + uki = self._item_group.find_by_key("uki") if not SysInfo.has_uefi() or not bootloader.has_uki_support(): uki.value = False uki.enabled = False @@ -536,11 +537,12 @@ class GlobalMenu(AbstractMenu[None]): def _select_profile(self, current_profile: ProfileConfiguration | None): from .profile.profile_menu import ProfileMenu + profile_config = ProfileMenu(preset=current_profile).run() return profile_config def _select_additional_packages(self, preset: list[str]) -> list[str]: - config: MirrorConfiguration | None = self._item_group.find_by_key('mirror_config').value + config: MirrorConfiguration | None = self._item_group.find_by_key("mirror_config").value repositories: set[Repository] = set() if config: @@ -548,7 +550,7 @@ class GlobalMenu(AbstractMenu[None]): packages = ask_additional_packages_to_install( preset, - repositories=repositories + repositories=repositories, ) return packages @@ -579,28 +581,28 @@ class GlobalMenu(AbstractMenu[None]): mirror_config: MirrorConfiguration = item.value - output = '' + output = "" if mirror_config.mirror_regions: - title = str(_('Selected mirror regions')) - divider = '-' * len(title) + title = str(_("Selected mirror regions")) + divider = "-" * len(title) regions = mirror_config.region_names - output += f'{title}\n{divider}\n{regions}\n\n' + output += f"{title}\n{divider}\n{regions}\n\n" if mirror_config.custom_servers: - title = str(_('Custom servers')) - divider = '-' * len(title) + title = str(_("Custom servers")) + divider = "-" * len(title) servers = mirror_config.custom_server_urls - output += f'{title}\n{divider}\n{servers}\n\n' + output += f"{title}\n{divider}\n{servers}\n\n" if mirror_config.optional_repositories: - title = str(_('Optional repositories')) - divider = '-' * len(title) - repos = ', '.join([r.value for r in mirror_config.optional_repositories]) - output += f'{title}\n{divider}\n{repos}\n\n' + title = str(_("Optional repositories")) + divider = "-" * len(title) + repos = ", ".join([r.value for r in mirror_config.optional_repositories]) + output += f"{title}\n{divider}\n{repos}\n\n" if mirror_config.custom_repositories: - title = str(_('Custom repositories')) + title = str(_("Custom repositories")) table = FormattedOutput.as_table(mirror_config.custom_repositories) - output += f'{title}:\n\n{table}' + output += f"{title}:\n\n{table}" return output.strip() diff --git a/archinstall/lib/hardware.py b/archinstall/lib/hardware.py index b1884173..5e0f240b 100644 --- a/archinstall/lib/hardware.py +++ b/archinstall/lib/hardware.py @@ -18,12 +18,12 @@ if TYPE_CHECKING: class CpuVendor(Enum): - AuthenticAMD = 'amd' - GenuineIntel = 'intel' - _Unknown = 'unknown' + AuthenticAMD = "amd" + GenuineIntel = "intel" + _Unknown = "unknown" @classmethod - def get_vendor(cls, name: str) -> 'CpuVendor': + def get_vendor(cls, name: str) -> "CpuVendor": if vendor := getattr(cls, name, None): return vendor else: @@ -39,54 +39,52 @@ class CpuVendor(Enum): def get_ucode(self) -> Path | None: if self._has_microcode(): - return Path(self.value + '-ucode.img') + return Path(self.value + "-ucode.img") return None class GfxPackage(Enum): - Dkms = 'dkms' - IntelMediaDriver = 'intel-media-driver' - LibvaIntelDriver = 'libva-intel-driver' - LibvaMesaDriver = 'libva-mesa-driver' - LibvaNvidiaDriver = 'libva-nvidia-driver' + Dkms = "dkms" + IntelMediaDriver = "intel-media-driver" + LibvaIntelDriver = "libva-intel-driver" + LibvaMesaDriver = "libva-mesa-driver" + LibvaNvidiaDriver = "libva-nvidia-driver" Mesa = "mesa" - NvidiaDkms = 'nvidia-dkms' - NvidiaOpenDkms = 'nvidia-open-dkms' - VulkanIntel = 'vulkan-intel' - VulkanRadeon = 'vulkan-radeon' - VulkanNouveau = 'vulkan-nouveau' + NvidiaDkms = "nvidia-dkms" + NvidiaOpenDkms = "nvidia-open-dkms" + VulkanIntel = "vulkan-intel" + VulkanRadeon = "vulkan-radeon" + VulkanNouveau = "vulkan-nouveau" Xf86VideoAmdgpu = "xf86-video-amdgpu" Xf86VideoAti = "xf86-video-ati" - Xf86VideoNouveau = 'xf86-video-nouveau' - Xf86VideoVmware = 'xf86-video-vmware' - XorgServer = 'xorg-server' - XorgXinit = 'xorg-xinit' + Xf86VideoNouveau = "xf86-video-nouveau" + Xf86VideoVmware = "xf86-video-vmware" + XorgServer = "xorg-server" + XorgXinit = "xorg-xinit" class GfxDriver(Enum): - AllOpenSource = 'All open-source' - AmdOpenSource = 'AMD / ATI (open-source)' - IntelOpenSource = 'Intel (open-source)' - NvidiaOpenKernel = 'Nvidia (open kernel module for newer GPUs, Turing+)' - NvidiaOpenSource = 'Nvidia (open-source nouveau driver)' - NvidiaProprietary = 'Nvidia (proprietary)' - VMOpenSource = 'VMware / VirtualBox (open-source)' + AllOpenSource = "All open-source" + AmdOpenSource = "AMD / ATI (open-source)" + IntelOpenSource = "Intel (open-source)" + NvidiaOpenKernel = "Nvidia (open kernel module for newer GPUs, Turing+)" + NvidiaOpenSource = "Nvidia (open-source nouveau driver)" + NvidiaProprietary = "Nvidia (proprietary)" + VMOpenSource = "VMware / VirtualBox (open-source)" def is_nvidia(self) -> bool: match self: - case GfxDriver.NvidiaProprietary | \ - GfxDriver.NvidiaOpenSource | \ - GfxDriver.NvidiaOpenKernel: + case GfxDriver.NvidiaProprietary | GfxDriver.NvidiaOpenSource | GfxDriver.NvidiaOpenKernel: return True case _: return False def packages_text(self) -> str: pkg_names = [p.value for p in self.gfx_packages()] - text = str(_('Installed packages')) + ':\n' + text = str(_("Installed packages")) + ":\n" for p in sorted(pkg_names): - text += f'\t- {p}\n' + text += f"\t- {p}\n" return text @@ -106,7 +104,7 @@ class GfxDriver(Enum): GfxPackage.IntelMediaDriver, GfxPackage.VulkanRadeon, GfxPackage.VulkanIntel, - GfxPackage.VulkanNouveau + GfxPackage.VulkanNouveau, ] case GfxDriver.AmdOpenSource: packages += [ @@ -114,38 +112,38 @@ class GfxDriver(Enum): GfxPackage.Xf86VideoAmdgpu, GfxPackage.Xf86VideoAti, GfxPackage.LibvaMesaDriver, - GfxPackage.VulkanRadeon + GfxPackage.VulkanRadeon, ] case GfxDriver.IntelOpenSource: packages += [ GfxPackage.Mesa, GfxPackage.LibvaIntelDriver, GfxPackage.IntelMediaDriver, - GfxPackage.VulkanIntel + GfxPackage.VulkanIntel, ] case GfxDriver.NvidiaOpenKernel: packages += [ GfxPackage.NvidiaOpenDkms, GfxPackage.Dkms, - GfxPackage.LibvaNvidiaDriver + GfxPackage.LibvaNvidiaDriver, ] case GfxDriver.NvidiaOpenSource: packages += [ GfxPackage.Mesa, GfxPackage.Xf86VideoNouveau, GfxPackage.LibvaMesaDriver, - GfxPackage.VulkanNouveau + GfxPackage.VulkanNouveau, ] case GfxDriver.NvidiaProprietary: packages += [ GfxPackage.NvidiaDkms, GfxPackage.Dkms, - GfxPackage.LibvaNvidiaDriver + GfxPackage.LibvaNvidiaDriver, ] case GfxDriver.VMOpenSource: packages += [ GfxPackage.Mesa, - GfxPackage.Xf86VideoVmware + GfxPackage.Xf86VideoVmware, ] return packages @@ -181,7 +179,7 @@ class _SysInfo: with mem_info_path.open() as file: for line in file: - key, value = line.strip().split(':') + key, value = line.strip().split(":") num = value.split()[0] mem_info[key] = int(num) @@ -195,7 +193,7 @@ class _SysInfo: """ Returns loaded kernel modules """ - modules_path = Path('/proc/modules') + modules_path = Path("/proc/modules") modules: list[str] = [] with modules_path.open() as file: @@ -213,42 +211,42 @@ class SysInfo: @staticmethod def has_wifi() -> bool: ifaces = list(list_interfaces().values()) - return 'WIRELESS' in enrich_iface_types(ifaces).values() + return "WIRELESS" in enrich_iface_types(ifaces).values() @staticmethod def has_uefi() -> bool: - return os.path.isdir('/sys/firmware/efi') + return os.path.isdir("/sys/firmware/efi") @staticmethod def _graphics_devices() -> dict[str, str]: cards: dict[str, str] = {} for line in SysCommand("lspci"): - if b' VGA ' in line or b' 3D ' in line: - _, identifier = line.split(b': ', 1) - cards[identifier.strip().decode('UTF-8')] = str(line) + if b" VGA " in line or b" 3D " in line: + _, identifier = line.split(b": ", 1) + cards[identifier.strip().decode("UTF-8")] = str(line) return cards @staticmethod def has_nvidia_graphics() -> bool: - return any('nvidia' in x.lower() for x in SysInfo._graphics_devices()) + return any("nvidia" in x.lower() for x in SysInfo._graphics_devices()) @staticmethod def has_amd_graphics() -> bool: - return any('amd' in x.lower() for x in SysInfo._graphics_devices()) + return any("amd" in x.lower() for x in SysInfo._graphics_devices()) @staticmethod def has_intel_graphics() -> bool: - return any('intel' in x.lower() for x in SysInfo._graphics_devices()) + return any("intel" in x.lower() for x in SysInfo._graphics_devices()) @staticmethod def cpu_vendor() -> CpuVendor | None: - if vendor := _sys_info.cpu_info.get('vendor_id'): + if vendor := _sys_info.cpu_info.get("vendor_id"): return CpuVendor.get_vendor(vendor) return None @staticmethod def cpu_model() -> str | None: - return _sys_info.cpu_info.get('model name', None) + return _sys_info.cpu_info.get("model name", None) @staticmethod def sys_vendor() -> str: @@ -262,20 +260,20 @@ class SysInfo: @staticmethod def mem_available() -> int: - return _sys_info.mem_info_by_key('MemAvailable') + return _sys_info.mem_info_by_key("MemAvailable") @staticmethod def mem_free() -> int: - return _sys_info.mem_info_by_key('MemFree') + return _sys_info.mem_info_by_key("MemFree") @staticmethod def mem_total() -> int: - return _sys_info.mem_info_by_key('MemTotal') + return _sys_info.mem_info_by_key("MemTotal") @staticmethod def virtualization() -> str | None: try: - return str(SysCommand("systemd-detect-virt")).strip('\r\n') + return str(SysCommand("systemd-detect-virt")).strip("\r\n") except SysCallError as err: debug(f"Could not detect virtual system: {err}") @@ -293,33 +291,33 @@ class SysInfo: @staticmethod def requires_sof_fw() -> bool: - return 'snd_sof' in _sys_info.loaded_modules + return "snd_sof" in _sys_info.loaded_modules @staticmethod def requires_alsa_fw() -> bool: modules = ( - 'snd_asihpi', - 'snd_cs46xx', - 'snd_darla20', - 'snd_darla24', - 'snd_echo3g', - 'snd_emu10k1', - 'snd_gina20', - 'snd_gina24', - 'snd_hda_codec_ca0132', - 'snd_hdsp', - 'snd_indigo', - 'snd_indigodj', - 'snd_indigodjx', - 'snd_indigoio', - 'snd_indigoiox', - 'snd_layla20', - 'snd_layla24', - 'snd_mia', - 'snd_mixart', - 'snd_mona', - 'snd_pcxhr', - 'snd_vx_lib' + "snd_asihpi", + "snd_cs46xx", + "snd_darla20", + "snd_darla24", + "snd_echo3g", + "snd_emu10k1", + "snd_gina20", + "snd_gina24", + "snd_hda_codec_ca0132", + "snd_hdsp", + "snd_indigo", + "snd_indigodj", + "snd_indigodjx", + "snd_indigoio", + "snd_indigoiox", + "snd_layla20", + "snd_layla24", + "snd_mia", + "snd_mixart", + "snd_mona", + "snd_pcxhr", + "snd_vx_lib", ) for loaded_module in _sys_info.loaded_modules: diff --git a/archinstall/lib/installer.py b/archinstall/lib/installer.py index 6d82b6d8..d7a51f09 100644 --- a/archinstall/lib/installer.py +++ b/archinstall/lib/installer.py @@ -67,24 +67,24 @@ class Installer: disk_config: DiskLayoutConfiguration, disk_encryption: DiskEncryption | None = None, base_packages: list[str] = [], - kernels: list[str] | None = None + kernels: list[str] | None = None, ): """ `Installer()` is the wrapper for most basic installation steps. It also wraps :py:func:`~archinstall.Installer.pacstrap` among other things. """ self._base_packages = base_packages or __packages__[:3] - self.kernels = kernels or ['linux'] + self.kernels = kernels or ["linux"] self._disk_config = disk_config self._disk_encryption = disk_encryption or DiskEncryption(EncryptionType.NoEncryption) self.target: Path = target - self.init_time = time.strftime('%Y-%m-%d_%H-%M-%S') - self.milliseconds = int(str(time.time()).split('.')[1]) + self.init_time = time.strftime("%Y-%m-%d_%H-%M-%S") + self.milliseconds = int(str(time.time()).split(".")[1]) self._helper_flags: dict[str, str | bool | None] = { - 'base': False, - 'bootloader': None + "base": False, + "bootloader": None, } for kernel in self.kernels: @@ -97,8 +97,8 @@ class Installer: self.post_base_install: list[Callable] = [] # type: ignore[type-arg] # TODO: Figure out which one of these two we'll use.. But currently we're mixing them.. - storage['session'] = self - storage['installation_session'] = self + storage["session"] = self + storage["installation_session"] = self self._modules: list[str] = [] self._binaries: list[str] = [] @@ -107,8 +107,17 @@ class Installer: # systemd, sd-vconsole and sd-encrypt will be replaced by udev, keymap and encrypt # if HSM is not used to encrypt the root volume. Check mkinitcpio() function for that override. self._hooks: list[str] = [ - "base", "systemd", "autodetect", "microcode", "modconf", "kms", "keyboard", - "sd-vconsole", "block", "filesystems", "fsck" + "base", + "systemd", + "autodetect", + "microcode", + "modconf", + "kms", + "keyboard", + "sd-vconsole", + "block", + "filesystems", + "fsck", ] self._kernel_params: list[str] = [] self._fstab_entries: list[str] = [] @@ -118,7 +127,7 @@ class Installer: self.pacman = Pacman(self.target, arch_config_handler.args.silent) - def __enter__(self) -> 'Installer': + def __enter__(self) -> "Installer": return self def __exit__(self, exc_type: type[BaseException] | None, exc_val, exc_tb: TracebackType | None) -> bool: @@ -129,21 +138,21 @@ class Installer: # We avoid printing /mnt/ because that might confuse people if they note it down # and then reboot, and a identical log file will be found in the ISO medium anyway. - log_file = os.path.join(storage['LOG_PATH'], storage['LOG_FILE']) + log_file = os.path.join(storage["LOG_PATH"], storage["LOG_FILE"]) Tui.print(str(_("[!] A log file has been created here: {}").format(log_file))) - Tui.print(str(_('Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues'))) + Tui.print(str(_("Please submit this issue (and file) to https://github.com/archlinux/archinstall/issues"))) raise exc_val if not (missing_steps := self.post_install_check()): - msg = f'Installation completed without any errors.\nLog files temporarily available at {storage["LOG_PATH"]}.\nYou may reboot when ready.\n' - log(msg, fg='green') + msg = f"Installation completed without any errors.\nLog files temporarily available at {storage['LOG_PATH']}.\nYou may reboot when ready.\n" + log(msg, fg="green") self.sync_log_to_install_medium() return True else: - warn('Some required steps were not successfully installed/configured before leaving the installer:') + warn("Some required steps were not successfully installed/configured before leaving the installer:") for step in missing_steps: - warn(f' - {step}') + warn(f" - {step}") warn(f"Detailed error logs can be found at: {storage['LOG_PATH']}") warn("Submit this zip file as an issue to https://github.com/archlinux/archinstall/issues") @@ -167,7 +176,7 @@ class Installer: """ if not arch_config_handler.args.skip_ntp: - info(str(_('Waiting for time sync (timedatectl show) to complete.'))) + info(str(_("Waiting for time sync (timedatectl show) to complete."))) started_wait = time.time() notified = False @@ -176,28 +185,28 @@ class Installer: notified = True warn(str(_("Time synchronization not completing, while you wait - check the docs for workarounds: https://archinstall.readthedocs.io/"))) - time_val = SysCommand('timedatectl show --property=NTPSynchronized --value').decode() - if time_val and time_val.strip() == 'yes': + time_val = SysCommand("timedatectl show --property=NTPSynchronized --value").decode() + if time_val and time_val.strip() == "yes": break time.sleep(1) else: - info(str(_('Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)'))) + info(str(_("Skipping waiting for automatic time sync (this can cause issues if time is out of sync during installation)"))) - info('Waiting for automatic mirror selection (reflector) to complete.') - while self._service_state('reflector') not in ('dead', 'failed', 'exited'): + info("Waiting for automatic mirror selection (reflector) to complete.") + while self._service_state("reflector") not in ("dead", "failed", "exited"): time.sleep(1) # info('Waiting for pacman-init.service to complete.') # while self._service_state('pacman-init') not in ('dead', 'failed', 'exited'): # time.sleep(1) - info(str(_('Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete.'))) + info(str(_("Waiting for Arch Linux keyring sync (archlinux-keyring-wkd-sync) to complete."))) # Wait for the timer to kick in - while self._service_started('archlinux-keyring-wkd-sync.timer') is None: + while self._service_started("archlinux-keyring-wkd-sync.timer") is None: time.sleep(1) # Wait for the service to enter a finished state - while self._service_state('archlinux-keyring-wkd-sync.service') not in ('dead', 'failed', 'exited'): + while self._service_state("archlinux-keyring-wkd-sync.service") not in ("dead", "failed", "exited"): time.sleep(1) def _verify_boot_part(self) -> None: @@ -208,14 +217,14 @@ class Installer: NOTE: this function should be run AFTER running the mount_ordered_layout function """ - boot_mount = self.target / 'boot' + boot_mount = self.target / "boot" lsblk_info = get_lsblk_by_mountpoint(boot_mount) if len(lsblk_info) > 0: if lsblk_info[0].size < Size(200, Unit.MiB, SectorSize.default()): raise DiskError( - f'The boot partition mounted at {boot_mount} is not large enough to install a boot loader. ' - f'Please resize it to at least 200MiB and re-run the installation.' + f"The boot partition mounted at {boot_mount} is not large enough to install a boot loader. " + f"Please resize it to at least 200MiB and re-run the installation.", ) def sanity_check(self) -> None: @@ -223,7 +232,7 @@ class Installer: self._verify_service_stop() def mount_ordered_layout(self) -> None: - debug('Mounting ordered layout') + debug("Mounting ordered layout") luks_handlers: dict[Any, Luks2] = {} @@ -245,7 +254,7 @@ class Installer: self._mount_partition_layout(luks_handlers) def _mount_partition_layout(self, luks_handlers: dict[Any, Luks2]) -> None: - debug('Mounting partition layout') + debug("Mounting partition layout") # do not mount any PVs part of the LVM configuration pvs = [] @@ -267,7 +276,7 @@ class Installer: # partitions have to mounted in the right order on btrfs the mountpoint will # be empty as the actual subvolumes are getting mounted instead so we'll use # '/' just for sorting - sorted_part_mods = sorted(not_pv_part_mods, key=lambda x: x.mountpoint or Path('/')) + sorted_part_mods = sorted(not_pv_part_mods, key=lambda x: x.mountpoint or Path("/")) for part_mod in sorted_part_mods: if luks_handler := luks_handlers.get(part_mod): @@ -279,13 +288,13 @@ class Installer: lvm_config = self._disk_config.lvm_config if not lvm_config: - debug('No lvm config defined to be mounted') + debug("No lvm config defined to be mounted") return - debug('Mounting LVM layout') + debug("Mounting LVM layout") for vg in lvm_config.vol_groups: - sorted_vol = sorted(vg.volumes, key=lambda x: x.mountpoint or Path('/')) + sorted_vol = sorted(vg.volumes, key=lambda x: x.mountpoint or Path("/")) for vol in sorted_vol: if luks_handler := luks_handlers.get(vol): @@ -295,13 +304,13 @@ class Installer: def _prepare_luks_partitions( self, - partitions: list[PartitionModification] + partitions: list[PartitionModification], ) -> dict[PartitionModification, Luks2]: return { part_mod: device_handler.unlock_luks2_dev( part_mod.dev_path, part_mod.mapper_name, - self._disk_encryption.encryption_password + self._disk_encryption.encryption_password, ) for part_mod in partitions if part_mod.mapper_name and part_mod.dev_path @@ -311,7 +320,7 @@ class Installer: lvm_config = self._disk_config.lvm_config if not lvm_config: - debug('No lvm config defined to be imported') + debug("No lvm config defined to be imported") return for vg in lvm_config.vol_groups: @@ -322,13 +331,13 @@ class Installer: def _prepare_luks_lvm( self, - lvm_volumes: list[LvmVolume] + lvm_volumes: list[LvmVolume], ) -> dict[LvmVolume, Luks2]: return { vol: device_handler.unlock_luks2_dev( vol.dev_path, vol.mapper_name, - self._disk_encryption.encryption_password + self._disk_encryption.encryption_password, ) for vol in lvm_volumes if vol.mapper_name and vol.dev_path @@ -346,7 +355,7 @@ class Installer: self._mount_btrfs_subvol( part_mod.dev_path, part_mod.btrfs_subvols, - part_mod.mount_options + part_mod.mount_options, ) elif part_mod.is_swap(): device_handler.swapon(part_mod.dev_path) @@ -383,11 +392,11 @@ class Installer: self, dev_path: Path, subvolumes: list[SubvolumeModification], - mount_options: list[str] = [] + mount_options: list[str] = [], ) -> None: for subvol in sorted(subvolumes, key=lambda x: x.relative_mountpoint): mountpoint = self.target / subvol.relative_mountpoint - options = mount_options + [f'subvol={subvol.name}'] + options = mount_options + [f"subvol={subvol.name}"] device_handler.mount(dev_path, mountpoint, options=options) def generate_key_files(self) -> None: @@ -409,11 +418,11 @@ class Installer: luks_handler = Luks2( part_mod.safe_dev_path, mapper_name=part_mod.mapper_name, - password=self._disk_encryption.encryption_password + password=self._disk_encryption.encryption_password, ) if gen_enc_file and not part_mod.is_root(): - debug(f'Creating key-file: {part_mod.dev_path}') + debug(f"Creating key-file: {part_mod.dev_path}") luks_handler.create_keyfile(self.target) if part_mod.is_root() and not gen_enc_file: @@ -422,7 +431,7 @@ class Installer: Fido2.fido2_enroll( self._disk_encryption.hsm_device, part_mod.safe_dev_path, - self._disk_encryption.encryption_password + self._disk_encryption.encryption_password, ) def _generate_key_file_lvm_volumes(self) -> None: @@ -432,11 +441,11 @@ class Installer: luks_handler = Luks2( vol.safe_dev_path, mapper_name=vol.mapper_name, - password=self._disk_encryption.encryption_password + password=self._disk_encryption.encryption_password, ) if gen_enc_file and not vol.is_root(): - info(f'Creating key-file: {vol.dev_path}') + info(f"Creating key-file: {vol.dev_path}") luks_handler.create_keyfile(self.target) if vol.is_root() and not gen_enc_file: @@ -445,15 +454,15 @@ class Installer: Fido2.fido2_enroll( self._disk_encryption.hsm_device, vol.safe_dev_path, - self._disk_encryption.encryption_password + self._disk_encryption.encryption_password, ) def sync_log_to_install_medium(self) -> bool: # Copy over the install log (if there is one) to the install medium if # at least the base has been strapped in, otherwise we won't have a filesystem/structure to copy to. - if self._helper_flags.get('base-strapped', False) is True: - if filename := storage.get('LOG_FILE', None): - absolute_logfile = os.path.join(storage.get('LOG_PATH', './'), filename) + if self._helper_flags.get("base-strapped", False) is True: + if filename := storage.get("LOG_FILE", None): + absolute_logfile = os.path.join(storage.get("LOG_PATH", "./"), filename) if not os.path.isdir(f"{self.target}/{os.path.dirname(absolute_logfile)}"): os.makedirs(f"{self.target}/{os.path.dirname(absolute_logfile)}") @@ -462,27 +471,34 @@ class Installer: return True - def add_swapfile(self, size: str = '4G', enable_resume: bool = True, file: str = '/swapfile') -> None: - if file[:1] != '/': + def add_swapfile(self, size: str = "4G", enable_resume: bool = True, file: str = "/swapfile") -> None: + if file[:1] != "/": file = f"/{file}" - if len(file.strip()) <= 0 or file == '/': + if len(file.strip()) <= 0 or file == "/": raise ValueError(f"The filename for the swap file has to be a valid path, not: {self.target}{file}") - SysCommand(f'dd if=/dev/zero of={self.target}{file} bs={size} count=1') - SysCommand(f'chmod 0600 {self.target}{file}') - SysCommand(f'mkswap {self.target}{file}') + SysCommand(f"dd if=/dev/zero of={self.target}{file} bs={size} count=1") + SysCommand(f"chmod 0600 {self.target}{file}") + SysCommand(f"mkswap {self.target}{file}") - self._fstab_entries.append(f'{file} none swap defaults 0 0') + self._fstab_entries.append(f"{file} none swap defaults 0 0") if enable_resume: - resume_uuid = SysCommand(f'findmnt -no UUID -T {self.target}{file}').decode() - resume_offset = SysCommand( - f'filefrag -v {self.target}{file}' - ).decode().split('0:', 1)[1].split(":", 1)[1].split("..", 1)[0].strip() + resume_uuid = SysCommand(f"findmnt -no UUID -T {self.target}{file}").decode() + resume_offset = ( + SysCommand( + f"filefrag -v {self.target}{file}", + ) + .decode() + .split("0:", 1)[1] + .split(":", 1)[1] + .split("..", 1)[0] + .strip() + ) - self._hooks.append('resume') - self._kernel_params.append(f'resume=UUID={resume_uuid}') - self._kernel_params.append(f'resume_offset={resume_offset}') + self._hooks.append("resume") + self._kernel_params.append(f"resume=UUID={resume_uuid}") + self._kernel_params.append(f"resume_offset={resume_offset}") def post_install_check(self, *args: str, **kwargs: str) -> list[str]: return [step for step, flag in self._helper_flags.items() if flag is False] @@ -490,7 +506,7 @@ class Installer: def set_mirrors( self, mirror_config: MirrorConfiguration, - on_target: bool = False + on_target: bool = False, ) -> None: """ Set the mirror configuration for the installation. @@ -501,97 +517,96 @@ class Installer: :on_target: Whether to set the mirrors on the target system or the live system. :param on_target: bool """ - debug('Setting mirrors on ' + ('target' if on_target else 'live system')) + debug("Setting mirrors on " + ("target" if on_target else "live system")) for plugin in plugins.values(): - if hasattr(plugin, 'on_mirrors'): + if hasattr(plugin, "on_mirrors"): if result := plugin.on_mirrors(mirror_config): mirror_config = result - root = self.target if on_target else Path('/') - mirrorlist_config = (root / 'etc/pacman.d/mirrorlist') - pacman_config = root / 'etc/pacman.conf' + root = self.target if on_target else Path("/") + mirrorlist_config = root / "etc/pacman.d/mirrorlist" + pacman_config = root / "etc/pacman.conf" repositories_config = mirror_config.repositories_config() if repositories_config: - debug(f'Pacman config: {repositories_config}') + debug(f"Pacman config: {repositories_config}") - with open(pacman_config, 'a') as fp: + with open(pacman_config, "a") as fp: fp.write(repositories_config) regions_config = mirror_config.regions_config(speed_sort=True) if regions_config: - debug(f'Mirrorlist:\n{regions_config}') + debug(f"Mirrorlist:\n{regions_config}") mirrorlist_config.write_text(regions_config) custom_servers = mirror_config.custom_servers_config() if custom_servers: - debug(f'Custom servers:\n{custom_servers}') + debug(f"Custom servers:\n{custom_servers}") content = mirrorlist_config.read_text() - mirrorlist_config.write_text(f'{custom_servers}\n\n{content}') + mirrorlist_config.write_text(f"{custom_servers}\n\n{content}") - def genfstab(self, flags: str = '-pU') -> None: + def genfstab(self, flags: str = "-pU") -> None: fstab_path = self.target / "etc" / "fstab" info(f"Updating {fstab_path}") try: - gen_fstab = SysCommand(f'genfstab {flags} {self.target}').output() + gen_fstab = SysCommand(f"genfstab {flags} {self.target}").output() except SysCallError as err: - raise RequirementError( - f'Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}') + raise RequirementError(f"Could not generate fstab, strapping in packages most likely failed (disk out of space?)\n Error: {err}") - with open(fstab_path, 'ab') as fp: + with open(fstab_path, "ab") as fp: fp.write(gen_fstab) if not fstab_path.is_file(): - raise RequirementError('Could not create fstab file') + raise RequirementError("Could not create fstab file") for plugin in plugins.values(): - if hasattr(plugin, 'on_genfstab'): + if hasattr(plugin, "on_genfstab"): if plugin.on_genfstab(self) is True: break - with open(fstab_path, 'a') as fp: + with open(fstab_path, "a") as fp: for entry in self._fstab_entries: - fp.write(f'{entry}\n') + fp.write(f"{entry}\n") def set_hostname(self, hostname: str) -> None: - (self.target / 'etc/hostname').write_text(hostname + '\n') + (self.target / "etc/hostname").write_text(hostname + "\n") def set_locale(self, locale_config: LocaleConfiguration) -> bool: - modifier = '' + modifier = "" lang = locale_config.sys_lang encoding = locale_config.sys_enc # This is a temporary patch to fix #1200 - if '.' in locale_config.sys_lang: - lang, potential_encoding = locale_config.sys_lang.split('.', 1) + if "." in locale_config.sys_lang: + lang, potential_encoding = locale_config.sys_lang.split(".", 1) # Override encoding if encoding is set to the default parameter # and the "found" encoding differs. - if locale_config.sys_enc == 'UTF-8' and locale_config.sys_enc != potential_encoding: + if locale_config.sys_enc == "UTF-8" and locale_config.sys_enc != potential_encoding: encoding = potential_encoding # Make sure we extract the modifier, that way we can put it in if needed. - if '@' in locale_config.sys_lang: - lang, modifier = locale_config.sys_lang.split('@', 1) + if "@" in locale_config.sys_lang: + lang, modifier = locale_config.sys_lang.split("@", 1) modifier = f"@{modifier}" # - End patch - locale_gen = self.target / 'etc/locale.gen' + locale_gen = self.target / "etc/locale.gen" locale_gen_lines = locale_gen.read_text().splitlines(True) # A locale entry in /etc/locale.gen may or may not contain the encoding # in the first column of the entry; check for both cases. - entry_re = re.compile(rf'#{lang}(\.{encoding})?{modifier} {encoding}') + entry_re = re.compile(rf"#{lang}(\.{encoding})?{modifier} {encoding}") lang_value = None for index, line in enumerate(locale_gen_lines): if entry_re.match(line): - uncommented_line = line.removeprefix('#') + uncommented_line = line.removeprefix("#") locale_gen_lines[index] = uncommented_line - locale_gen.write_text(''.join(locale_gen_lines)) + locale_gen.write_text("".join(locale_gen_lines)) lang_value = uncommented_line.split()[0] break @@ -600,12 +615,12 @@ class Installer: return False try: - SysCommand(f'arch-chroot {self.target} locale-gen') + SysCommand(f"arch-chroot {self.target} locale-gen") except SysCallError as e: - error(f'Failed to run locale-gen on target: {e}') + error(f"Failed to run locale-gen on target: {e}") return False - (self.target / 'etc/locale.conf').write_text(f'LANG={lang_value}\n') + (self.target / "etc/locale.conf").write_text(f"LANG={lang_value}\n") return True def set_timezone(self, zone: str) -> bool: @@ -615,27 +630,27 @@ class Installer: return True # Redundant for plugin in plugins.values(): - if hasattr(plugin, 'on_timezone'): + if hasattr(plugin, "on_timezone"): if result := plugin.on_timezone(zone): zone = result if (Path("/usr") / "share" / "zoneinfo" / zone).exists(): (Path(self.target) / "etc" / "localtime").unlink(missing_ok=True) - SysCommand(f'arch-chroot {self.target} ln -s /usr/share/zoneinfo/{zone} /etc/localtime') + SysCommand(f"arch-chroot {self.target} ln -s /usr/share/zoneinfo/{zone} /etc/localtime") return True else: - warn(f'Time zone {zone} does not exist, continuing with system default') + warn(f"Time zone {zone} does not exist, continuing with system default") return False def activate_time_synchronization(self) -> None: - info('Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers') - self.enable_service('systemd-timesyncd') + info("Activating systemd-timesyncd for time synchronization using Arch Linux and ntp.org NTP servers") + self.enable_service("systemd-timesyncd") def enable_espeakup(self) -> None: - info('Enabling espeakup.service for speech synthesis (accessibility)') - self.enable_service('espeakup') + info("Enabling espeakup.service for speech synthesis (accessibility)") + self.enable_service("espeakup") def enable_periodic_trim(self) -> None: info("Enabling periodic TRIM") @@ -647,19 +662,19 @@ class Installer: services = [services] for service in services: - info(f'Enabling service {service}') + info(f"Enabling service {service}") try: - self.arch_chroot(f'systemctl enable {service}') + self.arch_chroot(f"systemctl enable {service}") except SysCallError as err: raise ServiceException(f"Unable to start service {service}: {err}") for plugin in plugins.values(): - if hasattr(plugin, 'on_service'): + if hasattr(plugin, "on_service"): plugin.on_service(service) def run_command(self, cmd: str, *args: str, **kwargs: str) -> SysCommand: - return SysCommand(f'arch-chroot {self.target} {cmd}') + return SysCommand(f"arch-chroot {self.target} {cmd}") def arch_chroot(self, cmd: str, run_as: str | None = None) -> SysCommand: if run_as: @@ -674,48 +689,51 @@ class Installer: conf = nic.as_systemd_config() for plugin in plugins.values(): - if hasattr(plugin, 'on_configure_nic'): - conf = plugin.on_configure_nic( - nic.iface, - nic.dhcp, - nic.ip, - nic.gateway, - nic.dns - ) or conf + if hasattr(plugin, "on_configure_nic"): + conf = ( + plugin.on_configure_nic( + nic.iface, + nic.dhcp, + nic.ip, + nic.gateway, + nic.dns, + ) + or conf + ) with open(f"{self.target}/etc/systemd/network/10-{nic.iface}.network", "a") as netconf: netconf.write(str(conf)) def copy_iso_network_config(self, enable_services: bool = False) -> bool: # Copy (if any) iwd password and config files - if os.path.isdir('/var/lib/iwd/'): - if psk_files := glob.glob('/var/lib/iwd/*.psk'): + if os.path.isdir("/var/lib/iwd/"): + if psk_files := glob.glob("/var/lib/iwd/*.psk"): if not os.path.isdir(f"{self.target}/var/lib/iwd"): os.makedirs(f"{self.target}/var/lib/iwd") if enable_services: # If we haven't installed the base yet (function called pre-maturely) - if self._helper_flags.get('base', False) is False: - self._base_packages.append('iwd') + if self._helper_flags.get("base", False) is False: + self._base_packages.append("iwd") # This function will be called after minimal_installation() # as a hook for post-installs. This hook is only needed if # base is not installed yet. def post_install_enable_iwd_service(*args: str, **kwargs: str) -> None: - self.enable_service('iwd') + self.enable_service("iwd") self.post_base_install.append(post_install_enable_iwd_service) # Otherwise, we can go ahead and add the required package # and enable it's service: else: - self.pacman.strap('iwd') - self.enable_service('iwd') + self.pacman.strap("iwd") + self.enable_service("iwd") for psk in psk_files: shutil.copy2(psk, f"{self.target}/var/lib/iwd/{os.path.basename(psk)}") # Copy (if any) systemd-networkd config files - if netconfigurations := glob.glob('/etc/systemd/network/*'): + if netconfigurations := glob.glob("/etc/systemd/network/*"): if not os.path.isdir(f"{self.target}/etc/systemd/network/"): os.makedirs(f"{self.target}/etc/systemd/network/") @@ -724,26 +742,26 @@ class Installer: if enable_services: # If we haven't installed the base yet (function called pre-maturely) - if self._helper_flags.get('base', False) is False: + if self._helper_flags.get("base", False) is False: def post_install_enable_networkd_resolved(*args: str, **kwargs: str) -> None: - self.enable_service(['systemd-networkd', 'systemd-resolved']) + self.enable_service(["systemd-networkd", "systemd-resolved"]) self.post_base_install.append(post_install_enable_networkd_resolved) # Otherwise, we can go ahead and enable the services else: - self.enable_service(['systemd-networkd', 'systemd-resolved']) + self.enable_service(["systemd-networkd", "systemd-resolved"]) return True def mkinitcpio(self, flags: list[str]) -> bool: for plugin in plugins.values(): - if hasattr(plugin, 'on_mkinitcpio'): + if hasattr(plugin, "on_mkinitcpio"): # Allow plugins to override the usage of mkinitcpio altogether. if plugin.on_mkinitcpio(self): return True - with open(f'{self.target}/etc/mkinitcpio.conf', 'r+') as mkinit: + with open(f"{self.target}/etc/mkinitcpio.conf", "r+") as mkinit: content = mkinit.read() content = re.sub("\nMODULES=(.*)", f"\nMODULES=({' '.join(self._modules)})", content) content = re.sub("\nBINARIES=(.*)", f"\nBINARIES=({' '.join(self._binaries)})", content) @@ -755,14 +773,14 @@ class Installer: # This is purely for stability reasons, we're going away from this. # * systemd -> udev # * sd-vconsole -> keymap - self._hooks = [hook.replace('systemd', 'udev').replace('sd-vconsole', 'keymap consolefont') for hook in self._hooks] + self._hooks = [hook.replace("systemd", "udev").replace("sd-vconsole", "keymap consolefont") for hook in self._hooks] content = re.sub("\nHOOKS=(.*)", f"\nHOOKS=({' '.join(self._hooks)})", content) mkinit.seek(0) mkinit.write(content) try: - SysCommand(f'arch-chroot {self.target} mkinitcpio {" ".join(flags)}', peek_output=True) + SysCommand(f"arch-chroot {self.target} mkinitcpio {' '.join(flags)}", peek_output=True) return True except SysCallError as e: if e.worker_log: @@ -778,7 +796,7 @@ class Installer: def _prepare_fs_type( self, fs_type: FilesystemType, - mountpoint: Path | None + mountpoint: Path | None, ) -> None: if (pkg := fs_type.installation_pkg) is not None: self._base_packages.append(pkg) @@ -788,36 +806,36 @@ class Installer: self._binaries.append(binary) # https://github.com/archlinux/archinstall/issues/1837 - if fs_type.fs_type_mount == 'btrfs': + if fs_type.fs_type_mount == "btrfs": self._disable_fstrim = True # There is not yet an fsck tool for NTFS. If it's being used for the root filesystem, the hook should be removed. - if fs_type.fs_type_mount == 'ntfs3' and mountpoint == self.target: - if 'fsck' in self._hooks: - self._hooks.remove('fsck') + if fs_type.fs_type_mount == "ntfs3" and mountpoint == self.target: + if "fsck" in self._hooks: + self._hooks.remove("fsck") - def _prepare_encrypt(self, before: str = 'filesystems') -> None: + def _prepare_encrypt(self, before: str = "filesystems") -> None: if self._disk_encryption.hsm_device: # Required by mkinitcpio to add support for fido2-device options - self.pacman.strap('libfido2') + self.pacman.strap("libfido2") - if 'sd-encrypt' not in self._hooks: - self._hooks.insert(self._hooks.index(before), 'sd-encrypt') + if "sd-encrypt" not in self._hooks: + self._hooks.insert(self._hooks.index(before), "sd-encrypt") else: - if 'encrypt' not in self._hooks: - self._hooks.insert(self._hooks.index(before), 'encrypt') + if "encrypt" not in self._hooks: + self._hooks.insert(self._hooks.index(before), "encrypt") def minimal_installation( self, optional_repositories: list[Repository] = [], mkinitcpio: bool = True, hostname: str | None = None, - locale_config: LocaleConfiguration | None = LocaleConfiguration.default() + locale_config: LocaleConfiguration | None = LocaleConfiguration.default(), ): if self._disk_config.lvm_config: - lvm = 'lvm2' + lvm = "lvm2" self.add_additional_packages(lvm) - self._hooks.insert(self._hooks.index('filesystems') - 1, lvm) + self._hooks.insert(self._hooks.index("filesystems") - 1, lvm) for vg in self._disk_config.lvm_config.vol_groups: for vol in vg.volumes: @@ -839,12 +857,12 @@ class Installer: self._prepare_encrypt() if ucode := self._get_microcode(): - (self.target / 'boot' / ucode).unlink(missing_ok=True) + (self.target / "boot" / ucode).unlink(missing_ok=True) self._base_packages.append(ucode.stem) else: - debug('Archinstall will not install any ucode.') + debug("Archinstall will not install any ucode.") - debug(f'Optional repositories: {optional_repositories}') + debug(f"Optional repositories: {optional_repositories}") # This action takes place on the host system as pacstrap copies over package repository lists. pacman_conf = PacmanConfig(self.target) @@ -852,7 +870,7 @@ class Installer: pacman_conf.apply() self.pacman.strap(self._base_packages) - self._helper_flags['base-strapped'] = True + self._helper_flags["base-strapped"] = True pacman_conf.persist() @@ -878,12 +896,12 @@ class Installer: self.set_keyboard_language(locale_config.kb_layout) # TODO: Use python functions for this - SysCommand(f'arch-chroot {self.target} chmod 700 /root') + SysCommand(f"arch-chroot {self.target} chmod 700 /root") - if mkinitcpio and not self.mkinitcpio(['-P']): - error('Error generating initramfs (continuing anyway)') + if mkinitcpio and not self.mkinitcpio(["-P"]): + error("Error generating initramfs (continuing anyway)") - self._helper_flags['base'] = True + self._helper_flags["base"] = True # Run registered post-install hooks for function in self.post_base_install: @@ -891,13 +909,13 @@ class Installer: function(self) for plugin in plugins.values(): - if hasattr(plugin, 'on_install'): + if hasattr(plugin, "on_install"): plugin.on_install(self) - def setup_swap(self, kind: str = 'zram') -> None: - if kind == 'zram': + def setup_swap(self, kind: str = "zram") -> None: + if kind == "zram": info("Setting up swap on zram") - self.pacman.strap('zram-generator') + self.pacman.strap("zram-generator") # We could use the default example below, but maybe not the best idea: https://github.com/archlinux/archinstall/pull/678#issuecomment-962124813 # zram_example_location = '/usr/share/doc/zram-generator/zram-generator.conf.example' @@ -905,7 +923,7 @@ class Installer: with open(f"{self.target}/etc/systemd/zram-generator.conf", "w") as zram_conf: zram_conf.write("[zram0]\n") - self.enable_service('systemd-zram-setup@zram0.service') + self.enable_service("systemd-zram-setup@zram0.service") self._zram_enabled = True else: @@ -936,7 +954,7 @@ class Installer: lsblk_info = get_lsblk_info(mapper_dev_path, reverse=True, full_dev_path=True) if not lsblk_info.children or not lsblk_info.children[0].uuid: - raise ValueError('Unable to determine UUID of luks superblock') + raise ValueError("Unable to determine UUID of luks superblock") return lsblk_info.children[0].uuid @@ -944,7 +962,7 @@ class Installer: self, root_partition: PartitionModification, id_root: bool = True, - partuuid: bool = True + partuuid: bool = True, ) -> list[str]: kernel_parameters = [] @@ -953,67 +971,67 @@ class Installer: # or simply a partition encryption. Right now we assume it's a partition (and we always have) if self._disk_encryption and self._disk_encryption.hsm_device: - debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}') + debug(f"Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}") # Note: UUID must be used, not PARTUUID for sd-encrypt to work - kernel_parameters.append(f'rd.luks.name={root_partition.uuid}=root') + kernel_parameters.append(f"rd.luks.name={root_partition.uuid}=root") # Note: tpm2-device and fido2-device don't play along very well: # https://github.com/archlinux/archinstall/pull/1196#issuecomment-1129715645 - kernel_parameters.append('rd.luks.options=fido2-device=auto,password-echo=no') + kernel_parameters.append("rd.luks.options=fido2-device=auto,password-echo=no") elif partuuid: - debug(f'Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'cryptdevice=PARTUUID={root_partition.partuuid}:root') + debug(f"Root partition is an encrypted device, identifying by PARTUUID: {root_partition.partuuid}") + kernel_parameters.append(f"cryptdevice=PARTUUID={root_partition.partuuid}:root") else: - debug(f'Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}') - kernel_parameters.append(f'cryptdevice=UUID={root_partition.uuid}:root') + debug(f"Root partition is an encrypted device, identifying by UUID: {root_partition.uuid}") + kernel_parameters.append(f"cryptdevice=UUID={root_partition.uuid}:root") if id_root: - kernel_parameters.append('root=/dev/mapper/root') + kernel_parameters.append("root=/dev/mapper/root") elif id_root: if partuuid: - debug(f'Identifying root partition by PARTUUID: {root_partition.partuuid}') - kernel_parameters.append(f'root=PARTUUID={root_partition.partuuid}') + debug(f"Identifying root partition by PARTUUID: {root_partition.partuuid}") + kernel_parameters.append(f"root=PARTUUID={root_partition.partuuid}") else: - debug(f'Identifying root partition by UUID: {root_partition.uuid}') - kernel_parameters.append(f'root=UUID={root_partition.uuid}') + debug(f"Identifying root partition by UUID: {root_partition.uuid}") + kernel_parameters.append(f"root=UUID={root_partition.uuid}") return kernel_parameters def _get_kernel_params_lvm( self, - lvm: LvmVolume + lvm: LvmVolume, ) -> list[str]: kernel_parameters = [] match self._disk_encryption.encryption_type: case EncryptionType.LvmOnLuks: if not lvm.vg_name: - raise ValueError(f'Unable to determine VG name for {lvm.name}') + raise ValueError(f"Unable to determine VG name for {lvm.name}") pv_seg_info = device_handler.lvm_pvseg_info(lvm.vg_name, lvm.name) if not pv_seg_info: - raise ValueError(f'Unable to determine PV segment info for {lvm.vg_name}/{lvm.name}') + raise ValueError(f"Unable to determine PV segment info for {lvm.vg_name}/{lvm.name}") uuid = self._get_luks_uuid_from_mapper_dev(pv_seg_info.pv_name) if self._disk_encryption.hsm_device: - debug(f'LvmOnLuks, encrypted root partition, HSM, identifying by UUID: {uuid}') - kernel_parameters.append(f'rd.luks.name={uuid}=cryptlvm root={lvm.safe_dev_path}') + debug(f"LvmOnLuks, encrypted root partition, HSM, identifying by UUID: {uuid}") + kernel_parameters.append(f"rd.luks.name={uuid}=cryptlvm root={lvm.safe_dev_path}") else: - debug(f'LvmOnLuks, encrypted root partition, identifying by UUID: {uuid}') - kernel_parameters.append(f'cryptdevice=UUID={uuid}:cryptlvm root={lvm.safe_dev_path}') + debug(f"LvmOnLuks, encrypted root partition, identifying by UUID: {uuid}") + kernel_parameters.append(f"cryptdevice=UUID={uuid}:cryptlvm root={lvm.safe_dev_path}") case EncryptionType.LuksOnLvm: uuid = self._get_luks_uuid_from_mapper_dev(lvm.mapper_path) if self._disk_encryption.hsm_device: - debug(f'LuksOnLvm, encrypted root partition, HSM, identifying by UUID: {uuid}') - kernel_parameters.append(f'rd.luks.name={uuid}=root root=/dev/mapper/root') + debug(f"LuksOnLvm, encrypted root partition, HSM, identifying by UUID: {uuid}") + kernel_parameters.append(f"rd.luks.name={uuid}=root root=/dev/mapper/root") else: - debug(f'LuksOnLvm, encrypted root partition, identifying by UUID: {uuid}') - kernel_parameters.append(f'cryptdevice=UUID={uuid}:root root=/dev/mapper/root') + debug(f"LuksOnLvm, encrypted root partition, identifying by UUID: {uuid}") + kernel_parameters.append(f"cryptdevice=UUID={uuid}:root root=/dev/mapper/root") case EncryptionType.NoEncryption: - debug(f'Identifying root lvm by mapper device: {lvm.dev_path}') - kernel_parameters.append(f'root={lvm.safe_dev_path}') + debug(f"Identifying root lvm by mapper device: {lvm.dev_path}") + kernel_parameters.append(f"root={lvm.safe_dev_path}") return kernel_parameters @@ -1021,7 +1039,7 @@ class Installer: self, root: PartitionModification | LvmVolume, id_root: bool = True, - partuuid: bool = True + partuuid: bool = True, ) -> list[str]: kernel_parameters = [] @@ -1033,20 +1051,20 @@ class Installer: # Zswap should be disabled when using zram. # https://github.com/archlinux/archinstall/issues/881 if self._zram_enabled: - kernel_parameters.append('zswap.enabled=0') + kernel_parameters.append("zswap.enabled=0") if id_root: for sub_vol in root.btrfs_subvols: if sub_vol.is_root(): - kernel_parameters.append(f'rootflags=subvol={sub_vol.name}') + kernel_parameters.append(f"rootflags=subvol={sub_vol.name}") break - kernel_parameters.append('rw') + kernel_parameters.append("rw") - kernel_parameters.append(f'rootfstype={root.safe_fs_type.fs_type_mount}') + kernel_parameters.append(f"rootfstype={root.safe_fs_type.fs_type_mount}") kernel_parameters.extend(self._kernel_params) - debug(f'kernel parameters: {" ".join(kernel_parameters)}') + debug(f"kernel parameters: {' '.join(kernel_parameters)}") return kernel_parameters @@ -1054,11 +1072,11 @@ class Installer: self, boot_partition: PartitionModification, root: PartitionModification | LvmVolume, - entry_name: str + entry_name: str, ) -> None: # Loader entries are stored in $BOOT/loader: # https://uapi-group.org/specifications/specs/boot_loader_specification/#mount-points - entries_dir = self.target / boot_partition.relative_mountpoint / 'loader/entries' + entries_dir = self.target / boot_partition.relative_mountpoint / "loader/entries" # Ensure that the $BOOT/loader/entries/ directory exists before trying to create files in it entries_dir.mkdir(parents=True, exist_ok=True) @@ -1070,7 +1088,7 @@ class Installer: linux /vmlinuz-{{kernel}} initrd /initramfs-{{kernel}}{{variant}}.img options {" ".join(self._get_kernel_params(root))} - """ + """, ) for kernel in self.kernels: @@ -1085,19 +1103,19 @@ class Installer: boot_partition: PartitionModification, root: PartitionModification | LvmVolume, efi_partition: PartitionModification | None, - uki_enabled: bool = False + uki_enabled: bool = False, ) -> None: - debug('Installing systemd bootloader') + debug("Installing systemd bootloader") - self.pacman.strap('efibootmgr') + self.pacman.strap("efibootmgr") if not SysInfo.has_uefi(): raise HardwareIncompatibilityError if not efi_partition: - raise ValueError('Could not detect EFI system partition') + raise ValueError("Could not detect EFI system partition") elif not efi_partition.mountpoint: - raise ValueError('EFI system partition is not mounted') + raise ValueError("EFI system partition is not mounted") # TODO: Ideally we would want to check if another config # points towards the same disk and/or partition. @@ -1105,8 +1123,8 @@ class Installer: bootctl_options = [] if boot_partition != efi_partition: - bootctl_options.append(f'--esp-path={efi_partition.mountpoint}') - bootctl_options.append(f'--boot-path={boot_partition.mountpoint}') + bootctl_options.append(f"--esp-path={efi_partition.mountpoint}") + bootctl_options.append(f"--boot-path={boot_partition.mountpoint}") # Install the boot loader try: @@ -1117,19 +1135,19 @@ class Installer: # Loader configuration is stored in ESP/loader: # https://man.archlinux.org/man/loader.conf.5 - loader_conf = self.target / efi_partition.relative_mountpoint / 'loader/loader.conf' + loader_conf = self.target / efi_partition.relative_mountpoint / "loader/loader.conf" # Ensure that the ESP/loader/ directory exists before trying to create a file in it loader_conf.parent.mkdir(parents=True, exist_ok=True) default_kernel = self.kernels[0] if uki_enabled: - default_entry = f'arch-{default_kernel}.efi' + default_entry = f"arch-{default_kernel}.efi" else: - entry_name = self.init_time + '_{kernel}{variant}.conf' - default_entry = entry_name.format(kernel=default_kernel, variant='') + entry_name = self.init_time + "_{kernel}{variant}.conf" + default_entry = entry_name.format(kernel=default_kernel, variant="") self._create_bls_entries(boot_partition, root, entry_name) - default = f'default {default_entry}' + default = f"default {default_entry}" # Modify or create a loader.conf try: @@ -1137,68 +1155,68 @@ class Installer: except FileNotFoundError: loader_data = [ default, - 'timeout 15' + "timeout 15", ] else: for index, line in enumerate(loader_data): - if line.startswith('default'): + if line.startswith("default"): loader_data[index] = default - elif line.startswith('#timeout'): + elif line.startswith("#timeout"): # We add in the default timeout to support dual-boot - loader_data[index] = line.removeprefix('#') + loader_data[index] = line.removeprefix("#") - loader_conf.write_text('\n'.join(loader_data) + '\n') + loader_conf.write_text("\n".join(loader_data) + "\n") - self._helper_flags['bootloader'] = 'systemd' + self._helper_flags["bootloader"] = "systemd" def _add_grub_bootloader( self, boot_partition: PartitionModification, root: PartitionModification | LvmVolume, - efi_partition: PartitionModification | None + efi_partition: PartitionModification | None, ) -> None: - debug('Installing grub bootloader') + debug("Installing grub bootloader") - self.pacman.strap('grub') + self.pacman.strap("grub") - grub_default = self.target / 'etc/default/grub' + grub_default = self.target / "etc/default/grub" config = grub_default.read_text() - kernel_parameters = ' '.join(self._get_kernel_params(root, False, False)) - config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf'\1{kernel_parameters}\2', config, count=1) + kernel_parameters = " ".join(self._get_kernel_params(root, False, False)) + config = re.sub(r'(GRUB_CMDLINE_LINUX=")("\n)', rf"\1{kernel_parameters}\2", config, count=1) grub_default.write_text(config) info(f"GRUB boot partition: {boot_partition.dev_path}") - boot_dir = Path('/boot') + boot_dir = Path("/boot") command = [ - 'arch-chroot', + "arch-chroot", str(self.target), - 'grub-install', - '--debug' + "grub-install", + "--debug", ] if SysInfo.has_uefi(): if not efi_partition: - raise ValueError('Could not detect efi partition') + raise ValueError("Could not detect efi partition") info(f"GRUB EFI partition: {efi_partition.dev_path}") - self.pacman.strap('efibootmgr') # TODO: Do we need? Yes, but remove from minimal_installation() instead? + self.pacman.strap("efibootmgr") # TODO: Do we need? Yes, but remove from minimal_installation() instead? boot_dir_arg = [] if boot_partition.mountpoint and boot_partition.mountpoint != boot_dir: - boot_dir_arg.append(f'--boot-directory={boot_partition.mountpoint}') + boot_dir_arg.append(f"--boot-directory={boot_partition.mountpoint}") boot_dir = boot_partition.mountpoint add_options = [ - f'--target={platform.machine()}-efi', - f'--efi-directory={efi_partition.mountpoint}', + f"--target={platform.machine()}-efi", + f"--efi-directory={efi_partition.mountpoint}", *boot_dir_arg, - '--bootloader-id=GRUB', - '--removable' + "--bootloader-id=GRUB", + "--removable", ] command.extend(add_options) @@ -1216,9 +1234,9 @@ class Installer: parent_dev_path = device_handler.get_parent_device_path(boot_partition.safe_dev_path) add_options = [ - '--target=i386-pc', - '--recheck', - str(parent_dev_path) + "--target=i386-pc", + "--recheck", + str(parent_dev_path), ] try: @@ -1228,103 +1246,104 @@ class Installer: try: SysCommand( - f'arch-chroot {self.target} ' - f'grub-mkconfig -o {boot_dir}/grub/grub.cfg' + f"arch-chroot {self.target} grub-mkconfig -o {boot_dir}/grub/grub.cfg", ) except SysCallError as err: raise DiskError(f"Could not configure GRUB: {err}") - self._helper_flags['bootloader'] = "grub" + self._helper_flags["bootloader"] = "grub" def _add_limine_bootloader( self, boot_partition: PartitionModification, efi_partition: PartitionModification | None, root: PartitionModification | LvmVolume, - uki_enabled: bool = False + uki_enabled: bool = False, ) -> None: - debug('Installing Limine bootloader') + debug("Installing Limine bootloader") - self.pacman.strap('limine') + self.pacman.strap("limine") info(f"Limine boot partition: {boot_partition.dev_path}") - limine_path = self.target / 'usr' / 'share' / 'limine' + limine_path = self.target / "usr" / "share" / "limine" config_path = None hook_command = None if SysInfo.has_uefi(): - self.pacman.strap('efibootmgr') + self.pacman.strap("efibootmgr") if not efi_partition: - raise ValueError('Could not detect efi partition') + raise ValueError("Could not detect efi partition") elif not efi_partition.mountpoint: - raise ValueError('EFI partition is not mounted') + raise ValueError("EFI partition is not mounted") info(f"Limine EFI partition: {efi_partition.dev_path}") parent_dev_path = device_handler.get_parent_device_path(efi_partition.safe_dev_path) - is_target_usb = SysCommand( - f'udevadm info --no-pager --query=property --property=ID_BUS --value --name={parent_dev_path}' - ).decode() == 'usb' + is_target_usb = ( + SysCommand( + f"udevadm info --no-pager --query=property --property=ID_BUS --value --name={parent_dev_path}", + ).decode() + == "usb" + ) try: - efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI' - efi_dir_path_target = efi_partition.mountpoint / 'EFI' + efi_dir_path = self.target / efi_partition.mountpoint.relative_to("/") / "EFI" + efi_dir_path_target = efi_partition.mountpoint / "EFI" if is_target_usb: - efi_dir_path = efi_dir_path / 'BOOT' - efi_dir_path_target = efi_dir_path_target / 'BOOT' + efi_dir_path = efi_dir_path / "BOOT" + efi_dir_path_target = efi_dir_path_target / "BOOT" else: - efi_dir_path = efi_dir_path / 'limine' - efi_dir_path_target = efi_dir_path_target / 'limine' + efi_dir_path = efi_dir_path / "limine" + efi_dir_path_target = efi_dir_path_target / "limine" efi_dir_path.mkdir(parents=True, exist_ok=True) - for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'): + for file in ("BOOTIA32.EFI", "BOOTX64.EFI"): shutil.copy(limine_path / file, efi_dir_path) except Exception as err: - raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}') + raise DiskError(f"Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}") - config_path = efi_dir_path / 'limine.conf' + config_path = efi_dir_path / "limine.conf" hook_command = ( - f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_dir_path_target}/' - f' && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_dir_path_target}/' + f"/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_dir_path_target}/ && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_dir_path_target}/" ) if not is_target_usb: # Create EFI boot menu entry for Limine. try: - with open('/sys/firmware/efi/fw_platform_size') as fw_platform_size: + with open("/sys/firmware/efi/fw_platform_size") as fw_platform_size: efi_bitness = fw_platform_size.read().strip() except Exception as err: - raise OSError(f'Could not open or read /sys/firmware/efi/fw_platform_size to determine EFI bitness: {err}') + raise OSError(f"Could not open or read /sys/firmware/efi/fw_platform_size to determine EFI bitness: {err}") - if efi_bitness == '64': - loader_path = '/EFI/limine/BOOTX64.EFI' - elif efi_bitness == '32': - loader_path = '/EFI/limine/BOOTIA32.EFI' + if efi_bitness == "64": + loader_path = "/EFI/limine/BOOTX64.EFI" + elif efi_bitness == "32": + loader_path = "/EFI/limine/BOOTIA32.EFI" else: raise ValueError(f'EFI bitness is neither 32 nor 64 bits. Found "{efi_bitness}".') try: SysCommand( - 'efibootmgr' - ' --create' - f' --disk {parent_dev_path}' - f' --part {efi_partition.partn}' + "efibootmgr" + " --create" + f" --disk {parent_dev_path}" + f" --part {efi_partition.partn}" ' --label "Arch Linux Limine Bootloader"' - f' --loader {loader_path}' - ' --unicode' - ' --verbose' + f" --loader {loader_path}" + " --unicode" + " --verbose", ) except Exception as err: - raise ValueError(f'SysCommand for efibootmgr failed: {err}') + raise ValueError(f"SysCommand for efibootmgr failed: {err}") else: - boot_limine_path = self.target / 'boot' / 'limine' + boot_limine_path = self.target / "boot" / "limine" boot_limine_path.mkdir(parents=True, exist_ok=True) - config_path = boot_limine_path / 'limine.conf' + config_path = boot_limine_path / "limine.conf" parent_dev_path = device_handler.get_parent_device_path(boot_partition.safe_dev_path) @@ -1333,17 +1352,14 @@ class Installer: try: # The `limine-bios.sys` file contains stage 3 code. - shutil.copy(limine_path / 'limine-bios.sys', boot_limine_path) + shutil.copy(limine_path / "limine-bios.sys", boot_limine_path) # `limine bios-install` deploys the stage 1 and 2 to the - SysCommand(f'arch-chroot {self.target} limine bios-install {parent_dev_path}', peek_output=True) + SysCommand(f"arch-chroot {self.target} limine bios-install {parent_dev_path}", peek_output=True) except Exception as err: - raise DiskError(f'Failed to install Limine on {parent_dev_path}: {err}') + raise DiskError(f"Failed to install Limine on {parent_dev_path}: {err}") - hook_command = ( - f'/usr/bin/limine bios-install {parent_dev_path}' - f' && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/limine/' - ) + hook_command = f"/usr/bin/limine bios-install {parent_dev_path} && /usr/bin/cp /usr/share/limine/limine-bios.sys /boot/limine/" hook_contents = textwrap.dedent( f'''\ @@ -1357,54 +1373,54 @@ class Installer: Description = Deploying Limine after upgrade... When = PostTransaction Exec = /bin/sh -c "{hook_command}" - ''' + ''', ) - hooks_dir = self.target / 'etc' / 'pacman.d' / 'hooks' + hooks_dir = self.target / "etc" / "pacman.d" / "hooks" hooks_dir.mkdir(parents=True, exist_ok=True) - hook_path = hooks_dir / '99-limine.hook' + hook_path = hooks_dir / "99-limine.hook" hook_path.write_text(hook_contents) - kernel_params = ' '.join(self._get_kernel_params(root)) - config_contents = 'timeout: 5\n' + kernel_params = " ".join(self._get_kernel_params(root)) + config_contents = "timeout: 5\n" - path_root = 'boot()' + path_root = "boot()" if efi_partition and boot_partition != efi_partition: - path_root = f'uuid({boot_partition.partuuid})' + path_root = f"uuid({boot_partition.partuuid})" for kernel in self.kernels: - for variant in ('', '-fallback'): + for variant in ("", "-fallback"): if uki_enabled: entry = [ - 'protocol: efi', - f'path: boot():/EFI/Linux/arch-{kernel}.efi', - f'cmdline: {kernel_params}', + "protocol: efi", + f"path: boot():/EFI/Linux/arch-{kernel}.efi", + f"cmdline: {kernel_params}", ] else: entry = [ - 'protocol: linux', - f'path: {path_root}:/vmlinuz-{kernel}', - f'cmdline: {kernel_params}', - f'module_path: {path_root}:/initramfs-{kernel}{variant}.img', + "protocol: linux", + f"path: {path_root}:/vmlinuz-{kernel}", + f"cmdline: {kernel_params}", + f"module_path: {path_root}:/initramfs-{kernel}{variant}.img", ] - config_contents += f'\n/Arch Linux ({kernel}{variant})\n' - config_contents += '\n'.join([f' {it}' for it in entry]) + '\n' + config_contents += f"\n/Arch Linux ({kernel}{variant})\n" + config_contents += "\n".join([f" {it}" for it in entry]) + "\n" config_path.write_text(config_contents) - self._helper_flags['bootloader'] = "limine" + self._helper_flags["bootloader"] = "limine" def _add_efistub_bootloader( self, boot_partition: PartitionModification, root: PartitionModification | LvmVolume, - uki_enabled: bool = False + uki_enabled: bool = False, ) -> None: - debug('Installing efistub bootloader') + debug("Installing efistub bootloader") - self.pacman.strap('efibootmgr') + self.pacman.strap("efibootmgr") if not SysInfo.has_uefi(): raise HardwareIncompatibilityError @@ -1414,29 +1430,34 @@ class Installer: # And in which case we should do some clean up. if not uki_enabled: - loader = '/vmlinuz-{kernel}' + loader = "/vmlinuz-{kernel}" entries = ( - 'initrd=/initramfs-{kernel}.img', - *self._get_kernel_params(root) + "initrd=/initramfs-{kernel}.img", + *self._get_kernel_params(root), ) - cmdline = [' '.join(entries)] + cmdline = [" ".join(entries)] else: - loader = '/EFI/Linux/arch-{kernel}.efi' + loader = "/EFI/Linux/arch-{kernel}.efi" cmdline = [] parent_dev_path = device_handler.get_parent_device_path(boot_partition.safe_dev_path) cmd_template = ( - 'efibootmgr', - '--create', - '--disk', str(parent_dev_path), - '--part', str(boot_partition.partn), - '--label', 'Arch Linux ({kernel})', - '--loader', loader, - '--unicode', *cmdline, - '--verbose' + "efibootmgr", + "--create", + "--disk", + str(parent_dev_path), + "--part", + str(boot_partition.partn), + "--label", + "Arch Linux ({kernel})", + "--loader", + loader, + "--unicode", + *cmdline, + "--verbose", ) for kernel in self.kernels: @@ -1444,24 +1465,24 @@ class Installer: cmd = [arg.format(kernel=kernel) for arg in cmd_template] SysCommand(cmd) - self._helper_flags['bootloader'] = "efistub" + self._helper_flags["bootloader"] = "efistub" def _config_uki( self, root: PartitionModification | LvmVolume, - efi_partition: PartitionModification | None + efi_partition: PartitionModification | None, ) -> None: if not efi_partition or not efi_partition.mountpoint: - raise ValueError(f'Could not detect ESP at mountpoint {self.target}') + raise ValueError(f"Could not detect ESP at mountpoint {self.target}") # Set up kernel command line - with open(self.target / 'etc/kernel/cmdline', 'w') as cmdline: + with open(self.target / "etc/kernel/cmdline", "w") as cmdline: kernel_parameters = self._get_kernel_params(root) - cmdline.write(' '.join(kernel_parameters) + '\n') + cmdline.write(" ".join(kernel_parameters) + "\n") diff_mountpoint = None - if efi_partition.mountpoint != Path('/efi'): + if efi_partition.mountpoint != Path("/efi"): diff_mountpoint = str(efi_partition.mountpoint) image_re = re.compile('(.+_image="/([^"]+).+\n)') @@ -1469,7 +1490,7 @@ class Installer: # Modify .preset files for kernel in self.kernels: - preset = self.target / 'etc/mkinitcpio.d' / (kernel + '.preset') + preset = self.target / "etc/mkinitcpio.d" / (kernel + ".preset") config = preset.read_text().splitlines(True) for index, line in enumerate(config): @@ -1477,24 +1498,24 @@ class Installer: if m := image_re.match(line): image = self.target / m.group(2) image.unlink(missing_ok=True) - config[index] = '#' + m.group(1) + config[index] = "#" + m.group(1) elif m := uki_re.match(line): if diff_mountpoint: config[index] = m.group(2) + diff_mountpoint + m.group(3) else: config[index] = m.group(1) - elif line.startswith('#default_options='): - config[index] = line.removeprefix('#') + elif line.startswith("#default_options="): + config[index] = line.removeprefix("#") - preset.write_text(''.join(config)) + preset.write_text("".join(config)) # Directory for the UKIs - uki_dir = self.target / efi_partition.relative_mountpoint / 'EFI/Linux' + uki_dir = self.target / efi_partition.relative_mountpoint / "EFI/Linux" uki_dir.mkdir(parents=True, exist_ok=True) # Build the UKIs - if not self.mkinitcpio(['-P']): - error('Error generating initramfs (continuing anyway)') + if not self.mkinitcpio(["-P"]): + error("Error generating initramfs (continuing anyway)") def add_bootloader(self, bootloader: Bootloader, uki_enabled: bool = False): """ @@ -1509,7 +1530,7 @@ class Installer: """ for plugin in plugins.values(): - if hasattr(plugin, 'on_add_bootloader'): + if hasattr(plugin, "on_add_bootloader"): # Allow plugins to override the boot-loader handling. # This allows for bot configuring and installing bootloaders. if plugin.on_add_bootloader(self): @@ -1520,12 +1541,12 @@ class Installer: root = self._get_root() if boot_partition is None: - raise ValueError(f'Could not detect boot at mountpoint {self.target}') + raise ValueError(f"Could not detect boot at mountpoint {self.target}") if root is None: - raise ValueError(f'Could not detect root at mountpoint {self.target}') + raise ValueError(f"Could not detect root at mountpoint {self.target}") - info(f'Adding bootloader {bootloader.value} to {boot_partition.dev_path}') + info(f"Adding bootloader {bootloader.value} to {boot_partition.dev_path}") if uki_enabled: self._config_uki(root, efi_partition) @@ -1544,7 +1565,7 @@ class Installer: return self.pacman.strap(packages) def enable_sudo(self, entity: str, group: bool = False): - info(f'Enabling sudo permissions for {entity}') + info(f"Enabling sudo permissions for {entity}") sudoers_dir = self.target / "etc/sudoers.d" @@ -1554,8 +1575,8 @@ class Installer: # Guarantees sudoer confs directory recommended perms sudoers_dir.chmod(0o440) # Appends a reference to the sudoers file, because if we are here sudoers.d did not exist yet - with open(self.target / 'etc/sudoers', 'a') as sudoers: - sudoers.write('@includedir /etc/sudoers.d\n') + with open(self.target / "etc/sudoers", "a") as sudoers: + sudoers.write("@includedir /etc/sudoers.d\n") # We count how many files are there already so we know which number to prefix the file with num_of_rules_already = len(os.listdir(sudoers_dir)) @@ -1563,12 +1584,12 @@ class Installer: # Guarantees that entity str does not contain invalid characters for a linux file name: # \ / : * ? " < > | - safe_entity_file_name = re.sub(r'(\\|\/|:|\*|\?|"|<|>|\|)', '', entity) + safe_entity_file_name = re.sub(r'(\\|\/|:|\*|\?|"|<|>|\|)', "", entity) rule_file = sudoers_dir / f"{file_num_str}_{safe_entity_file_name}" - with rule_file.open('a') as sudoers: - sudoers.write(f'{"%" if group else ""}{entity} ALL=(ALL) ALL\n') + with rule_file.open("a") as sudoers: + sudoers.write(f"{'%' if group else ''}{entity} ALL=(ALL) ALL\n") # Guarantees sudoer conf file recommended perms rule_file.chmod(0o440) @@ -1585,19 +1606,19 @@ class Installer: # Password and Group management is still handled by user_create() handled_by_plugin = False for plugin in plugins.values(): - if hasattr(plugin, 'on_user_create'): + if hasattr(plugin, "on_user_create"): if result := plugin.on_user_create(self, user): handled_by_plugin = result if not handled_by_plugin: - info(f'Creating user {user.username}') + info(f"Creating user {user.username}") - cmd = f'arch-chroot {self.target} useradd -m' + cmd = f"arch-chroot {self.target} useradd -m" if user.sudo: - cmd += ' -G wheel' + cmd += " -G wheel" - cmd += f' {user.username}' + cmd += f" {user.username}" try: SysCommand(cmd) @@ -1605,7 +1626,7 @@ class Installer: raise SystemError(f"Could not create user inside installation: {err}") for plugin in plugins.values(): - if hasattr(plugin, 'on_user_created'): + if hasattr(plugin, "on_user_created"): if result := plugin.on_user_created(self, user): handled_by_plugin = result @@ -1613,38 +1634,38 @@ class Installer: self.set_user_password(user) for group in user.groups: - SysCommand(f'arch-chroot {self.target} gpasswd -a {user.username} {group}') + SysCommand(f"arch-chroot {self.target} gpasswd -a {user.username} {group}") def set_user_password(self, user: User) -> bool: - info(f'Setting password for {user.username}') + info(f"Setting password for {user.username}") enc_password = user.password.enc_password if user.password else None if not enc_password: - debug('User password is empty') + debug("User password is empty") return False - input_data = f'{user.username}:{enc_password}'.encode() - cmd = ['arch-chroot', str(self.target), 'chpasswd', '--encrypted'] + input_data = f"{user.username}:{enc_password}".encode() + cmd = ["arch-chroot", str(self.target), "chpasswd", "--encrypted"] try: run(cmd, input_data=input_data) return True except CalledProcessError as err: - debug(f'Error setting user password: {err}') + debug(f"Error setting user password: {err}") return False def user_set_shell(self, user: str, shell: str) -> bool: - info(f'Setting shell for {user} to {shell}') + info(f"Setting shell for {user} to {shell}") try: - SysCommand(f"arch-chroot {self.target} sh -c \"chsh -s {shell} {user}\"") + SysCommand(f'arch-chroot {self.target} sh -c "chsh -s {shell} {user}"') return True except SysCallError: return False def chown(self, owner: str, path: str, options: list[str] = []) -> bool: - cleaned_path = path.replace('\'', '\\\'') + cleaned_path = path.replace("'", "\\'") try: SysCommand(f"arch-chroot {self.target} sh -c 'chown {' '.join(options)} {owner} {cleaned_path}'") return True @@ -1662,6 +1683,7 @@ class Installer: # In accordance with https://github.com/archlinux/archinstall/issues/107#issuecomment-841701968 # Setting an empty keymap first, allows the subsequent call to set layout for both console and x11. from .boot import Boot + with Boot(self) as session: os.system('systemd-run --machine=archinstall --pty localectl set-keymap ""') @@ -1672,7 +1694,7 @@ class Installer: info(f"Keyboard language for this installation is now set to: {language}") else: - info('Keyboard language was not changed from default (no language specified)') + info("Keyboard language was not changed from default (no language specified)") return True @@ -1689,6 +1711,7 @@ class Installer: return False from .boot import Boot + with Boot(self) as session: session.SysCommand(["localectl", "set-x11-keymap", '""']) @@ -1697,18 +1720,22 @@ class Installer: except SysCallError as err: raise ServiceException(f"Unable to set locale '{language}' for X11: {err}") else: - info('X11-Keyboard language was not changed from default (no language specified)') + info("X11-Keyboard language was not changed from default (no language specified)") return True def _service_started(self, service_name: str) -> str | None: - if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): - service_name += '.service' # Just to be safe + if os.path.splitext(service_name)[1] not in (".service", ".target", ".timer"): + service_name += ".service" # Just to be safe - last_execution_time = SysCommand( - f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}", - environment_vars={'SYSTEMD_COLORS': '0'} - ).decode().removeprefix('ActiveEnterTimestamp=') + last_execution_time = ( + SysCommand( + f"systemctl show --property=ActiveEnterTimestamp --no-pager {service_name}", + environment_vars={"SYSTEMD_COLORS": "0"}, + ) + .decode() + .removeprefix("ActiveEnterTimestamp=") + ) if not last_execution_time: return None @@ -1716,17 +1743,17 @@ class Installer: return last_execution_time def _service_state(self, service_name: str) -> str: - if os.path.splitext(service_name)[1] not in ('.service', '.target', '.timer'): - service_name += '.service' # Just to be safe + if os.path.splitext(service_name)[1] not in (".service", ".target", ".timer"): + service_name += ".service" # Just to be safe return SysCommand( - f'systemctl show --no-pager -p SubState --value {service_name}', - environment_vars={'SYSTEMD_COLORS': '0'} + f"systemctl show --no-pager -p SubState --value {service_name}", + environment_vars={"SYSTEMD_COLORS": "0"}, ).decode() def accessibility_tools_in_use() -> bool: - return os.system('systemctl is-active --quiet espeakup.service') == 0 + return os.system("systemctl is-active --quiet espeakup.service") == 0 def run_custom_user_commands(commands: list[str], installation: Installer) -> None: diff --git a/archinstall/lib/interactions/__init__.py b/archinstall/lib/interactions/__init__.py index b18ed7bd..53ea6640 100644 --- a/archinstall/lib/interactions/__init__.py +++ b/archinstall/lib/interactions/__init__.py @@ -20,26 +20,26 @@ from .network_menu import ManualNetworkConfig, ask_to_configure_network from .system_conf import ask_for_bootloader, ask_for_swap, ask_for_uki, select_driver, select_kernel __all__ = [ - 'ManualNetworkConfig', - 'UserList', - 'add_number_of_parallel_downloads', - 'ask_additional_packages_to_install', - 'ask_for_a_timezone', - 'ask_for_additional_users', - 'ask_for_audio_selection', - 'ask_for_bootloader', - 'ask_for_swap', - 'ask_for_uki', - 'ask_hostname', - 'ask_ntp', - 'ask_to_configure_network', - 'get_default_partition_layout', - 'select_archinstall_language', - 'select_devices', - 'select_disk_config', - 'select_driver', - 'select_kernel', - 'select_main_filesystem_format', - 'suggest_multi_disk_layout', - 'suggest_single_disk_layout', + "ManualNetworkConfig", + "UserList", + "add_number_of_parallel_downloads", + "ask_additional_packages_to_install", + "ask_for_a_timezone", + "ask_for_additional_users", + "ask_for_audio_selection", + "ask_for_bootloader", + "ask_for_swap", + "ask_for_uki", + "ask_hostname", + "ask_ntp", + "ask_to_configure_network", + "get_default_partition_layout", + "select_archinstall_language", + "select_devices", + "select_disk_config", + "select_driver", + "select_kernel", + "select_main_filesystem_format", + "suggest_multi_disk_layout", + "suggest_single_disk_layout", ] diff --git a/archinstall/lib/interactions/disk_conf.py b/archinstall/lib/interactions/disk_conf.py index dcf67054..eb7f7758 100644 --- a/archinstall/lib/interactions/disk_conf.py +++ b/archinstall/lib/interactions/disk_conf.py @@ -70,9 +70,9 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice]: search_enabled=False, multi=True, preview_style=PreviewStyle.BOTTOM, - preview_size='auto', - preview_frame=FrameProperties.max('Partitions'), - allow_skip=True + preview_size="auto", + preview_frame=FrameProperties.max("Partitions"), + allow_skip=True, ).run() match result.type_: @@ -93,24 +93,24 @@ def select_devices(preset: list[BDevice] | None = []) -> list[BDevice]: def get_default_partition_layout( devices: list[BDevice], - filesystem_type: FilesystemType | None = None + filesystem_type: FilesystemType | None = None, ) -> list[DeviceModification]: if len(devices) == 1: device_modification = suggest_single_disk_layout( devices[0], - filesystem_type=filesystem_type + filesystem_type=filesystem_type, ) return [device_modification] else: return suggest_multi_disk_layout( devices, - filesystem_type=filesystem_type + filesystem_type=filesystem_type, ) def _manual_partitioning( preset: list[DeviceModification], - devices: list[BDevice] + devices: list[BDevice], ) -> list[DeviceModification]: modifications = [] for device in devices: @@ -132,7 +132,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay items = [ MenuItem(default_layout, value=default_layout), MenuItem(manual_mode, value=manual_mode), - MenuItem(pre_mount_mode, value=pre_mount_mode) + MenuItem(pre_mount_mode, value=pre_mount_mode), ] group = MenuItemGroup(items, sort_items=False) @@ -143,8 +143,8 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay group, allow_skip=True, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Disk configuration type'))), - allow_reset=True + frame=FrameProperties.min(str(_("Disk configuration type"))), + allow_reset=True, ).run() match result.type_: @@ -156,10 +156,10 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay selection = result.get_value() if selection == pre_mount_mode: - output = 'You will use whatever drive-setup is mounted at the specified directory\n' + output = "You will use whatever drive-setup is mounted at the specified directory\n" output += "WARNING: Archinstall won't check the suitability of this setup\n" - path = prompt_dir(str(_('Root mount directory')), output, allow_skip=True) + path = prompt_dir(str(_("Root mount directory")), output, allow_skip=True) if path is None: return None @@ -169,7 +169,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay return DiskLayoutConfiguration( config_type=DiskLayoutType.Pre_mount, device_modifications=mods, - mountpoint=path + mountpoint=path, ) preset_devices = [mod.device for mod in preset.device_modifications] if preset else [] @@ -183,7 +183,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay if modifications: return DiskLayoutConfiguration( config_type=DiskLayoutType.Default, - device_modifications=modifications + device_modifications=modifications, ) elif result.get_value() == manual_mode: preset_mods = preset.device_modifications if preset else [] @@ -192,7 +192,7 @@ def select_disk_config(preset: DiskLayoutConfiguration | None = None) -> DiskLay if modifications: return DiskLayoutConfiguration( config_type=DiskLayoutType.Manual, - device_modifications=modifications + device_modifications=modifications, ) return None @@ -213,8 +213,8 @@ def select_lvm_config( group, allow_reset=True, allow_skip=True, - frame=FrameProperties.min(str(_('LVM configuration type'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("LVM configuration type"))), + alignment=Alignment.CENTER, ).run() match result.type_: @@ -242,42 +242,42 @@ def _boot_partition(sector_size: SectorSize, using_gpt: bool) -> PartitionModifi type=PartitionType.Primary, start=start, length=size, - mountpoint=Path('/boot'), + mountpoint=Path("/boot"), fs_type=FilesystemType.Fat32, - flags=flags + flags=flags, ) def select_main_filesystem_format() -> FilesystemType: items = [ - MenuItem('btrfs', value=FilesystemType.Btrfs), - MenuItem('ext4', value=FilesystemType.Ext4), - MenuItem('xfs', value=FilesystemType.Xfs), - MenuItem('f2fs', value=FilesystemType.F2fs) + MenuItem("btrfs", value=FilesystemType.Btrfs), + MenuItem("ext4", value=FilesystemType.Ext4), + MenuItem("xfs", value=FilesystemType.Xfs), + MenuItem("f2fs", value=FilesystemType.F2fs), ] if arch_config_handler.args.advanced: - items.append(MenuItem('ntfs', value=FilesystemType.Ntfs)) + items.append(MenuItem("ntfs", value=FilesystemType.Ntfs)) group = MenuItemGroup(items, sort_items=False) result = SelectMenu[FilesystemType]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min('Filesystem'), - allow_skip=False + frame=FrameProperties.min("Filesystem"), + allow_skip=False, ).run() match result.type_: case ResultType.Selection: return result.get_value() case _: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def select_mount_options() -> list[str]: - prompt = str(_('Would you like to use compression or disable CoW?')) + '\n' - compression = str(_('Use compression')) - disable_cow = str(_('Disable Copy-on-Write')) + prompt = str(_("Would you like to use compression or disable CoW?")) + "\n" + compression = str(_("Use compression")) + disable_cow = str(_("Disable Copy-on-Write")) items = [ MenuItem(compression, value=BtrfsMountOption.compress.value), @@ -291,7 +291,7 @@ def select_mount_options() -> list[str]: columns=2, orientation=Orientation.HORIZONTAL, search_enabled=False, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -300,7 +300,7 @@ def select_mount_options() -> list[str]: case ResultType.Selection: return [result.get_value()] case _: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def process_root_partition_size(total_size: Size, sector_size: SectorSize) -> Size: @@ -321,7 +321,7 @@ def process_root_partition_size(total_size: Size, sector_size: SectorSize) -> Si def suggest_single_disk_layout( device: BDevice, filesystem_type: FilesystemType | None = None, - separate_home: bool | None = None + separate_home: bool | None = None, ) -> DeviceModification: if not filesystem_type: filesystem_type = select_main_filesystem_format() @@ -332,7 +332,7 @@ def suggest_single_disk_layout( min_size_to_allow_home_part = Size(64, Unit.GiB, sector_size) if filesystem_type == FilesystemType.Btrfs: - prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?')) + '\n' + prompt = str(_("Would you like to use BTRFS subvolumes with a default structure?")) + "\n" group = MenuItemGroup.yes_no() group.set_focus_by_value(MenuItem.yes().value) result = SelectMenu[bool]( @@ -341,7 +341,7 @@ def suggest_single_disk_layout( alignment=Alignment.CENTER, columns=2, orientation=Orientation.HORIZONTAL, - allow_skip=False + allow_skip=False, ).run() using_subvolumes = result.item() == MenuItem.yes() @@ -364,16 +364,12 @@ def suggest_single_disk_layout( boot_partition = _boot_partition(sector_size, using_gpt) device_modification.add_partition(boot_partition) - if ( - separate_home is False - or using_subvolumes - or total_size < min_size_to_allow_home_part - ): + if separate_home is False or using_subvolumes or total_size < min_size_to_allow_home_part: using_home_partition = False elif separate_home: using_home_partition = True else: - prompt = str(_('Would you like to create a separate partition for /home?')) + '\n' + prompt = str(_("Would you like to create a separate partition for /home?")) + "\n" group = MenuItemGroup.yes_no() group.set_focus_by_value(MenuItem.yes().value) result = SelectMenu( @@ -382,7 +378,7 @@ def suggest_single_disk_layout( orientation=Orientation.HORIZONTAL, columns=2, alignment=Alignment.CENTER, - allow_skip=False + allow_skip=False, ).run() using_home_partition = result.item() == MenuItem.yes() @@ -401,9 +397,9 @@ def suggest_single_disk_layout( type=PartitionType.Primary, start=root_start, length=root_length, - mountpoint=Path('/') if not using_subvolumes else None, + mountpoint=Path("/") if not using_subvolumes else None, fs_type=filesystem_type, - mount_options=mount_options + mount_options=mount_options, ) device_modification.add_partition(root_partition) @@ -413,10 +409,10 @@ def suggest_single_disk_layout( # https://unix.stackexchange.com/questions/246976/btrfs-subvolume-uuid-clash # https://github.com/classy-giraffe/easy-arch/blob/main/easy-arch.sh subvolumes = [ - SubvolumeModification(Path('@'), Path('/')), - SubvolumeModification(Path('@home'), Path('/home')), - SubvolumeModification(Path('@log'), Path('/var/log')), - SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')) + SubvolumeModification(Path("@"), Path("/")), + SubvolumeModification(Path("@home"), Path("/home")), + SubvolumeModification(Path("@log"), Path("/var/log")), + SubvolumeModification(Path("@pkg"), Path("/var/cache/pacman/pkg")), ] root_partition.btrfs_subvols = subvolumes elif using_home_partition: @@ -435,10 +431,10 @@ def suggest_single_disk_layout( type=PartitionType.Primary, start=home_start, length=home_length, - mountpoint=Path('/home'), + mountpoint=Path("/home"), fs_type=filesystem_type, mount_options=mount_options, - flags=flags + flags=flags, ) device_modification.add_partition(home_partition) @@ -447,7 +443,7 @@ def suggest_single_disk_layout( def suggest_multi_disk_layout( devices: list[BDevice], - filesystem_type: FilesystemType | None = None + filesystem_type: FilesystemType | None = None, ) -> list[DeviceModification]: if not devices: return [] @@ -478,11 +474,11 @@ def suggest_multi_disk_layout( root_device: BDevice | None = sorted_delta[0][0] if home_device is None or root_device is None: - text = str(_('The selected drives do not have the minimum capacity required for an automatic suggestion\n')) - text += str(_('Minimum capacity for /home partition: {}GiB\n').format(min_home_partition_size.format_size(Unit.GiB))) - text += str(_('Minimum capacity for Arch Linux partition: {}GiB').format(desired_root_partition_size.format_size(Unit.GiB))) + text = str(_("The selected drives do not have the minimum capacity required for an automatic suggestion\n")) + text += str(_("Minimum capacity for /home partition: {}GiB\n").format(min_home_partition_size.format_size(Unit.GiB))) + text += str(_("Minimum capacity for Arch Linux partition: {}GiB").format(desired_root_partition_size.format_size(Unit.GiB))) - items = [MenuItem(str(_('Continue')))] + items = [MenuItem(str(_("Continue")))] group = MenuItemGroup(items) SelectMenu(group).run() @@ -491,11 +487,11 @@ def suggest_multi_disk_layout( if filesystem_type == FilesystemType.Btrfs: mount_options = select_mount_options() - device_paths = ', '.join([str(d.device_info.path) for d in devices]) + device_paths = ", ".join([str(d.device_info.path) for d in devices]) - debug(f'Suggesting multi-disk-layout for devices: {device_paths}') - debug(f'/root: {root_device.device_info.path}') - debug(f'/home: {home_device.device_info.path}') + debug(f"Suggesting multi-disk-layout for devices: {device_paths}") + debug(f"/root: {root_device.device_info.path}") + debug(f"/home: {home_device.device_info.path}") root_device_modification = DeviceModification(root_device, wipe=True) home_device_modification = DeviceModification(home_device, wipe=True) @@ -523,9 +519,9 @@ def suggest_multi_disk_layout( type=PartitionType.Primary, start=root_start, length=root_length, - mountpoint=Path('/'), + mountpoint=Path("/"), mount_options=mount_options, - fs_type=filesystem_type + fs_type=filesystem_type, ) root_device_modification.add_partition(root_partition) @@ -545,10 +541,10 @@ def suggest_multi_disk_layout( type=PartitionType.Primary, start=home_start, length=home_length, - mountpoint=Path('/home'), + mountpoint=Path("/home"), mount_options=mount_options, fs_type=filesystem_type, - flags=flags + flags=flags, ) home_device_modification.add_partition(home_partition) @@ -558,10 +554,10 @@ def suggest_multi_disk_layout( def suggest_lvm_layout( disk_config: DiskLayoutConfiguration, filesystem_type: FilesystemType | None = None, - vg_grp_name: str = 'ArchinstallVg', + vg_grp_name: str = "ArchinstallVg", ) -> LvmConfiguration: if disk_config.config_type != DiskLayoutType.Default: - raise ValueError('LVM suggested volumes are only available for default partitioning') + raise ValueError("LVM suggested volumes are only available for default partitioning") using_subvolumes = False btrfs_subvols = [] @@ -572,7 +568,7 @@ def suggest_lvm_layout( filesystem_type = select_main_filesystem_format() if filesystem_type == FilesystemType.Btrfs: - prompt = str(_('Would you like to use BTRFS subvolumes with a default structure?')) + '\n' + prompt = str(_("Would you like to use BTRFS subvolumes with a default structure?")) + "\n" group = MenuItemGroup.yes_no() group.set_focus_by_value(MenuItem.yes().value) @@ -591,10 +587,10 @@ def suggest_lvm_layout( if using_subvolumes: btrfs_subvols = [ - SubvolumeModification(Path('@'), Path('/')), - SubvolumeModification(Path('@home'), Path('/home')), - SubvolumeModification(Path('@log'), Path('/var/log')), - SubvolumeModification(Path('@pkg'), Path('/var/cache/pacman/pkg')) + SubvolumeModification(Path("@"), Path("/")), + SubvolumeModification(Path("@home"), Path("/home")), + SubvolumeModification(Path("@log"), Path("/var/log")), + SubvolumeModification(Path("@pkg"), Path("/var/cache/pacman/pkg")), ] home_volume = False @@ -610,7 +606,7 @@ def suggest_lvm_layout( other_part.append(part) if not boot_part: - raise ValueError('Unable to find boot partition in partition modifications') + raise ValueError("Unable to find boot partition in partition modifications") total_vol_available = sum( [p.length for p in other_part], @@ -623,12 +619,12 @@ def suggest_lvm_layout( root_vol = LvmVolume( status=LvmVolumeStatus.Create, - name='root', + name="root", fs_type=filesystem_type, length=root_vol_size, - mountpoint=Path('/'), + mountpoint=Path("/"), btrfs_subvols=btrfs_subvols, - mount_options=mount_options + mount_options=mount_options, ) lvm_vol_group.volumes.append(root_vol) @@ -636,10 +632,10 @@ def suggest_lvm_layout( if home_volume: home_vol = LvmVolume( status=LvmVolumeStatus.Create, - name='home', + name="home", fs_type=filesystem_type, length=home_vol_size, - mountpoint=Path('/home'), + mountpoint=Path("/home"), ) lvm_vol_group.volumes.append(home_vol) diff --git a/archinstall/lib/interactions/general_conf.py b/archinstall/lib/interactions/general_conf.py index 4fefb691..747b24fc 100644 --- a/archinstall/lib/interactions/general_conf.py +++ b/archinstall/lib/interactions/general_conf.py @@ -26,17 +26,22 @@ if TYPE_CHECKING: class PostInstallationAction(Enum): - EXIT = str(_('Exit archinstall')) - REBOOT = str(_('Reboot system')) - CHROOT = str(_('chroot into installation for post-installation configurations')) + EXIT = str(_("Exit archinstall")) + REBOOT = str(_("Reboot system")) + CHROOT = str(_("chroot into installation for post-installation configurations")) def ask_ntp(preset: bool = True) -> bool: - header = str(_('Would you like to use automatic time synchronization (NTP) with the default time servers?\n')) + '\n' - header += str(_( - 'Hardware time and other post-configuration steps might be required in order for NTP to work.\n' - 'For more information, please check the Arch wiki' - )) + '\n' + header = str(_("Would you like to use automatic time synchronization (NTP) with the default time servers?\n")) + "\n" + header += ( + str( + _( + "Hardware time and other post-configuration steps might be required in order for NTP to work.\n" + "For more information, please check the Arch wiki", + ) + ) + + "\n" + ) preset_val = MenuItem.yes() if preset else MenuItem.no() group = MenuItemGroup.yes_no() @@ -48,7 +53,7 @@ def ask_ntp(preset: bool = True) -> bool: allow_skip=True, alignment=Alignment.CENTER, columns=2, - orientation=Orientation.HORIZONTAL + orientation=Orientation.HORIZONTAL, ).run() match result.type_: @@ -57,15 +62,15 @@ def ask_ntp(preset: bool = True) -> bool: case ResultType.Selection: return result.item() == MenuItem.yes() case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") def ask_hostname(preset: str | None = None) -> str | None: result = EditMenu( - str(_('Hostname')), + str(_("Hostname")), alignment=Alignment.CENTER, allow_skip=True, - default_text=preset + default_text=preset, ).input() match result.type_: @@ -77,11 +82,11 @@ def ask_hostname(preset: str | None = None) -> str | None: return None return hostname case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def ask_for_a_timezone(preset: str | None = None) -> str | None: - default = 'UTC' + default = "UTC" timezones = list_timezones() items = [MenuItem(tz, value=tz) for tz in timezones] @@ -93,7 +98,7 @@ def ask_for_a_timezone(preset: str | None = None) -> str | None: group, allow_reset=True, allow_skip=True, - frame=FrameProperties.min(str(_('Timezone'))), + frame=FrameProperties.min(str(_("Timezone"))), alignment=Alignment.CENTER, ).run() @@ -117,7 +122,7 @@ def ask_for_audio_selection(preset: AudioConfiguration | None = None) -> AudioCo group, allow_skip=True, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Audio'))) + frame=FrameProperties.min(str(_("Audio"))), ).run() match result.type_: @@ -126,7 +131,7 @@ def ask_for_audio_selection(preset: AudioConfiguration | None = None) -> AudioCo case ResultType.Selection: return AudioConfiguration(audio=result.get_value()) case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def select_language(preset: str | None = None) -> str | None: @@ -137,8 +142,7 @@ def select_language(preset: str | None = None) -> str | None: # raise Deprecated("select_language() has been deprecated, use select_kb_layout() instead.") # No need to translate this i feel, as it's a short lived message. - warn( - "select_language() is deprecated, use select_kb_layout() instead. select_language() will be removed in a future version") + warn("select_language() is deprecated, use select_kb_layout() instead. select_language() will be removed in a future version") return select_kb_layout(preset) @@ -152,9 +156,9 @@ def select_archinstall_language(languages: list[Language], preset: Language) -> group = MenuItemGroup(items, sort_items=True) group.set_focus_by_value(preset) - title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n' + title = "NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n" title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n' - title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n' + title += "e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n" result = SelectMenu[Language]( group, @@ -162,7 +166,7 @@ def select_archinstall_language(languages: list[Language], preset: Language) -> allow_skip=True, allow_reset=False, alignment=Alignment.CENTER, - frame=FrameProperties.min(header=str(_('Select language'))) + frame=FrameProperties.min(header=str(_("Select language"))), ).run() match result.type_: @@ -171,27 +175,27 @@ def select_archinstall_language(languages: list[Language], preset: Language) -> case ResultType.Selection: return result.get_value() case ResultType.Reset: - raise ValueError('Language selection not handled') + raise ValueError("Language selection not handled") def ask_additional_packages_to_install( preset: list[str] = [], - repositories: set[Repository] = set() + repositories: set[Repository] = set(), ) -> list[str]: repositories |= {Repository.Core, Repository.Extra} - respos_text = ', '.join([r.value for r in repositories]) - output = str(_('Repositories: {}')).format(respos_text) + '\n' + respos_text = ", ".join([r.value for r in repositories]) + output = str(_("Repositories: {}")).format(respos_text) + "\n" - output += str(_('Loading packages...')) + output += str(_("Loading packages...")) Tui.print(output, clear_screen=True) packages = list_available_packages(tuple(repositories)) package_groups = PackageGroup.from_available_packages(packages) # Additional packages (with some light weight error handling for invalid package names) - header = str(_('Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.')) + '\n' - header += str(_('Select any packages from the below list that should be installed additionally')) + '\n' + header = str(_("Only packages such as base, base-devel, linux, linux-firmware, efibootmgr and optional profile packages are installed.")) + "\n" + header += str(_("Select any packages from the below list that should be installed additionally")) + "\n" # there are over 15k packages so this needs to be quick preset_packages: list[AvailablePackage | PackageGroup] = [] @@ -205,16 +209,18 @@ def ask_additional_packages_to_install( MenuItem( name, value=pkg, - preview_action=lambda x: x.value.info() - ) for name, pkg in packages.items() + preview_action=lambda x: x.value.info(), + ) + for name, pkg in packages.items() ] items += [ MenuItem( name, value=group, - preview_action=lambda x: x.value.info() - ) for name, group in package_groups.items() + preview_action=lambda x: x.value.info(), + ) + for name, group in package_groups.items() ] menu_group = MenuItemGroup(items, sort_items=True) @@ -227,9 +233,9 @@ def ask_additional_packages_to_install( allow_reset=True, allow_skip=True, multi=True, - preview_frame=FrameProperties.max('Package info'), + preview_frame=FrameProperties.max("Package info"), preview_style=PreviewStyle.RIGHT, - preview_size='auto' + preview_size="auto", ).run() match result.type_: @@ -245,10 +251,10 @@ def ask_additional_packages_to_install( def add_number_of_parallel_downloads(preset: int | None = None) -> int | None: max_recommended = 5 - header = str(_('This option enables the number of parallel downloads that can occur during package downloads')) + '\n' - header += str(_('Enter the number of parallel downloads to be enabled.\n\nNote:\n')) - header += str(_(' - Maximum recommended value : {} ( Allows {} parallel downloads at a time )')).format(max_recommended, max_recommended) + '\n' - header += str(_(' - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n')) + header = str(_("This option enables the number of parallel downloads that can occur during package downloads")) + "\n" + header += str(_("Enter the number of parallel downloads to be enabled.\n\nNote:\n")) + header += str(_(" - Maximum recommended value : {} ( Allows {} parallel downloads at a time )")).format(max_recommended, max_recommended) + "\n" + header += str(_(" - Disable/Default : 0 ( Disables parallel downloading, allows only 1 download at a time )\n")) def validator(s: str) -> str | None: try: @@ -258,15 +264,15 @@ def add_number_of_parallel_downloads(preset: int | None = None) -> int | None: except Exception: pass - return str(_('Invalid download number')) + return str(_("Invalid download number")) result = EditMenu( - str(_('Number downloads')), + str(_("Number downloads")), header=header, allow_skip=True, allow_reset=True, validator=validator, - default_text=str(preset) if preset is not None else None + default_text=str(preset) if preset is not None else None, ).input() match result.type_: @@ -294,8 +300,8 @@ def add_number_of_parallel_downloads(preset: int | None = None) -> int | None: def ask_post_installation() -> PostInstallationAction: - header = str(_('Installation completed')) + '\n\n' - header += str(_('What would you like to do next?')) + '\n' + header = str(_("Installation completed")) + "\n\n" + header += str(_("What would you like to do next?")) + "\n" items = [MenuItem(action.value, value=action) for action in PostInstallationAction] group = MenuItemGroup(items) @@ -311,11 +317,11 @@ def ask_post_installation() -> PostInstallationAction: case ResultType.Selection: return result.get_value() case _: - raise ValueError('Post installation action not handled') + raise ValueError("Post installation action not handled") def ask_abort() -> None: - prompt = str(_('Do you really want to abort?')) + '\n' + prompt = str(_("Do you really want to abort?")) + "\n" group = MenuItemGroup.yes_no() result = SelectMenu[bool]( @@ -324,7 +330,7 @@ def ask_abort() -> None: allow_skip=False, alignment=Alignment.CENTER, columns=2, - orientation=Orientation.HORIZONTAL + orientation=Orientation.HORIZONTAL, ).run() if result.item() == MenuItem.yes(): diff --git a/archinstall/lib/interactions/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py index dbe3aed4..99b7fed4 100644 --- a/archinstall/lib/interactions/manage_users_conf.py +++ b/archinstall/lib/interactions/manage_users_conf.py @@ -23,17 +23,17 @@ if TYPE_CHECKING: class UserList(ListManager[User]): def __init__(self, prompt: str, lusers: list[User]): self._actions = [ - str(_('Add a user')), - str(_('Change password')), - str(_('Promote/Demote user')), - str(_('Delete User')) + str(_("Add a user")), + str(_("Change password")), + str(_("Promote/Demote user")), + str(_("Delete User")), ] super().__init__( lusers, [self._actions[0]], self._actions[1:], - prompt + prompt, ) @override @@ -50,8 +50,8 @@ class UserList(ListManager[User]): data = [d for d in data if d.username != new_user.username] data += [new_user] elif action == self._actions[1] and entry: # change password - header = f'{_("User")}: {entry.username}\n' - new_password = get_password(str(_('Password')), header=header) + header = f"{_('User')}: {entry.username}\n" + new_password = get_password(str(_("Password")), header=header) if new_password: user = next(filter(lambda x: x == entry, data)) @@ -65,15 +65,15 @@ class UserList(ListManager[User]): return data def _check_for_correct_username(self, username: str) -> str | None: - if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32: + if re.match(r"^[a-z_][a-z0-9_-]*\$?$", username) and len(username) <= 32: return None return str(_("The username you entered is invalid")) def _add_user(self) -> User | None: editResult = EditMenu( - str(_('Username')), + str(_("Username")), allow_skip=True, - validator=self._check_for_correct_username + validator=self._check_for_correct_username, ).input() match editResult.type_: @@ -82,16 +82,16 @@ class UserList(ListManager[User]): case ResultType.Selection: username = editResult.text() case _: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") - header = f'{_("Username")}: {username}\n' + header = f"{_('Username')}: {username}\n" - password = get_password(str(_('Password')), header=header, allow_skip=True) + password = get_password(str(_("Password")), header=header, allow_skip=True) if not password: return None - header += f'{_("Password")}: {password.hidden()}\n\n' + header += f"{_('Password')}: {password.hidden()}\n\n" header += str(_('Should "{}" be a superuser (sudo)?\n')).format(username) group = MenuItemGroup.yes_no() @@ -104,18 +104,18 @@ class UserList(ListManager[User]): columns=2, orientation=Orientation.HORIZONTAL, search_enabled=False, - allow_skip=False + allow_skip=False, ).run() match result.type_: case ResultType.Selection: sudo = result.item() == MenuItem.yes() case _: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") return User(username, password, sudo) -def ask_for_additional_users(prompt: str = '', defined_users: list[User] = []) -> list[User]: +def ask_for_additional_users(prompt: str = "", defined_users: list[User] = []) -> list[User]: users = UserList(prompt, defined_users).run() return users diff --git a/archinstall/lib/interactions/network_menu.py b/archinstall/lib/interactions/network_menu.py index ec50e5f1..38994580 100644 --- a/archinstall/lib/interactions/network_menu.py +++ b/archinstall/lib/interactions/network_menu.py @@ -23,21 +23,21 @@ if TYPE_CHECKING: class ManualNetworkConfig(ListManager[Nic]): def __init__(self, prompt: str, preset: list[Nic]): self._actions = [ - str(_('Add interface')), - str(_('Edit interface')), - str(_('Delete interface')) + str(_("Add interface")), + str(_("Edit interface")), + str(_("Delete interface")), ] super().__init__( preset, [self._actions[0]], self._actions[1:], - prompt + prompt, ) @override def selected_action_display(self, selection: Nic) -> str: - return selection.iface if selection.iface else '' + return selection.iface if selection.iface else "" @override def handle_action(self, action: str, entry: Nic | None, data: list[Nic]) -> list[Nic]: @@ -73,8 +73,8 @@ class ManualNetworkConfig(ListManager[Nic]): result = SelectMenu[str]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Interfaces'))), - allow_skip=True + frame=FrameProperties.min(str(_("Interfaces"))), + allow_skip=True, ).run() match result.type_: @@ -83,7 +83,7 @@ class ManualNetworkConfig(ListManager[Nic]): case ResultType.Selection: return result.get_value() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def _get_ip_address( self, @@ -91,11 +91,11 @@ class ManualNetworkConfig(ListManager[Nic]): header: str, allow_skip: bool, multi: bool, - preset: str | None = None + preset: str | None = None, ) -> str | None: def validator(ip: str) -> str | None: if multi: - ips = ip.split(' ') + ips = ip.split(" ") else: ips = [ip] @@ -104,14 +104,14 @@ class ManualNetworkConfig(ListManager[Nic]): ipaddress.ip_interface(ip) return None except ValueError: - return str(_('You need to enter a valid IP in IP-config mode')) + return str(_("You need to enter a valid IP in IP-config mode")) result = EditMenu( title, header=header, validator=validator, allow_skip=allow_skip, - default_text=preset + default_text=preset, ).input() match result.type_: @@ -120,14 +120,14 @@ class ManualNetworkConfig(ListManager[Nic]): case ResultType.Selection: return result.text() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def _edit_iface(self, edit_nic: Nic) -> Nic: iface_name = edit_nic.iface - modes = ['DHCP (auto detect)', 'IP (static)'] - default_mode = 'DHCP (auto detect)' + modes = ["DHCP (auto detect)", "IP (static)"] + default_mode = "DHCP (auto detect)" - header = str(_('Select which mode to configure for "{}"').format(iface_name)) + '\n' + header = str(_('Select which mode to configure for "{}"').format(iface_name)) + "\n" items = [MenuItem(m, value=m) for m in modes] group = MenuItemGroup(items, sort_items=True) group.set_default_by_value(default_mode) @@ -137,43 +137,43 @@ class ManualNetworkConfig(ListManager[Nic]): header=header, allow_skip=False, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Modes'))) + frame=FrameProperties.min(str(_("Modes"))), ).run() match result.type_: case ResultType.Selection: mode = result.get_value() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") case ResultType.Skip: - raise ValueError('The mode menu should not be skippable') + raise ValueError("The mode menu should not be skippable") case _: assert_never(result.type_) - if mode == 'IP (static)': - header = str(_('Enter the IP and subnet for {} (example: 192.168.0.5/24): ').format(iface_name)) + '\n' - ip = self._get_ip_address(str(_('IP address')), header, False, False) + if mode == "IP (static)": + header = str(_("Enter the IP and subnet for {} (example: 192.168.0.5/24): ").format(iface_name)) + "\n" + ip = self._get_ip_address(str(_("IP address")), header, False, False) - header = str(_('Enter your gateway (router) IP address (leave blank for none)')) + '\n' - gateway = self._get_ip_address(str(_('Gateway address')), header, True, False) + header = str(_("Enter your gateway (router) IP address (leave blank for none)")) + "\n" + gateway = self._get_ip_address(str(_("Gateway address")), header, True, False) if edit_nic.dns: - display_dns = ' '.join(edit_nic.dns) + display_dns = " ".join(edit_nic.dns) else: display_dns = None - header = str(_('Enter your DNS servers with space separated (leave blank for none)')) + '\n' + header = str(_("Enter your DNS servers with space separated (leave blank for none)")) + "\n" dns_servers = self._get_ip_address( - str(_('DNS servers')), + str(_("DNS servers")), header, True, True, - display_dns + display_dns, ) dns = [] if dns_servers is not None: - dns = dns_servers.split(' ') + dns = dns_servers.split(" ") return Nic(iface=iface_name, ip=ip, gateway=gateway, dns=dns, dhcp=False) else: @@ -195,9 +195,9 @@ def ask_to_configure_network(preset: NetworkConfiguration | None) -> NetworkConf result = SelectMenu[NetworkConfiguration]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Network configuration'))), + frame=FrameProperties.min(str(_("Network configuration"))), allow_reset=True, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -215,7 +215,7 @@ def ask_to_configure_network(preset: NetworkConfiguration | None) -> NetworkConf return NetworkConfiguration(NicType.NM) case NicType.MANUAL: preset_nics = preset.nics if preset else [] - nics = ManualNetworkConfig(str(_('Configure interfaces')), preset_nics).run() + nics = ManualNetworkConfig(str(_("Configure interfaces")), preset_nics).run() if nics: return NetworkConfiguration(NicType.MANUAL, nics) diff --git a/archinstall/lib/interactions/system_conf.py b/archinstall/lib/interactions/system_conf.py index 65afa279..18b0d4c0 100644 --- a/archinstall/lib/interactions/system_conf.py +++ b/archinstall/lib/interactions/system_conf.py @@ -40,8 +40,8 @@ def select_kernel(preset: list[str] = []) -> list[str]: allow_skip=True, allow_reset=True, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Kernel'))), - multi=True + frame=FrameProperties.min(str(_("Kernel"))), + multi=True, ).run() match result.type_: @@ -58,7 +58,7 @@ def ask_for_bootloader(preset: Bootloader | None) -> Bootloader | None: if not SysInfo.has_uefi(): options = [Bootloader.Grub, Bootloader.Limine] default = Bootloader.Grub - header = str(_('UEFI is not detected and some options are disabled')) + header = str(_("UEFI is not detected and some options are disabled")) else: options = [b for b in Bootloader] default = Bootloader.Systemd @@ -73,8 +73,8 @@ def ask_for_bootloader(preset: Bootloader | None) -> Bootloader | None: group, header=header, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Bootloader'))), - allow_skip=True + frame=FrameProperties.min(str(_("Bootloader"))), + allow_skip=True, ).run() match result.type_: @@ -83,11 +83,11 @@ def ask_for_bootloader(preset: Bootloader | None) -> Bootloader | None: case ResultType.Selection: return result.get_value() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def ask_for_uki(preset: bool = True) -> bool: - prompt = str(_('Would you like to use unified kernel images?')) + '\n' + prompt = str(_("Would you like to use unified kernel images?")) + "\n" group = MenuItemGroup.yes_no() group.set_focus_by_value(preset) @@ -98,7 +98,7 @@ def ask_for_uki(preset: bool = True) -> bool: columns=2, orientation=Orientation.HORIZONTAL, alignment=Alignment.CENTER, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -107,7 +107,7 @@ def ask_for_uki(preset: bool = True) -> bool: case ResultType.Selection: return result.item() == MenuItem.yes() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None) -> GfxDriver | None: @@ -128,22 +128,22 @@ def select_driver(options: list[GfxDriver] = [], preset: GfxDriver | None = None if preset is not None: group.set_focus_by_value(preset) - header = '' + header = "" if SysInfo.has_amd_graphics(): - header += str(_('For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.')) + '\n' + header += str(_("For the best compatibility with your AMD hardware, you may want to use either the all open-source or AMD / ATI options.")) + "\n" if SysInfo.has_intel_graphics(): - header += str(_('For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n')) + header += str(_("For the best compatibility with your Intel hardware, you may want to use either the all open-source or Intel options.\n")) if SysInfo.has_nvidia_graphics(): - header += str(_('For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n')) + header += str(_("For the best compatibility with your Nvidia hardware, you may want to use the Nvidia proprietary driver.\n")) result = SelectMenu[GfxDriver]( group, header=header, allow_skip=True, allow_reset=True, - preview_size='auto', + preview_size="auto", preview_style=PreviewStyle.BOTTOM, - preview_frame=FrameProperties(str(_('Info')), h_frame_style=FrameStyle.MIN) + preview_frame=FrameProperties(str(_("Info")), h_frame_style=FrameStyle.MIN), ).run() match result.type_: @@ -161,7 +161,7 @@ def ask_for_swap(preset: bool = True) -> bool: else: default_item = MenuItem.no() - prompt = str(_('Would you like to use swap on zram?')) + '\n' + prompt = str(_("Would you like to use swap on zram?")) + "\n" group = MenuItemGroup.yes_no() group.set_focus_by_value(default_item) @@ -172,7 +172,7 @@ def ask_for_swap(preset: bool = True) -> bool: columns=2, orientation=Orientation.HORIZONTAL, alignment=Alignment.CENTER, - allow_skip=True + allow_skip=True, ).run() match result.type_: @@ -181,6 +181,6 @@ def ask_for_swap(preset: bool = True) -> bool: case ResultType.Selection: return result.item() == MenuItem.yes() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") return preset diff --git a/archinstall/lib/locale/__init__.py b/archinstall/lib/locale/__init__.py index daff054c..c1e55d4d 100644 --- a/archinstall/lib/locale/__init__.py +++ b/archinstall/lib/locale/__init__.py @@ -9,11 +9,11 @@ from .utils import ( ) __all__ = [ - 'list_keyboard_languages', - 'list_locales', - 'list_timezones', - 'list_x11_keyboard_languages', - 'set_kb_layout', - 'verify_keyboard_layout', - 'verify_x11_keyboard_layout', + "list_keyboard_languages", + "list_locales", + "list_timezones", + "list_x11_keyboard_languages", + "set_kb_layout", + "verify_keyboard_layout", + "verify_x11_keyboard_layout", ] diff --git a/archinstall/lib/locale/locale_menu.py b/archinstall/lib/locale/locale_menu.py index dc3d739f..867240b0 100644 --- a/archinstall/lib/locale/locale_menu.py +++ b/archinstall/lib/locale/locale_menu.py @@ -20,7 +20,7 @@ if TYPE_CHECKING: class LocaleMenu(AbstractSubMenu[LocaleConfiguration]): def __init__( self, - locale_conf: LocaleConfiguration + locale_conf: LocaleConfiguration, ): self._locale_conf = locale_conf menu_optioons = self._define_menu_options() @@ -29,39 +29,39 @@ class LocaleMenu(AbstractSubMenu[LocaleConfiguration]): super().__init__( self._item_group, config=self._locale_conf, - allow_reset=True + allow_reset=True, ) def _define_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Keyboard layout')), + text=str(_("Keyboard layout")), action=self._select_kb_layout, value=self._locale_conf.kb_layout, preview_action=self._prev_locale, - key='kb_layout' + key="kb_layout", ), MenuItem( - text=str(_('Locale language')), + text=str(_("Locale language")), action=select_locale_lang, value=self._locale_conf.sys_lang, preview_action=self._prev_locale, - key='sys_lang' + key="sys_lang", ), MenuItem( - text=str(_('Locale encoding')), + text=str(_("Locale encoding")), action=select_locale_enc, value=self._locale_conf.sys_enc, preview_action=self._prev_locale, - key='sys_enc' - ) + key="sys_enc", + ), ] def _prev_locale(self, item: MenuItem) -> str | None: temp_locale = LocaleConfiguration( - self._menu_item_group.find_by_key('kb_layout').get_value(), - self._menu_item_group.find_by_key('sys_lang').get_value(), - self._menu_item_group.find_by_key('sys_enc').get_value(), + self._menu_item_group.find_by_key("kb_layout").get_value(), + self._menu_item_group.find_by_key("sys_lang").get_value(), + self._menu_item_group.find_by_key("sys_enc").get_value(), ) return temp_locale.preview() @@ -88,7 +88,7 @@ def select_locale_lang(preset: str | None = None) -> str | None: result = SelectMenu[str]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Locale language'))), + frame=FrameProperties.min(str(_("Locale language"))), allow_skip=True, ).run() @@ -98,7 +98,7 @@ def select_locale_lang(preset: str | None = None) -> str | None: case ResultType.Skip: return preset case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") def select_locale_enc(preset: str | None = None) -> str | None: @@ -112,7 +112,7 @@ def select_locale_enc(preset: str | None = None) -> str | None: result = SelectMenu[str]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Locale encoding'))), + frame=FrameProperties.min(str(_("Locale encoding"))), allow_skip=True, ).run() @@ -122,7 +122,7 @@ def select_locale_enc(preset: str | None = None) -> str | None: case ResultType.Skip: return preset case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") def select_kb_layout(preset: str | None = None) -> str | None: @@ -144,7 +144,7 @@ def select_kb_layout(preset: str | None = None) -> str | None: result = SelectMenu[str]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Keyboard layout'))), + frame=FrameProperties.min(str(_("Keyboard layout"))), allow_skip=True, ).run() @@ -154,6 +154,6 @@ def select_kb_layout(preset: str | None = None) -> str | None: case ResultType.Skip: return preset case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") return None diff --git a/archinstall/lib/locale/utils.py b/archinstall/lib/locale/utils.py index f596e52e..22b4bf39 100644 --- a/archinstall/lib/locale/utils.py +++ b/archinstall/lib/locale/utils.py @@ -4,28 +4,36 @@ from ..output import error def list_keyboard_languages() -> list[str]: - return SysCommand( - "localectl --no-pager list-keymaps", - environment_vars={'SYSTEMD_COLORS': '0'} - ).decode().splitlines() + return ( + SysCommand( + "localectl --no-pager list-keymaps", + environment_vars={"SYSTEMD_COLORS": "0"}, + ) + .decode() + .splitlines() + ) def list_locales() -> list[str]: locales = [] - with open('/usr/share/i18n/SUPPORTED') as file: + with open("/usr/share/i18n/SUPPORTED") as file: for line in file: - if line != 'C.UTF-8 UTF-8\n': + if line != "C.UTF-8 UTF-8\n": locales.append(line.rstrip()) return locales def list_x11_keyboard_languages() -> list[str]: - return SysCommand( - "localectl --no-pager list-x11-keymap-layouts", - environment_vars={'SYSTEMD_COLORS': '0'} - ).decode().splitlines() + return ( + SysCommand( + "localectl --no-pager list-x11-keymap-layouts", + environment_vars={"SYSTEMD_COLORS": "0"}, + ) + .decode() + .splitlines() + ) def verify_keyboard_layout(layout: str) -> bool: @@ -44,10 +52,14 @@ def verify_x11_keyboard_layout(layout: str) -> bool: def get_kb_layout() -> str: try: - lines = SysCommand( - "localectl --no-pager status", - environment_vars={'SYSTEMD_COLORS': '0'} - ).decode().splitlines() + lines = ( + SysCommand( + "localectl --no-pager status", + environment_vars={"SYSTEMD_COLORS": "0"}, + ) + .decode() + .splitlines() + ) except Exception: return "" @@ -73,7 +85,7 @@ def set_kb_layout(locale: str) -> bool: return False try: - SysCommand(f'localectl set-keymap {locale}') + SysCommand(f"localectl set-keymap {locale}") except SysCallError as err: raise ServiceException(f"Unable to set locale '{locale}' for console: {err}") @@ -83,7 +95,11 @@ def set_kb_layout(locale: str) -> bool: def list_timezones() -> list[str]: - return SysCommand( - "timedatectl --no-pager list-timezones", - environment_vars={'SYSTEMD_COLORS': '0'} - ).decode().splitlines() + return ( + SysCommand( + "timedatectl --no-pager list-timezones", + environment_vars={"SYSTEMD_COLORS": "0"}, + ) + .decode() + .splitlines() + ) diff --git a/archinstall/lib/luks.py b/archinstall/lib/luks.py index 5a578aa6..45ec40e9 100644 --- a/archinstall/lib/luks.py +++ b/archinstall/lib/luks.py @@ -24,25 +24,25 @@ class Luks2: @property def mapper_dev(self) -> Path | None: if self.mapper_name: - return Path(f'/dev/mapper/{self.mapper_name}') + return Path(f"/dev/mapper/{self.mapper_name}") return None def isLuks(self) -> bool: try: - SysCommand(f'cryptsetup isLuks {self.luks_dev_path}') + SysCommand(f"cryptsetup isLuks {self.luks_dev_path}") return True except SysCallError: return False def erase(self) -> None: - debug(f'Erasing luks partition: {self.luks_dev_path}') - worker = SysCommandWorker(f'cryptsetup erase {self.luks_dev_path}') + debug(f"Erasing luks partition: {self.luks_dev_path}") + worker = SysCommandWorker(f"cryptsetup erase {self.luks_dev_path}") worker.poll() - worker.write(b'YES\n', line_ending=False) + worker.write(b"YES\n", line_ending=False) def __post_init__(self) -> None: if self.luks_dev_path is None: - raise ValueError('Partition must have a path set') + raise ValueError("Partition must have a path set") def __enter__(self) -> None: self.unlock(self.key_file) @@ -53,50 +53,56 @@ class Luks2: def _password_bytes(self) -> bytes: if not self.password: - raise ValueError('Password for luks2 device was not specified') + raise ValueError("Password for luks2 device was not specified") if isinstance(self.password, bytes): return self.password else: - return bytes(self.password.plaintext, 'UTF-8') + return bytes(self.password.plaintext, "UTF-8") def _get_passphrase_args( self, - key_file: Path | None = None + key_file: Path | None = None, ) -> tuple[list[str], bytes | None]: key_file = key_file or self.key_file if key_file: - return ['--key-file', str(key_file)], None + return ["--key-file", str(key_file)], None return [], self._password_bytes() def encrypt( self, key_size: int = 512, - hash_type: str = 'sha512', + hash_type: str = "sha512", iter_time: int = 10000, - key_file: Path | None = None + key_file: Path | None = None, ) -> Path | None: - debug(f'Luks2 encrypting: {self.luks_dev_path}') + debug(f"Luks2 encrypting: {self.luks_dev_path}") key_file_arg, passphrase = self._get_passphrase_args(key_file) cmd = [ - 'cryptsetup', - '--batch-mode', - '--verbose', - '--type', 'luks2', - '--pbkdf', 'argon2id', - '--hash', hash_type, - '--key-size', str(key_size), - '--iter-time', str(iter_time), + "cryptsetup", + "--batch-mode", + "--verbose", + "--type", + "luks2", + "--pbkdf", + "argon2id", + "--hash", + hash_type, + "--key-size", + str(key_size), + "--iter-time", + str(iter_time), *key_file_arg, - '--use-urandom', - 'luksFormat', str(self.luks_dev_path) + "--use-urandom", + "luksFormat", + str(self.luks_dev_path), ] - debug(f'cryptsetup format: {shlex.join(cmd)}') + debug(f"cryptsetup format: {shlex.join(cmd)}") try: result = run(cmd, input_data=passphrase) @@ -104,23 +110,23 @@ class Luks2: output = err.stdout.decode().rstrip() raise DiskError(f'Could not encrypt volume "{self.luks_dev_path}": {output}') - debug(f'cryptsetup luksFormat output: {result.stdout.decode().rstrip()}') + debug(f"cryptsetup luksFormat output: {result.stdout.decode().rstrip()}") self.key_file = key_file return key_file def _get_luks_uuid(self) -> str: - command = f'cryptsetup luksUUID {self.luks_dev_path}' + command = f"cryptsetup luksUUID {self.luks_dev_path}" try: return SysCommand(command).decode() except SysCallError as err: - info(f'Unable to get UUID for Luks device: {self.luks_dev_path}') + info(f"Unable to get UUID for Luks device: {self.luks_dev_path}") raise err def is_unlocked(self) -> bool: - return self.mapper_name is not None and Path(f'/dev/mapper/{self.mapper_name}').exists() + return self.mapper_name is not None and Path(f"/dev/mapper/{self.mapper_name}").exists() def unlock(self, key_file: Path | None = None) -> None: """ @@ -130,27 +136,29 @@ class Luks2: :param key_file: An alternative key file :type key_file: Path """ - debug(f'Unlocking luks2 device: {self.luks_dev_path}') + debug(f"Unlocking luks2 device: {self.luks_dev_path}") if not self.mapper_name: - raise ValueError('mapper name missing') + raise ValueError("mapper name missing") key_file_arg, passphrase = self._get_passphrase_args(key_file) cmd = [ - 'cryptsetup', 'open', + "cryptsetup", + "open", str(self.luks_dev_path), str(self.mapper_name), *key_file_arg, - '--type', 'luks2' + "--type", + "luks2", ] result = run(cmd, input_data=passphrase) - debug(f'cryptsetup open output: {result.stdout.decode().rstrip()}') + debug(f"cryptsetup open output: {result.stdout.decode().rstrip()}") if not self.mapper_dev or not self.mapper_dev.is_symlink(): - raise DiskError(f'Failed to open luks2 device: {self.luks_dev_path}') + raise DiskError(f"Failed to open luks2 device: {self.luks_dev_path}") def lock(self) -> None: umount(self.luks_dev_path) @@ -163,7 +171,7 @@ class Luks2: for child in lsblk_info.children: # Unmount the child location for mountpoint in child.mountpoints: - debug(f'Unmounting {mountpoint}') + debug(f"Unmounting {mountpoint}") umount(mountpoint, recursive=True) # And close it if possible. @@ -175,20 +183,20 @@ class Luks2: Routine to create keyfiles, so it can be moved elsewhere """ if self.mapper_name is None: - raise ValueError('Mapper name must be provided') + raise ValueError("Mapper name must be provided") # Once we store the key as ../xyzloop.key systemd-cryptsetup can # automatically load this key if we name the device to "xyzloop" - kf_path = Path(f'/etc/cryptsetup-keys.d/{self.mapper_name}.key') + kf_path = Path(f"/etc/cryptsetup-keys.d/{self.mapper_name}.key") key_file = target_path / kf_path.relative_to(kf_path.root) - crypttab_path = target_path / 'etc/crypttab' + crypttab_path = target_path / "etc/crypttab" if key_file.exists(): if not override: - info(f'Key file {key_file} already exists, keeping existing') + info(f"Key file {key_file} already exists, keeping existing") return else: - info(f'Key file {key_file} already exists, overriding') + info(f"Key file {key_file} already exists, overriding") key_file.parent.mkdir(parents=True, exist_ok=True) @@ -201,30 +209,30 @@ class Luks2: self._crypttab(crypttab_path, kf_path, options=["luks", "key-slot=1"]) def _add_key(self, key_file: Path) -> None: - debug(f'Adding additional key-file {key_file}') + debug(f"Adding additional key-file {key_file}") - command = f'cryptsetup -q -v luksAddKey {self.luks_dev_path} {key_file}' + command = f"cryptsetup -q -v luksAddKey {self.luks_dev_path} {key_file}" worker = SysCommandWorker(command) pw_injected = False while worker.is_alive(): - if b'Enter any existing passphrase' in worker and pw_injected is False: + if b"Enter any existing passphrase" in worker and pw_injected is False: worker.write(self._password_bytes()) pw_injected = True if worker.exit_code != 0: - raise DiskError(f'Could not add encryption key {key_file} to {self.luks_dev_path}: {worker.decode()}') + raise DiskError(f"Could not add encryption key {key_file} to {self.luks_dev_path}: {worker.decode()}") def _crypttab( self, crypttab_path: Path, key_file: Path, - options: list[str] + options: list[str], ) -> None: - debug(f'Adding crypttab entry for key {key_file}') + debug(f"Adding crypttab entry for key {key_file}") - with open(crypttab_path, 'a') as crypttab: - opt = ','.join(options) + with open(crypttab_path, "a") as crypttab: + opt = ",".join(options) uuid = self._get_luks_uuid() row = f"{self.mapper_name} UUID={uuid} {key_file} {opt}\n" crypttab.write(row) diff --git a/archinstall/lib/menu/__init__.py b/archinstall/lib/menu/__init__.py index 86fb05f3..280990c8 100644 --- a/archinstall/lib/menu/__init__.py +++ b/archinstall/lib/menu/__init__.py @@ -2,7 +2,7 @@ from .abstract_menu import AbstractMenu, AbstractSubMenu from .list_manager import ListManager __all__ = [ - 'AbstractMenu', - 'AbstractSubMenu', - 'ListManager', + "AbstractMenu", + "AbstractSubMenu", + "ListManager", ] diff --git a/archinstall/lib/menu/abstract_menu.py b/archinstall/lib/menu/abstract_menu.py index 2560ef58..2abbedf7 100644 --- a/archinstall/lib/menu/abstract_menu.py +++ b/archinstall/lib/menu/abstract_menu.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -CONFIG_KEY = '__config__' +CONFIG_KEY = "__config__" class AbstractMenu[ValueT]: @@ -26,7 +26,7 @@ class AbstractMenu[ValueT]: config: Any, auto_cursor: bool = True, allow_reset: bool = False, - reset_warning: str | None = None + reset_warning: str | None = None, ): self._menu_item_group = item_group self._config = config @@ -88,7 +88,7 @@ class AbstractMenu[ValueT]: found = True if not found: - raise ValueError(f'No selector found: {key}') + raise ValueError(f"No selector found: {key}") def disable_all(self) -> None: for item in self._menu_item_group.items: @@ -107,8 +107,8 @@ class AbstractMenu[ValueT]: allow_reset=self._allow_reset, reset_warning_msg=self._reset_warning, preview_style=PreviewStyle.RIGHT, - preview_size='auto', - preview_frame=FrameProperties('Info', FrameStyle.MAX), + preview_size="auto", + preview_frame=FrameProperties("Info", FrameStyle.MAX), ).run() match result.type_: @@ -132,14 +132,14 @@ class AbstractSubMenu[ValueT](AbstractMenu[ValueT]): item_group: MenuItemGroup, config: Any, auto_cursor: bool = True, - allow_reset: bool = False + allow_reset: bool = False, ): - back_text = f'{Chars.Right_arrow} ' + str(_('Back')) + back_text = f"{Chars.Right_arrow} " + str(_("Back")) item_group.add_item(MenuItem(text=back_text)) super().__init__( item_group, config=config, auto_cursor=auto_cursor, - allow_reset=allow_reset + allow_reset=allow_reset, ) diff --git a/archinstall/lib/menu/list_manager.py b/archinstall/lib/menu/list_manager.py index 8c898896..3618167e 100644 --- a/archinstall/lib/menu/list_manager.py +++ b/archinstall/lib/menu/list_manager.py @@ -21,7 +21,7 @@ class ListManager[ValueT]: entries: list[ValueT], base_actions: list[str], sub_menu_actions: list[str], - prompt: str | None = None + prompt: str | None = None, ): """ :param prompt: Text which will appear at the header @@ -42,9 +42,9 @@ class ListManager[ValueT]: self._prompt = prompt - self._separator = '' - self._confirm_action = str(_('Confirm and exit')) - self._cancel_action = str(_('Cancel')) + self._separator = "" + self._confirm_action = str(_("Confirm and exit")) + self._cancel_action = str(_("Cancel")) self._terminate_actions = [self._confirm_action, self._cancel_action] self._base_actions = base_actions @@ -67,12 +67,12 @@ class ListManager[ValueT]: while True: group = MenuHelper( data=self._data, - additional_options=additional_options + additional_options=additional_options, ).create_menu_group() prompt = None if self._prompt is not None: - prompt = f'{self._prompt}\n\n' + prompt = f"{self._prompt}\n\n" prompt = None @@ -81,14 +81,14 @@ class ListManager[ValueT]: header=prompt, search_enabled=False, allow_skip=False, - alignment=Alignment.CENTER + alignment=Alignment.CENTER, ).run() match result.type_: case ResultType.Selection: value = result.get_value() case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") if value in self._base_actions: value = cast(str, value) @@ -114,21 +114,21 @@ class ListManager[ValueT]: items = [MenuItem(o, value=o) for o in options] group = MenuItemGroup(items, sort_items=False) - header = f'{self.selected_action_display(entry)}\n' + header = f"{self.selected_action_display(entry)}\n" result = SelectMenu[str]( group, header=header, search_enabled=False, allow_skip=False, - alignment=Alignment.CENTER + alignment=Alignment.CENTER, ).run() match result.type_: case ResultType.Selection: value = result.get_value() case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") if value != self._cancel_action: self._data = self.handle_action(value, entry, self._data) @@ -138,14 +138,14 @@ class ListManager[ValueT]: this will return the value to be displayed in the "Select an action for '{}'" string """ - raise NotImplementedError('Please implement me in the child class') + raise NotImplementedError("Please implement me in the child class") def handle_action(self, action: str, entry: ValueT | None, data: list[ValueT]) -> list[ValueT]: """ this function is called when a base action or a specific action for an entry is triggered """ - raise NotImplementedError('Please implement me in the child class') + raise NotImplementedError("Please implement me in the child class") def filter_options(self, selection: ValueT, options: list[str]) -> list[str]: """ diff --git a/archinstall/lib/menu/menu_helper.py b/archinstall/lib/menu/menu_helper.py index 79ffb5ea..82a7d69e 100644 --- a/archinstall/lib/menu/menu_helper.py +++ b/archinstall/lib/menu/menu_helper.py @@ -8,9 +8,9 @@ class MenuHelper: def __init__( self, data: list[Any], - additional_options: list[str] = [] + additional_options: list[str] = [], ) -> None: - self._separator = '' + self._separator = "" self._data = data self._additional_options = additional_options @@ -39,10 +39,10 @@ class MenuHelper: if data: table = FormattedOutput.as_table(data) - rows = table.split('\n') + rows = table.split("\n") # these are the header rows of the table - display_data = {f'{rows[0]}': None, f'{rows[1]}': None} + display_data = {f"{rows[0]}": None, f"{rows[1]}": None} for row, entry in zip(rows[2:], data): display_data[row] = entry diff --git a/archinstall/lib/mirrors.py b/archinstall/lib/mirrors.py index 5e2d4c1d..ff9c13d4 100644 --- a/archinstall/lib/mirrors.py +++ b/archinstall/lib/mirrors.py @@ -35,16 +35,16 @@ if TYPE_CHECKING: class CustomMirrorRepositoriesList(ListManager[CustomRepository]): def __init__(self, custom_repositories: list[CustomRepository]): self._actions = [ - str(_('Add a custom repository')), - str(_('Change custom repository')), - str(_('Delete custom repository')) + str(_("Add a custom repository")), + str(_("Change custom repository")), + str(_("Delete custom repository")), ] super().__init__( custom_repositories, [self._actions[0]], self._actions[1:], - '' + "", ) @override @@ -56,7 +56,7 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): self, action: str, entry: CustomRepository | None, - data: list[CustomRepository] + data: list[CustomRepository], ) -> list[CustomRepository]: if action == self._actions[0]: # add new_repo = self._add_custom_repository() @@ -75,10 +75,10 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): def _add_custom_repository(self, preset: CustomRepository | None = None) -> CustomRepository | None: edit_result = EditMenu( - str(_('Repository name')), + str(_("Repository name")), alignment=Alignment.CENTER, allow_skip=True, - default_text=preset.name if preset else None + default_text=preset.name if preset else None, ).input() match edit_result.type_: @@ -87,16 +87,16 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): case ResultType.Skip: return preset case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") - header = f'{_("Name")}: {name}' + header = f"{_('Name')}: {name}" edit_result = EditMenu( - str(_('Url')), + str(_("Url")), header=header, alignment=Alignment.CENTER, allow_skip=True, - default_text=preset.url if preset else None + default_text=preset.url if preset else None, ).input() match edit_result.type_: @@ -105,10 +105,10 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): case ResultType.Skip: return preset case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") - header += f'\n{_("Url")}: {url}\n' - prompt = f'{header}\n' + str(_('Select signature check')) + header += f"\n{_('Url')}: {url}\n" + prompt = f"{header}\n" + str(_("Select signature check")) sign_chk_items = [MenuItem(s.value, value=s.value) for s in SignCheck] group = MenuItemGroup(sign_chk_items, sort_items=False) @@ -120,17 +120,17 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): group, header=prompt, alignment=Alignment.CENTER, - allow_skip=False + allow_skip=False, ).run() match result.type_: case ResultType.Selection: sign_check = SignCheck(result.get_value()) case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") - header += f'{_("Signature check")}: {sign_check.value}\n' - prompt = f'{header}\n' + 'Select signature option' + header += f"{_('Signature check')}: {sign_check.value}\n" + prompt = f"{header}\n" + "Select signature option" sign_opt_items = [MenuItem(s.value, value=s.value) for s in SignOption] group = MenuItemGroup(sign_opt_items, sort_items=False) @@ -142,14 +142,14 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): group, header=prompt, alignment=Alignment.CENTER, - allow_skip=False + allow_skip=False, ).run() match result.type_: case ResultType.Selection: sign_opt = SignOption(result.get_value()) case _: - raise ValueError('Unhandled return type') + raise ValueError("Unhandled return type") return CustomRepository(name, url, sign_check, sign_opt) @@ -157,16 +157,16 @@ class CustomMirrorRepositoriesList(ListManager[CustomRepository]): class CustomMirrorServersList(ListManager[CustomServer]): def __init__(self, custom_servers: list[CustomServer]): self._actions = [ - str(_('Add a custom server')), - str(_('Change custom server')), - str(_('Delete custom server')) + str(_("Add a custom server")), + str(_("Change custom server")), + str(_("Delete custom server")), ] super().__init__( custom_servers, [self._actions[0]], self._actions[1:], - '' + "", ) @override @@ -178,7 +178,7 @@ class CustomMirrorServersList(ListManager[CustomServer]): self, action: str, entry: CustomServer | None, - data: list[CustomServer] + data: list[CustomServer], ) -> list[CustomServer]: if action == self._actions[0]: # add new_server = self._add_custom_server() @@ -197,10 +197,10 @@ class CustomMirrorServersList(ListManager[CustomServer]): def _add_custom_server(self, preset: CustomServer | None = None) -> CustomServer | None: edit_result = EditMenu( - str(_('Server url')), + str(_("Server url")), alignment=Alignment.CENTER, allow_skip=True, - default_text=preset.url if preset else None + default_text=preset.url if preset else None, ).input() match edit_result.type_: @@ -216,7 +216,7 @@ class CustomMirrorServersList(ListManager[CustomServer]): class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): def __init__( self, - preset: MirrorConfiguration | None = None + preset: MirrorConfiguration | None = None, ): if preset: self._mirror_config = preset @@ -229,60 +229,60 @@ class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): super().__init__( self._item_group, config=self._mirror_config, - allow_reset=True + allow_reset=True, ) def _define_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Select regions')), + text=str(_("Select regions")), action=select_mirror_regions, value=self._mirror_config.mirror_regions, preview_action=self._prev_regions, - key='mirror_regions' + key="mirror_regions", ), MenuItem( - text=str(_('Add custom servers')), + text=str(_("Add custom servers")), action=add_custom_mirror_servers, value=self._mirror_config.custom_servers, preview_action=self._prev_custom_servers, - key='custom_servers' + key="custom_servers", ), MenuItem( - text=str(_('Optional repositories')), + text=str(_("Optional repositories")), action=select_optional_repositories, value=[], preview_action=self._prev_additional_repos, - key='optional_repositories' + key="optional_repositories", ), MenuItem( - text=str(_('Add custom repository')), + text=str(_("Add custom repository")), action=select_custom_mirror, value=self._mirror_config.custom_repositories, preview_action=self._prev_custom_mirror, - key='custom_repositories' - ) + key="custom_repositories", + ), ] def _prev_regions(self, item: MenuItem) -> str | None: regions = item.get_value() - output = '' + output = "" for region in regions: - output += f'{region.name}\n' + output += f"{region.name}\n" for url in region.urls: - output += f' - {url}\n' + output += f" - {url}\n" - output += '\n' + output += "\n" return output def _prev_additional_repos(self, item: MenuItem) -> str | None: if item.value: repositories: list[Repository] = item.value - repos = ', '.join([repo.value for repo in repositories]) - return f'{_("Additional repositories")}: {repos}' + repos = ", ".join([repo.value for repo in repositories]) + return f"{_('Additional repositories')}: {repos}" return None def _prev_custom_mirror(self, item: MenuItem) -> str | None: @@ -298,7 +298,7 @@ class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): return None custom_servers: list[CustomServer] = item.value - output = '\n'.join([server.url for server in custom_servers]) + output = "\n".join([server.url for server in custom_servers]) return output.strip() @override @@ -308,7 +308,7 @@ class MirrorMenu(AbstractSubMenu[MirrorConfiguration]): def select_mirror_regions(preset: list[MirrorRegion]) -> list[MirrorRegion]: - Tui.print(str(_('Loading mirror regions...')), clear_screen=True) + Tui.print(str(_("Loading mirror regions...")), clear_screen=True) mirror_list_handler.load_mirrors() available_regions = mirror_list_handler.get_mirror_regions() @@ -326,7 +326,7 @@ def select_mirror_regions(preset: list[MirrorRegion]) -> list[MirrorRegion]: result = SelectMenu[MirrorRegion]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Mirror regions'))), + frame=FrameProperties.min(str(_("Mirror regions"))), allow_reset=True, allow_skip=True, multi=True, @@ -368,10 +368,10 @@ def select_optional_repositories(preset: list[Repository]) -> list[Repository]: result = SelectMenu[Repository]( group, alignment=Alignment.CENTER, - frame=FrameProperties.min('Additional repositories'), + frame=FrameProperties.min("Additional repositories"), allow_reset=True, allow_skip=True, - multi=True + multi=True, ).run() match result.type_: @@ -386,7 +386,7 @@ def select_optional_repositories(preset: list[Repository]) -> list[Repository]: class MirrorListHandler: def __init__( self, - local_mirrorlist: Path = Path('/etc/pacman.d/mirrorlist'), + local_mirrorlist: Path = Path("/etc/pacman.d/mirrorlist"), ) -> None: self._local_mirrorlist = local_mirrorlist self._status_mappings: dict[str, list[MirrorStatusEntryV3]] | None = None @@ -411,6 +411,7 @@ class MirrorListHandler: def load_mirrors(self) -> None: from .args import arch_config_handler + if arch_config_handler.args.offline: self.load_local_mirrors() else: @@ -427,14 +428,14 @@ class MirrorListHandler: self._status_mappings = self._parse_remote_mirror_list(mirrorlist) return True except Exception as e: - debug(f'Error while fetching mirror list: {e}') + debug(f"Error while fetching mirror list: {e}") time.sleep(attempt_nr + 1) - debug('Unable to fetch mirror list remotely, falling back to local mirror list') + debug("Unable to fetch mirror list remotely, falling back to local mirror list") return False def load_local_mirrors(self) -> None: - with self._local_mirrorlist.open('r') as fp: + with self._local_mirrorlist.open("r") as fp: mirrorlist = fp.read() self._status_mappings = self._parse_locale_mirrors(mirrorlist) @@ -450,13 +451,15 @@ class MirrorListHandler: for mirror in mirror_status.urls: # We filter out mirrors that have bad criteria values - if any([ - mirror.active is False, # Disabled by mirror-list admins - mirror.last_sync is None, # Has not synced recently - # mirror.score (error rate) over time reported from backend: - # https://github.com/archlinux/archweb/blob/31333d3516c91db9a2f2d12260bd61656c011fd1/mirrors/utils.py#L111C22-L111C66 - (mirror.score is None or mirror.score >= 100), - ]): + if any( + [ + mirror.active is False, # Disabled by mirror-list admins + mirror.last_sync is None, # Has not synced recently + # mirror.score (error rate) over time reported from backend: + # https://github.com/archlinux/archweb/blob/31333d3516c91db9a2f2d12260bd61656c011fd1/mirrors/utils.py#L111C22-L111C66 + (mirror.score is None or mirror.score >= 100), + ] + ): continue if mirror.country == "": @@ -466,13 +469,12 @@ class MirrorListHandler: # So we have to assume world-wide mirror.country = "Worldwide" - if mirror.url.startswith('http'): + if mirror.url.startswith("http"): sorting_placeholder.setdefault(mirror.country, []).append(mirror) - sorted_by_regions: dict[str, list[MirrorStatusEntryV3]] = dict({ - region: unsorted_mirrors - for region, unsorted_mirrors in sorted(sorting_placeholder.items(), key=lambda item: item[0]) - }) + sorted_by_regions: dict[str, list[MirrorStatusEntryV3]] = dict( + {region: unsorted_mirrors for region, unsorted_mirrors in sorted(sorting_placeholder.items(), key=lambda item: item[0])} + ) return sorted_by_regions @@ -484,35 +486,35 @@ class MirrorListHandler: mirror_list: dict[str, list[MirrorStatusEntryV3]] = {} - current_region = '' + current_region = "" for line in lines: line = line.strip() - if line.startswith('## '): - current_region = line.replace('## ', '').strip() + if line.startswith("## "): + current_region = line.replace("## ", "").strip() mirror_list.setdefault(current_region, []) - if line.startswith('Server = '): + if line.startswith("Server = "): if not current_region: - current_region = 'Local' + current_region = "Local" mirror_list.setdefault(current_region, []) - url = line.removeprefix('Server = ') + url = line.removeprefix("Server = ") mirror_entry = MirrorStatusEntryV3( - url=url.removesuffix('$repo/os/$arch'), + url=url.removesuffix("$repo/os/$arch"), protocol=urllib.parse.urlparse(url).scheme, active=True, - country=current_region or 'Worldwide', + country=current_region or "Worldwide", # The following values are normally populated by # archlinux.org mirror-list endpoint, and can't be known # from just the local mirror-list file. - country_code='WW', + country_code="WW", isos=True, ipv4=True, ipv6=True, - details='Locally defined mirror', + details="Locally defined mirror", ) mirror_list[current_region].append(mirror_entry) diff --git a/archinstall/lib/models/__init__.py b/archinstall/lib/models/__init__.py index 00c74890..5b58f2a0 100644 --- a/archinstall/lib/models/__init__.py +++ b/archinstall/lib/models/__init__.py @@ -35,46 +35,46 @@ from .profile_model import ProfileConfiguration from .users import PasswordStrength, User __all__ = [ - 'Audio', - 'AudioConfiguration', - 'BDevice', - 'Bootloader', - 'CustomRepository', - 'DeviceGeometry', - 'DeviceModification', - 'DiskEncryption', - 'DiskLayoutConfiguration', - 'DiskLayoutType', - 'EncryptionType', - 'Fido2Device', - 'FilesystemType', - 'LocalPackage', - 'LocaleConfiguration', - 'LsblkInfo', - 'LvmConfiguration', - 'LvmLayoutType', - 'LvmVolume', - 'LvmVolumeGroup', - 'LvmVolumeStatus', - 'MirrorConfiguration', - 'MirrorRegion', - 'ModificationStatus', - 'NetworkConfiguration', - 'Nic', - 'NicType', - 'PackageSearch', - 'PackageSearchResult', - 'PartitionFlag', - 'PartitionModification', - 'PartitionTable', - 'PartitionType', - 'PasswordStrength', - 'ProfileConfiguration', - 'Repository', - 'SectorSize', - 'Size', - 'SubvolumeModification', - 'Unit', - 'User', - '_DeviceInfo', + "Audio", + "AudioConfiguration", + "BDevice", + "Bootloader", + "CustomRepository", + "DeviceGeometry", + "DeviceModification", + "DiskEncryption", + "DiskLayoutConfiguration", + "DiskLayoutType", + "EncryptionType", + "Fido2Device", + "FilesystemType", + "LocalPackage", + "LocaleConfiguration", + "LsblkInfo", + "LvmConfiguration", + "LvmLayoutType", + "LvmVolume", + "LvmVolumeGroup", + "LvmVolumeStatus", + "MirrorConfiguration", + "MirrorRegion", + "ModificationStatus", + "NetworkConfiguration", + "Nic", + "NicType", + "PackageSearch", + "PackageSearchResult", + "PartitionFlag", + "PartitionModification", + "PartitionTable", + "PartitionType", + "PasswordStrength", + "ProfileConfiguration", + "Repository", + "SectorSize", + "Size", + "SubvolumeModification", + "Unit", + "User", + "_DeviceInfo", ] diff --git a/archinstall/lib/models/audio_configuration.py b/archinstall/lib/models/audio_configuration.py index f8f37553..ac4da30f 100644 --- a/archinstall/lib/models/audio_configuration.py +++ b/archinstall/lib/models/audio_configuration.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: class Audio(StrEnum): - NO_AUDIO = 'No audio server' + NO_AUDIO = "No audio server" PIPEWIRE = auto() PULSEAUDIO = auto() @@ -21,20 +21,20 @@ class AudioConfiguration: def json(self) -> dict[str, str]: return { - 'audio': self.audio.value + "audio": self.audio.value, } @staticmethod - def parse_arg(arg: dict[str, str]) -> 'AudioConfiguration': + def parse_arg(arg: dict[str, str]) -> "AudioConfiguration": return AudioConfiguration( - Audio(arg['audio']) + Audio(arg["audio"]), ) def install_audio_config( self, - installation: 'Installer' + installation: "Installer", ) -> None: - info(f'Installing audio server: {self.audio.name}') + info(f"Installing audio server: {self.audio.name}") from ...default_profiles.applications.pipewire import PipewireProfile @@ -46,7 +46,7 @@ class AudioConfiguration: if self.audio != Audio.NO_AUDIO: if SysInfo.requires_sof_fw(): - installation.add_additional_packages('sof-firmware') + installation.add_additional_packages("sof-firmware") if SysInfo.requires_alsa_fw(): - installation.add_additional_packages('alsa-firmware') + installation.add_additional_packages("alsa-firmware") diff --git a/archinstall/lib/models/bootloader.py b/archinstall/lib/models/bootloader.py index cea67d80..80ca5911 100644 --- a/archinstall/lib/models/bootloader.py +++ b/archinstall/lib/models/bootloader.py @@ -8,10 +8,10 @@ from ..output import warn class Bootloader(Enum): - Systemd = 'Systemd-boot' - Grub = 'Grub' - Efistub = 'Efistub' - Limine = 'Limine' + Systemd = "Systemd-boot" + Grub = "Grub" + Efistub = "Efistub" + Limine = "Limine" def has_uki_support(self) -> bool: match self: @@ -40,7 +40,7 @@ class Bootloader(Enum): bootloader = bootloader.capitalize() if bootloader not in cls.values(): - values = ', '.join(cls.values()) + values = ", ".join(cls.values()) warn(f'Invalid bootloader value "{bootloader}". Allowed values: {values}') sys.exit(1) return Bootloader(bootloader) diff --git a/archinstall/lib/models/device_model.py b/archinstall/lib/models/device_model.py index 7ceaebbd..13f12b3f 100644 --- a/archinstall/lib/models/device_model.py +++ b/archinstall/lib/models/device_model.py @@ -23,22 +23,22 @@ if TYPE_CHECKING: _: Callable[[str], DeferredTranslation] -ENC_IDENTIFIER = 'ainst' +ENC_IDENTIFIER = "ainst" class DiskLayoutType(Enum): - Default = 'default_layout' - Manual = 'manual_partitioning' - Pre_mount = 'pre_mounted_config' + Default = "default_layout" + Manual = "manual_partitioning" + Pre_mount = "pre_mounted_config" def display_msg(self) -> str: match self: case DiskLayoutType.Default: - return str(_('Use a best-effort default partition layout')) + return str(_("Use a best-effort default partition layout")) case DiskLayoutType.Manual: - return str(_('Manual Partitioning')) + return str(_("Manual Partitioning")) case DiskLayoutType.Pre_mount: - return str(_('Pre-mounted configuration')) + return str(_("Pre-mounted configuration")) class _DiskLayoutConfigurationSerialization(TypedDict): @@ -60,17 +60,17 @@ class DiskLayoutConfiguration: def json(self) -> _DiskLayoutConfigurationSerialization: if self.config_type == DiskLayoutType.Pre_mount: return { - 'config_type': self.config_type.value, - 'mountpoint': str(self.mountpoint) + "config_type": self.config_type.value, + "mountpoint": str(self.mountpoint), } else: config: _DiskLayoutConfigurationSerialization = { - 'config_type': self.config_type.value, - 'device_modifications': [mod.json() for mod in self.device_modifications], + "config_type": self.config_type.value, + "device_modifications": [mod.json() for mod in self.device_modifications], } if self.lvm_config: - config['lvm_config'] = self.lvm_config.json() + config["lvm_config"] = self.lvm_config.json() return config @@ -79,19 +79,19 @@ class DiskLayoutConfiguration: from archinstall.lib.disk.device_handler import device_handler device_modifications: list[DeviceModification] = [] - config_type = disk_config.get('config_type', None) + config_type = disk_config.get("config_type", None) if not config_type: - raise ValueError('Missing disk layout configuration: config_type') + raise ValueError("Missing disk layout configuration: config_type") config = DiskLayoutConfiguration( config_type=DiskLayoutType(config_type), - device_modifications=device_modifications + device_modifications=device_modifications, ) if config_type == DiskLayoutType.Pre_mount.value: - if not (mountpoint := disk_config.get('mountpoint')): - raise ValueError('Must set a mountpoint when layout type is pre-mount') + if not (mountpoint := disk_config.get("mountpoint")): + raise ValueError("Must set a mountpoint when layout type is pre-mount") path = Path(str(mountpoint)) @@ -102,8 +102,8 @@ class DiskLayoutConfiguration: return config - for entry in disk_config.get('device_modifications', []): - device_path = Path(entry['device']) if entry.get('device', None) else None + for entry in disk_config.get("device_modifications", []): + device_path = Path(entry["device"]) if entry.get("device", None) else None if not device_path: continue @@ -114,32 +114,29 @@ class DiskLayoutConfiguration: continue device_modification = DeviceModification( - wipe=entry.get('wipe', False), - device=device + wipe=entry.get("wipe", False), + device=device, ) device_partitions: list[PartitionModification] = [] - for partition in entry.get('partitions', []): - flags = [ - flag for f in partition.get('flags', []) - if (flag := PartitionFlag.from_string(f)) - ] + for partition in entry.get("partitions", []): + flags = [flag for f in partition.get("flags", []) if (flag := PartitionFlag.from_string(f))] device_partition = PartitionModification( - status=ModificationStatus(partition['status']), - fs_type=FilesystemType(partition['fs_type']) if partition.get('fs_type') else None, - start=Size.parse_args(partition['start']), - length=Size.parse_args(partition['size']), - mount_options=partition['mount_options'], - mountpoint=Path(partition['mountpoint']) if partition['mountpoint'] else None, - dev_path=Path(partition['dev_path']) if partition['dev_path'] else None, - type=PartitionType(partition['type']), + status=ModificationStatus(partition["status"]), + fs_type=FilesystemType(partition["fs_type"]) if partition.get("fs_type") else None, + start=Size.parse_args(partition["start"]), + length=Size.parse_args(partition["size"]), + mount_options=partition["mount_options"], + mountpoint=Path(partition["mountpoint"]) if partition["mountpoint"] else None, + dev_path=Path(partition["dev_path"]) if partition["dev_path"] else None, + type=PartitionType(partition["type"]), flags=flags, - btrfs_subvols=SubvolumeModification.parse_args(partition.get('btrfs', [])), + btrfs_subvols=SubvolumeModification.parse_args(partition.get("btrfs", [])), ) # special 'invisible' attr to internally identify the part mod - device_partition._obj_id = partition['obj_id'] + device_partition._obj_id = partition["obj_id"] device_partitions.append(device_partition) device_modification.partitions = device_partitions @@ -148,59 +145,47 @@ class DiskLayoutConfiguration: for dev_mod in device_modifications: dev_mod.partitions.sort(key=lambda p: (not p.is_delete(), p.start)) - non_delete_partitions = [ - part_mod for part_mod in dev_mod.partitions - if not part_mod.is_delete() - ] + non_delete_partitions = [part_mod for part_mod in dev_mod.partitions if not part_mod.is_delete()] if not non_delete_partitions: continue first = non_delete_partitions[0] if first.status == ModificationStatus.Create and not first.start.is_valid_start(): - raise ValueError('First partition must start at no less than 1 MiB') + raise ValueError("First partition must start at no less than 1 MiB") for i, current_partition in enumerate(non_delete_partitions[1:], start=1): previous_partition = non_delete_partitions[i - 1] - if ( - current_partition.status == ModificationStatus.Create - and current_partition.start < previous_partition.end - ): - raise ValueError('Partitions overlap') + if current_partition.status == ModificationStatus.Create and current_partition.start < previous_partition.end: + raise ValueError("Partitions overlap") - create_partitions = [ - part_mod for part_mod in non_delete_partitions - if part_mod.status == ModificationStatus.Create - ] + create_partitions = [part_mod for part_mod in non_delete_partitions if part_mod.status == ModificationStatus.Create] if not create_partitions: continue for part in create_partitions: - if ( - part.start != part.start.align() - or part.length != part.length.align() - ): - raise ValueError('Partition is misaligned') + if part.start != part.start.align() or part.length != part.length.align(): + raise ValueError("Partition is misaligned") last = create_partitions[-1] total_size = dev_mod.device.device_info.total_size if dev_mod.using_gpt(device_handler.partition_table): if last.end > total_size.gpt_end(): - raise ValueError('Partition overlaps backup GPT header') + raise ValueError("Partition overlaps backup GPT header") elif last.end > total_size.align(): - raise ValueError('Partition too large for device') + raise ValueError("Partition too large for device") # Parse LVM configuration from settings - if (lvm_arg := disk_config.get('lvm_config', None)) is not None: + if (lvm_arg := disk_config.get("lvm_config", None)) is not None: config.lvm_config = LvmConfiguration.parse_arg(lvm_arg, config) return config class PartitionTable(Enum): - GPT = 'gpt' - MBR = 'msdos' + GPT = "gpt" + MBR = "msdos" def is_gpt(self) -> bool: return self == PartitionTable.GPT @@ -214,31 +199,31 @@ class PartitionTable(Enum): class Units(Enum): - BINARY = 'binary' - DECIMAL = 'decimal' + BINARY = "binary" + DECIMAL = "decimal" class Unit(Enum): B = 1 # byte - kB = 1000 ** 1 # kilobyte - MB = 1000 ** 2 # megabyte - GB = 1000 ** 3 # gigabyte - TB = 1000 ** 4 # terabyte - PB = 1000 ** 5 # petabyte - EB = 1000 ** 6 # exabyte - ZB = 1000 ** 7 # zettabyte - YB = 1000 ** 8 # yottabyte + kB = 1000**1 # kilobyte + MB = 1000**2 # megabyte + GB = 1000**3 # gigabyte + TB = 1000**4 # terabyte + PB = 1000**5 # petabyte + EB = 1000**6 # exabyte + ZB = 1000**7 # zettabyte + YB = 1000**8 # yottabyte - KiB = 1024 ** 1 # kibibyte - MiB = 1024 ** 2 # mebibyte - GiB = 1024 ** 3 # gibibyte - TiB = 1024 ** 4 # tebibyte - PiB = 1024 ** 5 # pebibyte - EiB = 1024 ** 6 # exbibyte - ZiB = 1024 ** 7 # zebibyte - YiB = 1024 ** 8 # yobibyte + KiB = 1024**1 # kibibyte + MiB = 1024**2 # mebibyte + GiB = 1024**3 # gibibyte + TiB = 1024**4 # tebibyte + PiB = 1024**5 # pebibyte + EiB = 1024**6 # exbibyte + ZiB = 1024**7 # zebibyte + YiB = 1024**8 # yobibyte - sectors = 'sectors' # size in sector + sectors = "sectors" # size in sector @staticmethod def get_all_units() -> list[str]: @@ -246,11 +231,11 @@ class Unit(Enum): @staticmethod def get_si_units() -> list[Unit]: - return [u for u in Unit if 'i' not in u.name and u.name != 'sectors'] + return [u for u in Unit if "i" not in u.name and u.name != "sectors"] @staticmethod def get_binary_units() -> list[Unit]: - return [u for u in Unit if 'i' in u.name or u.name == 'B'] + return [u for u in Unit if "i" in u.name or u.name == "B"] class _SectorSizeSerialization(TypedDict): @@ -266,7 +251,7 @@ class SectorSize: def __post_init__(self) -> None: match self.unit: case Unit.sectors: - raise ValueError('Unit type sector not allowed for SectorSize') + raise ValueError("Unit type sector not allowed for SectorSize") @staticmethod def default() -> SectorSize: @@ -274,15 +259,15 @@ class SectorSize: def json(self) -> _SectorSizeSerialization: return { - 'value': self.value, - 'unit': self.unit.name, + "value": self.value, + "unit": self.unit.name, } @classmethod def parse_args(cls, arg: _SectorSizeSerialization) -> SectorSize: return SectorSize( - arg['value'], - Unit[arg['unit']] + arg["value"], + Unit[arg["unit"]], ) def normalize(self) -> int: @@ -306,32 +291,32 @@ class Size: def __post_init__(self) -> None: if not isinstance(self.sector_size, SectorSize): - raise ValueError('sector size must be of type SectorSize') + raise ValueError("sector size must be of type SectorSize") def json(self) -> _SizeSerialization: return { - 'value': self.value, - 'unit': self.unit.name, - 'sector_size': self.sector_size.json() + "value": self.value, + "unit": self.unit.name, + "sector_size": self.sector_size.json(), } @classmethod def parse_args(cls, size_arg: _SizeSerialization) -> Size: - sector_size = size_arg['sector_size'] + sector_size = size_arg["sector_size"] return Size( - size_arg['value'], - Unit[size_arg['unit']], + size_arg["value"], + Unit[size_arg["unit"]], SectorSize.parse_args(sector_size), ) def convert( self, target_unit: Unit, - sector_size: SectorSize | None = None + sector_size: SectorSize | None = None, ) -> Size: if target_unit == Unit.sectors and sector_size is None: - raise ValueError('If target has unit sector, a sector size must be provided') + raise ValueError("If target has unit sector, a sector size must be provided") if self.unit == target_unit: return self @@ -350,20 +335,20 @@ class Size: def as_text(self) -> str: return self.format_size( self.unit, - self.sector_size + self.sector_size, ) def format_size( self, target_unit: Unit, sector_size: SectorSize | None = None, - include_unit: bool = True + include_unit: bool = True, ) -> str: target_size = self.convert(target_unit, sector_size) if include_unit: - return f'{target_size.value} {target_unit.name}' - return f'{target_size.value}' + return f"{target_size.value} {target_unit.name}" + return f"{target_size.value}" def binary_unit_highest(self, include_unit: bool = True) -> str: binary_units = Unit.get_binary_units() @@ -380,13 +365,13 @@ class Size: formatted_size = f"{size:.1f}" - if formatted_size.endswith('.0'): + if formatted_size.endswith(".0"): formatted_size = formatted_size[:-2] if not include_unit: return formatted_size - return f'{formatted_size} {unit.name}' + return f"{formatted_size} {unit.name}" def si_unit_highest(self, include_unit: bool = True) -> str: si_units = Unit.get_si_units() @@ -399,8 +384,8 @@ class Size: si_value = max(filtered, key=lambda x: x.unit.value) if include_unit: - return f'{si_value.value} {si_value.unit.name}' - return f'{si_value.value}' + return f"{si_value.value} {si_value.unit.name}" + return f"{si_value.value}" def format_highest(self, include_unit: bool = True, units: Units = Units.BINARY) -> str: if units == Units.BINARY: @@ -465,8 +450,8 @@ class Size: class BtrfsMountOption(Enum): - compress = 'compress=zstd' - nodatacow = 'nodatacow' + compress = "compress=zstd" + nodatacow = "nodatacow" @dataclass @@ -501,18 +486,18 @@ class _PartitionInfo: end = self.start + self.length part_info = { - 'Name': self.name, - 'Type': self.type.value, - 'Filesystem': self.fs_type.value if self.fs_type else str(_('Unknown')), - 'Path': str(self.path), - 'Start': self.start.format_size(Unit.sectors, self.sector_size, include_unit=False), - 'End': end.format_size(Unit.sectors, self.sector_size, include_unit=False), - 'Size': self.length.format_highest(), - 'Flags': ', '.join([f.description for f in self.flags]) + "Name": self.name, + "Type": self.type.value, + "Filesystem": self.fs_type.value if self.fs_type else str(_("Unknown")), + "Path": str(self.path), + "Start": self.start.format_size(Unit.sectors, self.sector_size, include_unit=False), + "End": end.format_size(Unit.sectors, self.sector_size, include_unit=False), + "Size": self.length.format_highest(), + "Flags": ", ".join([f.description for f in self.flags]), } if self.btrfs_subvol_infos: - part_info['Btrfs vol.'] = f'{len(self.btrfs_subvol_infos)} subvolumes' + part_info["Btrfs vol."] = f"{len(self.btrfs_subvol_infos)} subvolumes" return part_info @@ -522,7 +507,7 @@ class _PartitionInfo: partition: Partition, lsblk_info: LsblkInfo, fs_type: FilesystemType | None, - btrfs_subvol_infos: list[_BtrfsSubvolumeInfo] = [] + btrfs_subvol_infos: list[_BtrfsSubvolumeInfo] = [], ) -> _PartitionInfo: partition_type = PartitionType.get_type_from_code(partition.type) flags = [f for f in PartitionFlag if partition.getFlag(f.flag_id)] @@ -530,13 +515,13 @@ class _PartitionInfo: start = Size( partition.geometry.start, Unit.sectors, - SectorSize(partition.disk.device.sectorSize, Unit.B) + SectorSize(partition.disk.device.sectorSize, Unit.B), ) length = Size( - int(partition.getLength(unit='B')), + int(partition.getLength(unit="B")), Unit.B, - SectorSize(partition.disk.device.sectorSize, Unit.B) + SectorSize(partition.disk.device.sectorSize, Unit.B), ) return _PartitionInfo( @@ -553,7 +538,7 @@ class _PartitionInfo: uuid=lsblk_info.uuid, disk=partition.disk, mountpoints=lsblk_info.mountpoints, - btrfs_subvol_infos=btrfs_subvol_infos + btrfs_subvol_infos=btrfs_subvol_infos, ) @@ -571,24 +556,24 @@ class _DeviceInfo: def table_data(self) -> dict[str, str | int | bool]: total_free_space = sum([region.get_length(unit=Unit.MiB) for region in self.free_space_regions]) return { - 'Model': self.model, - 'Path': str(self.path), - 'Type': self.type, - 'Size': self.total_size.format_highest(), - 'Free space': int(total_free_space), - 'Sector size': self.sector_size.value, - 'Read only': self.read_only + "Model": self.model, + "Path": str(self.path), + "Type": self.type, + "Size": self.total_size.format_highest(), + "Free space": int(total_free_space), + "Sector size": self.sector_size.value, + "Read only": self.read_only, } @classmethod def from_disk(cls, disk: Disk) -> _DeviceInfo: device = disk.device if device.type == 18: - device_type = 'loop' + device_type = "loop" elif device.type in parted.devices: device_type = parted.devices[device.type] else: - debug(f'Device code unknown: {device.type}') + debug(f"Device code unknown: {device.type}") device_type = parted.devices[parted.DEVICE_UNKNOWN] sector_size = SectorSize(device.sectorSize, Unit.B) @@ -599,10 +584,10 @@ class _DeviceInfo: path=Path(device.path), type=device_type, sector_size=sector_size, - total_size=Size(int(device.getLength(unit='B')), Unit.B, sector_size), + total_size=Size(int(device.getLength(unit="B")), Unit.B, sector_size), free_space_regions=free_space, read_only=device.readOnly, - dirty=device.dirty + dirty=device.dirty, ) @@ -624,13 +609,13 @@ class SubvolumeModification: def parse_args(cls, subvol_args: list[_SubvolumeModificationSerialization]) -> list[SubvolumeModification]: mods = [] for entry in subvol_args: - if not entry.get('name', None) or not entry.get('mountpoint', None): - debug(f'Subvolume arg is missing name: {entry}') + if not entry.get("name", None) or not entry.get("mountpoint", None): + debug(f"Subvolume arg is missing name: {entry}") continue - mountpoint = Path(entry['mountpoint']) if entry['mountpoint'] else None + mountpoint = Path(entry["mountpoint"]) if entry["mountpoint"] else None - mods.append(SubvolumeModification(entry['name'], mountpoint)) + mods.append(SubvolumeModification(entry["name"], mountpoint)) return mods @@ -643,15 +628,15 @@ class SubvolumeModification: if self.mountpoint is not None: return self.mountpoint.relative_to(self.mountpoint.anchor) - raise ValueError('Mountpoint is not specified') + raise ValueError("Mountpoint is not specified") def is_root(self) -> bool: if self.mountpoint: - return self.mountpoint == Path('/') + return self.mountpoint == Path("/") return False def json(self) -> _SubvolumeModificationSerialization: - return {'name': str(self.name), 'mountpoint': str(self.mountpoint)} + return {"name": str(self.name), "mountpoint": str(self.mountpoint)} def table_data(self) -> _SubvolumeModificationSerialization: return self.json() @@ -678,15 +663,15 @@ class DeviceGeometry: end = Size(self._geometry.end, Unit.sectors, self._sector_size) length = Size(self._geometry.getLength(), Unit.sectors, self._sector_size) - start_str = f'{self._geometry.start} / {start.format_size(Unit.B, include_unit=False)}' - end_str = f'{self._geometry.end} / {end.format_size(Unit.B, include_unit=False)}' - length_str = f'{self._geometry.getLength()} / {length.format_size(Unit.B, include_unit=False)}' + start_str = f"{self._geometry.start} / {start.format_size(Unit.B, include_unit=False)}" + end_str = f"{self._geometry.end} / {end.format_size(Unit.B, include_unit=False)}" + length_str = f"{self._geometry.getLength()} / {length.format_size(Unit.B, include_unit=False)}" return { - 'Sector size': self._sector_size.value, - 'Start (sector/B)': start_str, - 'End (sector/B)': end_str, - 'Size (sectors/B)': length_str + "Sector size": self._sector_size.value, + "Start (sector/B)": start_str, + "End (sector/B)": end_str, + "Size (sectors/B)": length_str, } @@ -702,16 +687,16 @@ class BDevice: class PartitionType(Enum): - Boot = 'boot' - Primary = 'primary' - _Unknown = 'unknown' + Boot = "boot" + Primary = "primary" + _Unknown = "unknown" @classmethod def get_type_from_code(cls, code: int) -> PartitionType: if code == parted.PARTITION_NORMAL: return PartitionType.Primary else: - debug(f'Partition code not supported: {code}') + debug(f"Partition code not supported: {code}") return PartitionType._Unknown def get_partition_code(self) -> int | None: @@ -747,7 +732,7 @@ class PartitionFlag(PartitionFlagDataMixin, Enum): if s in (partition_flag.name.lower(), partition_flag.alias): return partition_flag - debug(f'Partition flag not supported: {s}') + debug(f"Partition flag not supported: {s}") return None @@ -755,6 +740,7 @@ class PartitionGUID(Enum): """ A list of Partition type GUIDs (lsblk -o+PARTTYPE) can be found here: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs """ + LINUX_ROOT_X86_64 = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709" @property @@ -763,21 +749,21 @@ class PartitionGUID(Enum): class FilesystemType(Enum): - Btrfs = 'btrfs' - Ext2 = 'ext2' - Ext3 = 'ext3' - Ext4 = 'ext4' - F2fs = 'f2fs' - Fat12 = 'fat12' - Fat16 = 'fat16' - Fat32 = 'fat32' - Ntfs = 'ntfs' - Xfs = 'xfs' - LinuxSwap = 'linux-swap' + Btrfs = "btrfs" + Ext2 = "ext2" + Ext3 = "ext3" + Ext4 = "ext4" + F2fs = "f2fs" + Fat12 = "fat12" + Fat16 = "fat16" + Fat32 = "fat32" + Ntfs = "ntfs" + Xfs = "xfs" + LinuxSwap = "linux-swap" # this is not a FS known to parted, so be careful # with the usage from this enum - Crypto_luks = 'crypto_LUKS' + Crypto_luks = "crypto_LUKS" def is_crypto(self) -> bool: return self == FilesystemType.Crypto_luks @@ -786,25 +772,25 @@ class FilesystemType(Enum): def fs_type_mount(self) -> str: match self: case FilesystemType.Ntfs: - return 'ntfs3' + return "ntfs3" case FilesystemType.Fat32: - return 'vfat' + return "vfat" case _: return self.value @property def parted_value(self) -> str: - return self.value + '(v1)' if self == FilesystemType.LinuxSwap else self.value + return self.value + "(v1)" if self == FilesystemType.LinuxSwap else self.value @property def installation_pkg(self) -> str | None: match self: case FilesystemType.Btrfs: - return 'btrfs-progs' + return "btrfs-progs" case FilesystemType.Xfs: - return 'xfsprogs' + return "xfsprogs" case FilesystemType.F2fs: - return 'f2fs-tools' + return "f2fs-tools" case _: return None @@ -812,7 +798,7 @@ class FilesystemType(Enum): def installation_module(self) -> str | None: match self: case FilesystemType.Btrfs: - return 'btrfs' + return "btrfs" case _: return None @@ -820,7 +806,7 @@ class FilesystemType(Enum): def installation_binary(self) -> str | None: match self: case FilesystemType.Btrfs: - return '/usr/bin/btrfs' + return "/usr/bin/btrfs" case _: return None @@ -828,16 +814,16 @@ class FilesystemType(Enum): def installation_hooks(self) -> str | None: match self: case FilesystemType.Btrfs: - return 'btrfs' + return "btrfs" case _: return None class ModificationStatus(Enum): - Exist = 'existing' - Modify = 'modify' - Delete = 'delete' - Create = 'create' + Exist = "existing" + Modify = "modify" + Delete = "delete" + Create = "create" class _PartitionModificationSerialization(TypedDict): @@ -874,14 +860,14 @@ class PartitionModification: def __post_init__(self) -> None: # needed to use the object as a dictionary key due to hash func - if not hasattr(self, '_obj_id'): + if not hasattr(self, "_obj_id"): self._obj_id: uuid.UUID | str = uuid.uuid4() if self.is_exists_or_modify() and not self.dev_path: - raise ValueError('If partition marked as existing a path must be set') + raise ValueError("If partition marked as existing a path must be set") if self.fs_type is None and self.status == ModificationStatus.Modify: - raise ValueError('FS type must not be empty on modifications with status type modify') + raise ValueError("FS type must not be empty on modifications with status type modify") @override def __hash__(self) -> int: @@ -893,20 +879,20 @@ class PartitionModification: @property def obj_id(self) -> str: - if hasattr(self, '_obj_id'): + if hasattr(self, "_obj_id"): return str(self._obj_id) - return '' + return "" @property def safe_dev_path(self) -> Path: if self.dev_path is None: - raise ValueError('Device path was not set') + raise ValueError("Device path was not set") return self.dev_path @property def safe_fs_type(self) -> FilesystemType: if self.fs_type is None: - raise ValueError('File system type is not set') + raise ValueError("File system type is not set") return self.fs_type @classmethod @@ -916,7 +902,7 @@ class PartitionModification: subvol_mods = [] for i in partition_info.btrfs_subvol_infos: subvol_mods.append( - SubvolumeModification.from_existing_subvol_info(i) + SubvolumeModification.from_existing_subvol_info(i), ) else: mountpoint = partition_info.mountpoints[0] if partition_info.mountpoints else None @@ -934,7 +920,7 @@ class PartitionModification: uuid=partition_info.uuid, flags=partition_info.flags, mountpoint=mountpoint, - btrfs_subvols=subvol_mods + btrfs_subvols=subvol_mods, ) @property @@ -946,19 +932,19 @@ class PartitionModification: if self.mountpoint: return self.mountpoint.relative_to(self.mountpoint.anchor) - raise ValueError('Mountpoint is not specified') + raise ValueError("Mountpoint is not specified") def is_efi(self) -> bool: return PartitionFlag.ESP in self.flags def is_boot(self) -> bool: if self.mountpoint is not None: - return self.mountpoint == Path('/boot') + return self.mountpoint == Path("/boot") return False def is_root(self) -> bool: if self.mountpoint is not None: - return self.mountpoint == Path('/') + return self.mountpoint == Path("/") else: for subvol in self.btrfs_subvols: if subvol.is_root(): @@ -968,7 +954,7 @@ class PartitionModification: def is_home(self) -> bool: if self.mountpoint is not None: - return self.mountpoint == Path('/home') + return self.mountpoint == Path("/home") return False def is_swap(self) -> bool: @@ -987,7 +973,7 @@ class PartitionModification: return self.status in [ ModificationStatus.Exist, ModificationStatus.Delete, - ModificationStatus.Modify + ModificationStatus.Modify, ] def is_create_or_modify(self) -> bool: @@ -996,7 +982,7 @@ class PartitionModification: @property def mapper_name(self) -> str | None: if self.dev_path: - return f'{ENC_IDENTIFIER}{self.dev_path.name}' + return f"{ENC_IDENTIFIER}{self.dev_path.name}" return None def set_flag(self, flag: PartitionFlag) -> None: @@ -1014,17 +1000,17 @@ class PartitionModification: Called for configuration settings """ return { - 'obj_id': self.obj_id, - 'status': self.status.value, - 'type': self.type.value, - 'start': self.start.json(), - 'size': self.length.json(), - 'fs_type': self.fs_type.value if self.fs_type else None, - 'mountpoint': str(self.mountpoint) if self.mountpoint else None, - 'mount_options': self.mount_options, - 'flags': [f.description for f in self.flags], - 'dev_path': str(self.dev_path) if self.dev_path else None, - 'btrfs': [vol.json() for vol in self.btrfs_subvols] + "obj_id": self.obj_id, + "status": self.status.value, + "type": self.type.value, + "start": self.start.json(), + "size": self.length.json(), + "fs_type": self.fs_type.value if self.fs_type else None, + "mountpoint": str(self.mountpoint) if self.mountpoint else None, + "mount_options": self.mount_options, + "flags": [f.description for f in self.flags], + "dev_path": str(self.dev_path) if self.dev_path else None, + "btrfs": [vol.json() for vol in self.btrfs_subvols], } def table_data(self) -> dict[str, str]: @@ -1032,37 +1018,37 @@ class PartitionModification: Called for displaying data in table format """ part_mod = { - 'Status': self.status.value, - 'Device': str(self.dev_path) if self.dev_path else '', - 'Type': self.type.value, - 'Start': self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False), - 'End': self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False), - 'Size': self.length.format_highest(), - 'FS type': self.fs_type.value if self.fs_type else 'Unknown', - 'Mountpoint': str(self.mountpoint) if self.mountpoint else '', - 'Mount options': ', '.join(self.mount_options), - 'Flags': ', '.join([f.description for f in self.flags]), + "Status": self.status.value, + "Device": str(self.dev_path) if self.dev_path else "", + "Type": self.type.value, + "Start": self.start.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + "End": self.end.format_size(Unit.sectors, self.start.sector_size, include_unit=False), + "Size": self.length.format_highest(), + "FS type": self.fs_type.value if self.fs_type else "Unknown", + "Mountpoint": str(self.mountpoint) if self.mountpoint else "", + "Mount options": ", ".join(self.mount_options), + "Flags": ", ".join([f.description for f in self.flags]), } if self.btrfs_subvols: - part_mod['Btrfs vol.'] = f'{len(self.btrfs_subvols)} subvolumes' + part_mod["Btrfs vol."] = f"{len(self.btrfs_subvols)} subvolumes" return part_mod class LvmLayoutType(Enum): - Default = 'default' + Default = "default" # Manual = 'manual_lvm' def display_msg(self) -> str: match self: case LvmLayoutType.Default: - return str(_('Default layout')) + return str(_("Default layout")) # case LvmLayoutType.Manual: # return str(_('Manual configuration')) - raise ValueError(f'Unknown type: {self}') + raise ValueError(f"Unknown type: {self}") class _LvmVolumeGroupSerialization(TypedDict): @@ -1079,9 +1065,9 @@ class LvmVolumeGroup: def json(self) -> _LvmVolumeGroupSerialization: return { - 'name': self.name, - 'lvm_pvs': [p.obj_id for p in self.pvs], - 'volumes': [vol.json() for vol in self.volumes] + "name": self.name, + "lvm_pvs": [p.obj_id for p in self.pvs], + "volumes": [vol.json() for vol in self.volumes], } @staticmethod @@ -1089,13 +1075,13 @@ class LvmVolumeGroup: lvm_pvs = [] for mod in disk_config.device_modifications: for part in mod.partitions: - if part.obj_id in arg.get('lvm_pvs', []): + if part.obj_id in arg.get("lvm_pvs", []): lvm_pvs.append(part) return LvmVolumeGroup( - arg['name'], + arg["name"], lvm_pvs, - [LvmVolume.parse_arg(vol) for vol in arg['volumes']] + [LvmVolume.parse_arg(vol) for vol in arg["volumes"]], ) def contains_lv(self, lv: LvmVolume) -> bool: @@ -1103,10 +1089,10 @@ class LvmVolumeGroup: class LvmVolumeStatus(Enum): - Exist = 'existing' - Modify = 'modify' - Delete = 'delete' - Create = 'create' + Exist = "existing" + Modify = "modify" + Delete = "delete" + Create = "create" class _LvmVolumeSerialization(TypedDict): @@ -1137,7 +1123,7 @@ class LvmVolume: def __post_init__(self) -> None: # needed to use the object as a dictionary key due to hash func - if not hasattr(self, '_obj_id'): + if not hasattr(self, "_obj_id"): self._obj_id: uuid.UUID | str = uuid.uuid4() @override @@ -1146,33 +1132,33 @@ class LvmVolume: @property def obj_id(self) -> str: - if hasattr(self, '_obj_id'): + if hasattr(self, "_obj_id"): return str(self._obj_id) - return '' + return "" @property def mapper_name(self) -> str | None: if self.dev_path: - return f'{ENC_IDENTIFIER}{self.safe_dev_path.name}' + return f"{ENC_IDENTIFIER}{self.safe_dev_path.name}" return None @property def mapper_path(self) -> Path: if self.mapper_name: - return Path(f'/dev/mapper/{self.mapper_name}') + return Path(f"/dev/mapper/{self.mapper_name}") - raise ValueError('No mapper path set') + raise ValueError("No mapper path set") @property def safe_dev_path(self) -> Path: if self.dev_path: return self.dev_path - raise ValueError('No device path for volume defined') + raise ValueError("No device path for volume defined") @property def safe_fs_type(self) -> FilesystemType: if self.fs_type is None: - raise ValueError('File system type is not set') + raise ValueError("File system type is not set") return self.fs_type @property @@ -1184,45 +1170,45 @@ class LvmVolume: if self.mountpoint is not None: return self.mountpoint.relative_to(self.mountpoint.anchor) - raise ValueError('Mountpoint is not specified') + raise ValueError("Mountpoint is not specified") @staticmethod def parse_arg(arg: _LvmVolumeSerialization) -> LvmVolume: volume = LvmVolume( - status=LvmVolumeStatus(arg['status']), - name=arg['name'], - fs_type=FilesystemType(arg['fs_type']), - length=Size.parse_args(arg['length']), - mountpoint=Path(arg['mountpoint']) if arg['mountpoint'] else None, - mount_options=arg.get('mount_options', []), - btrfs_subvols=SubvolumeModification.parse_args(arg.get('btrfs', [])) + status=LvmVolumeStatus(arg["status"]), + name=arg["name"], + fs_type=FilesystemType(arg["fs_type"]), + length=Size.parse_args(arg["length"]), + mountpoint=Path(arg["mountpoint"]) if arg["mountpoint"] else None, + mount_options=arg.get("mount_options", []), + btrfs_subvols=SubvolumeModification.parse_args(arg.get("btrfs", [])), ) - volume._obj_id = arg['obj_id'] + volume._obj_id = arg["obj_id"] return volume def json(self) -> _LvmVolumeSerialization: return { - 'obj_id': self.obj_id, - 'status': self.status.value, - 'name': self.name, - 'fs_type': self.fs_type.value, - 'length': self.length.json(), - 'mountpoint': str(self.mountpoint) if self.mountpoint else None, - 'mount_options': self.mount_options, - 'btrfs': [vol.json() for vol in self.btrfs_subvols] + "obj_id": self.obj_id, + "status": self.status.value, + "name": self.name, + "fs_type": self.fs_type.value, + "length": self.length.json(), + "mountpoint": str(self.mountpoint) if self.mountpoint else None, + "mount_options": self.mount_options, + "btrfs": [vol.json() for vol in self.btrfs_subvols], } def table_data(self) -> dict[str, str]: part_mod = { - 'Type': self.status.value, - 'Name': self.name, - 'Size': self.length.format_highest(), - 'FS type': self.fs_type.value, - 'Mountpoint': str(self.mountpoint) if self.mountpoint else '', - 'Mount options': ', '.join(self.mount_options), - 'Btrfs': '{} {}'.format(str(len(self.btrfs_subvols)), 'vol') + "Type": self.status.value, + "Name": self.name, + "Size": self.length.format_highest(), + "FS type": self.fs_type.value, + "Mountpoint": str(self.mountpoint) if self.mountpoint else "", + "Mount options": ", ".join(self.mount_options), + "Btrfs": "{} {}".format(str(len(self.btrfs_subvols)), "vol"), } return part_mod @@ -1237,7 +1223,7 @@ class LvmVolume: def is_root(self) -> bool: if self.mountpoint is not None: - return Path('/') == self.mountpoint + return Path("/") == self.mountpoint else: for subvol in self.btrfs_subvols: if subvol.is_root(): @@ -1282,13 +1268,13 @@ class LvmConfiguration: for group in self.vol_groups: for pv in group.pvs: if pv in pvs: - raise ValueError('A PV cannot be used in multiple volume groups') + raise ValueError("A PV cannot be used in multiple volume groups") pvs.append(pv) def json(self) -> _LvmConfigurationSerialization: return { - 'config_type': self.config_type.value, - 'vol_groups': [vol_gr.json() for vol_gr in self.vol_groups] + "config_type": self.config_type.value, + "vol_groups": [vol_gr.json() for vol_gr in self.vol_groups], } @staticmethod @@ -1297,12 +1283,12 @@ class LvmConfiguration: for mod in disk_config.device_modifications: for part in mod.partitions: # FIXME: 'lvm_pvs' does not seem like it can ever exist in the 'arg' serialization - if part.obj_id in arg.get('lvm_pvs', []): # type: ignore[operator] + if part.obj_id in arg.get("lvm_pvs", []): # type: ignore[operator] lvm_pvs.append(part) return LvmConfiguration( - config_type=LvmLayoutType(arg['config_type']), - vol_groups=[LvmVolumeGroup.parse_arg(vol_group, disk_config) for vol_group in arg['vol_groups']], + config_type=LvmLayoutType(arg["config_type"]), + vol_groups=[LvmVolumeGroup.parse_arg(vol_group, disk_config) for vol_group in arg["vol_groups"]], ) def get_all_pvs(self) -> list[PartitionModification]: @@ -1380,34 +1366,34 @@ class DeviceModification: Called when generating configuration files """ return { - 'device': str(self.device.device_info.path), - 'wipe': self.wipe, - 'partitions': [p.json() for p in self.partitions] + "device": str(self.device.device_info.path), + "wipe": self.wipe, + "partitions": [p.json() for p in self.partitions], } class EncryptionType(Enum): NoEncryption = "no_encryption" Luks = "luks" - LvmOnLuks = 'lvm_on_luks' - LuksOnLvm = 'luks_on_lvm' + LvmOnLuks = "lvm_on_luks" + LuksOnLvm = "luks_on_lvm" @classmethod - def _encryption_type_mapper(cls) -> dict[str, 'EncryptionType']: + def _encryption_type_mapper(cls) -> dict[str, "EncryptionType"]: return { - str(_('No Encryption')): EncryptionType.NoEncryption, - str(_('LUKS')): EncryptionType.Luks, - str(_('LVM on LUKS')): EncryptionType.LvmOnLuks, - str(_('LUKS on LVM')): EncryptionType.LuksOnLvm + str(_("No Encryption")): EncryptionType.NoEncryption, + str(_("LUKS")): EncryptionType.Luks, + str(_("LVM on LUKS")): EncryptionType.LvmOnLuks, + str(_("LUKS on LVM")): EncryptionType.LuksOnLvm, } @classmethod - def text_to_type(cls, text: str) -> 'EncryptionType': + def text_to_type(cls, text: str) -> "EncryptionType": mapping = cls._encryption_type_mapper() return mapping[text] @classmethod - def type_to_text(cls, type_: 'EncryptionType') -> str: + def type_to_text(cls, type_: "EncryptionType") -> str: mapping = cls._encryption_type_mapper() type_to_text = {type_: text for text, type_ in mapping.items()} return type_to_text[type_] @@ -1430,27 +1416,27 @@ class DiskEncryption: def __post_init__(self) -> None: if self.encryption_type in [EncryptionType.Luks, EncryptionType.LvmOnLuks] and not self.partitions: - raise ValueError('Luks or LvmOnLuks encryption require partitions to be defined') + raise ValueError("Luks or LvmOnLuks encryption require partitions to be defined") if self.encryption_type == EncryptionType.LuksOnLvm and not self.lvm_volumes: - raise ValueError('LuksOnLvm encryption require LMV volumes to be defined') + raise ValueError("LuksOnLvm encryption require LMV volumes to be defined") def should_generate_encryption_file(self, dev: PartitionModification | LvmVolume) -> bool: if isinstance(dev, PartitionModification): - return dev in self.partitions and dev.mountpoint != Path('/') + return dev in self.partitions and dev.mountpoint != Path("/") elif isinstance(dev, LvmVolume): - return dev in self.lvm_volumes and dev.mountpoint != Path('/') + return dev in self.lvm_volumes and dev.mountpoint != Path("/") return False def json(self) -> _DiskEncryptionSerialization: obj: _DiskEncryptionSerialization = { - 'encryption_type': self.encryption_type.value, - 'partitions': [p.obj_id for p in self.partitions], - 'lvm_volumes': [vol.obj_id for vol in self.lvm_volumes] + "encryption_type": self.encryption_type.value, + "partitions": [p.obj_id for p in self.partitions], + "lvm_volumes": [vol.obj_id for vol in self.lvm_volumes], } if self.hsm_device: - obj['hsm_device'] = self.hsm_device.json() + obj["hsm_device"] = self.hsm_device.json() return obj @@ -1473,8 +1459,8 @@ class DiskEncryption: cls, disk_config: DiskLayoutConfiguration, disk_encryption: _DiskEncryptionSerialization, - password: Password | None = None - ) -> 'DiskEncryption | None': + password: Password | None = None, + ) -> "DiskEncryption | None": if not cls.validate_enc(disk_config): return None @@ -1484,23 +1470,23 @@ class DiskEncryption: enc_partitions = [] for mod in disk_config.device_modifications: for part in mod.partitions: - if part.obj_id in disk_encryption.get('partitions', []): + if part.obj_id in disk_encryption.get("partitions", []): enc_partitions.append(part) volumes = [] if disk_config.lvm_config: for vol in disk_config.lvm_config.get_all_volumes(): - if vol.obj_id in disk_encryption.get('lvm_volumes', []): + if vol.obj_id in disk_encryption.get("lvm_volumes", []): volumes.append(vol) enc = DiskEncryption( - EncryptionType(disk_encryption['encryption_type']), + EncryptionType(disk_encryption["encryption_type"]), password, enc_partitions, - volumes + volumes, ) - if hsm := disk_encryption.get('hsm_device', None): + if hsm := disk_encryption.get("hsm_device", None): enc.hsm_device = Fido2Device.parse_arg(hsm) return enc @@ -1520,24 +1506,24 @@ class Fido2Device: def json(self) -> _Fido2DeviceSerialization: return { - 'path': str(self.path), - 'manufacturer': self.manufacturer, - 'product': self.product + "path": str(self.path), + "manufacturer": self.manufacturer, + "product": self.product, } def table_data(self) -> dict[str, str]: return { - 'Path': str(self.path), - 'Manufacturer': self.manufacturer, - 'Product': self.product + "Path": str(self.path), + "Manufacturer": self.manufacturer, + "Product": self.product, } @classmethod - def parse_arg(cls, arg: _Fido2DeviceSerialization) -> 'Fido2Device': + def parse_arg(cls, arg: _Fido2DeviceSerialization) -> "Fido2Device": return Fido2Device( - Path(arg['path']), - arg['manufacturer'], - arg['product'] + Path(arg["path"]), + arg["manufacturer"], + arg["product"], ) @@ -1545,7 +1531,7 @@ class LsblkInfo(BaseModel): name: str path: Path pkname: str | None - log_sec: int = Field(alias='log-sec') + log_sec: int = Field(alias="log-sec") size: Size pttype: str | None ptuuid: str | None @@ -1558,32 +1544,28 @@ class LsblkInfo(BaseModel): fstype: str | None fsver: str | None fsavail: int | None - fsuse_percentage: str | None = Field(alias='fsuse%') + fsuse_percentage: str | None = Field(alias="fsuse%") type: str mountpoint: Path | None mountpoints: list[Path] fsroots: list[Path] children: list[LsblkInfo] = Field(default_factory=list) - @field_validator('size', mode='before') + @field_validator("size", mode="before") @classmethod def convert_size(cls, v: int, info: ValidationInfo) -> Size: - sector_size = SectorSize(info.data['log_sec'], Unit.B) + sector_size = SectorSize(info.data["log_sec"], Unit.B) return Size(v, Unit.B, sector_size) - @field_validator('mountpoints', 'fsroots', mode='before') + @field_validator("mountpoints", "fsroots", mode="before") @classmethod def remove_none(cls, v: list[Path | None]) -> list[Path]: return [item for item in v if item is not None] - @field_serializer('size', when_used='json') + @field_serializer("size", when_used="json") def serialize_size(self, size: Size) -> str: return size.format_size(Unit.MiB) @classmethod def fields(cls) -> list[str]: - return [ - field.alias or name - for name, field in cls.model_fields.items() - if name != 'children' - ] + return [field.alias or name for name, field in cls.model_fields.items() if name != "children"] diff --git a/archinstall/lib/models/locale.py b/archinstall/lib/models/locale.py index 897408d9..75cddd2d 100644 --- a/archinstall/lib/models/locale.py +++ b/archinstall/lib/models/locale.py @@ -18,42 +18,42 @@ class LocaleConfiguration: sys_enc: str @staticmethod - def default() -> 'LocaleConfiguration': + def default() -> "LocaleConfiguration": layout = get_kb_layout() if layout == "": - layout = 'us' - return LocaleConfiguration(layout, 'en_US.UTF-8', 'UTF-8') + layout = "us" + return LocaleConfiguration(layout, "en_US.UTF-8", "UTF-8") def json(self) -> dict[str, str]: return { - 'kb_layout': self.kb_layout, - 'sys_lang': self.sys_lang, - 'sys_enc': self.sys_enc + "kb_layout": self.kb_layout, + "sys_lang": self.sys_lang, + "sys_enc": self.sys_enc, } def preview(self) -> str: - output = '{}: {}\n'.format(str(_('Keyboard layout')), self.kb_layout) - output += '{}: {}\n'.format(str(_('Locale language')), self.sys_lang) - output += '{}: {}'.format(str(_('Locale encoding')), self.sys_enc) + output = "{}: {}\n".format(str(_("Keyboard layout")), self.kb_layout) + output += "{}: {}\n".format(str(_("Locale language")), self.sys_lang) + output += "{}: {}".format(str(_("Locale encoding")), self.sys_enc) return output @classmethod - def _load_config(cls, config: 'LocaleConfiguration', args: dict[str, str]) -> 'LocaleConfiguration': - if 'sys_lang' in args: - config.sys_lang = args['sys_lang'] - if 'sys_enc' in args: - config.sys_enc = args['sys_enc'] - if 'kb_layout' in args: - config.kb_layout = args['kb_layout'] + def _load_config(cls, config: "LocaleConfiguration", args: dict[str, str]) -> "LocaleConfiguration": + if "sys_lang" in args: + config.sys_lang = args["sys_lang"] + if "sys_enc" in args: + config.sys_enc = args["sys_enc"] + if "kb_layout" in args: + config.kb_layout = args["kb_layout"] return config @classmethod - def parse_arg(cls, args: dict[str, Any]) -> 'LocaleConfiguration': + def parse_arg(cls, args: dict[str, Any]) -> "LocaleConfiguration": default = cls.default() - if 'locale_config' in args: - default = cls._load_config(default, args['locale_config']) + if "locale_config" in args: + default = cls._load_config(default, args["locale_config"]) else: default = cls._load_config(default, args) diff --git a/archinstall/lib/models/mirrors.py b/archinstall/lib/models/mirrors.py index 72ff009d..2af05151 100644 --- a/archinstall/lib/models/mirrors.py +++ b/archinstall/lib/models/mirrors.py @@ -45,7 +45,7 @@ class MirrorStatusEntryV3(BaseModel): @property def server_url(self) -> str: - return f'{self.url}$repo/os/$arch' + return f"{self.url}$repo/os/$arch" @property def speed(self) -> float: @@ -101,7 +101,7 @@ class MirrorStatusEntryV3(BaseModel): return self._latency @classmethod - @field_validator('score', mode='before') + @field_validator("score", mode="before") def validate_score(cls, value: float) -> int | None: if value is not None: value = round(value) @@ -109,12 +109,12 @@ class MirrorStatusEntryV3(BaseModel): return value - @model_validator(mode='after') - def debug_output(self, validation_info) -> 'MirrorStatusEntryV3': - self._hostname, *port = urllib.parse.urlparse(self.url).netloc.split(':', 1) + @model_validator(mode="after") + def debug_output(self, validation_info) -> "MirrorStatusEntryV3": + self._hostname, *port = urllib.parse.urlparse(self.url).netloc.split(":", 1) self._port = int(port[0]) if port and len(port) >= 1 else None - debug(f"Loaded mirror {self._hostname}" + (f" with current score of {self.score}" if self.score else '')) + debug(f"Loaded mirror {self._hostname}" + (f" with current score of {self.score}" if self.score else "")) return self @@ -125,13 +125,13 @@ class MirrorStatusListV3(BaseModel): urls: list[MirrorStatusEntryV3] version: int - @model_validator(mode='before') + @model_validator(mode="before") @classmethod def check_model( cls, - data: dict[str, int | datetime.datetime | list[MirrorStatusEntryV3]] + data: dict[str, int | datetime.datetime | list[MirrorStatusEntryV3]], ) -> dict[str, int | datetime.datetime | list[MirrorStatusEntryV3]]: - if data.get('version') == 3: + if data.get("version") == 3: return data raise ValueError("MirrorStatusListV3 only accepts version 3 data from https://archlinux.org/mirrors/status/json/") @@ -153,14 +153,14 @@ class MirrorRegion: class SignCheck(Enum): - Never = 'Never' - Optional = 'Optional' - Required = 'Required' + Never = "Never" + Optional = "Optional" + Required = "Required" class SignOption(Enum): - TrustedOnly = 'TrustedOnly' - TrustAll = 'TrustAll' + TrustedOnly = "TrustedOnly" + TrustAll = "TrustAll" class _CustomRepositorySerialization(TypedDict): @@ -179,31 +179,31 @@ class CustomRepository: def table_data(self) -> dict[str, str]: return { - 'Name': self.name, - 'Url': self.url, - 'Sign check': self.sign_check.value, - 'Sign options': self.sign_option.value + "Name": self.name, + "Url": self.url, + "Sign check": self.sign_check.value, + "Sign options": self.sign_option.value, } def json(self) -> _CustomRepositorySerialization: return { - 'name': self.name, - 'url': self.url, - 'sign_check': self.sign_check.value, - 'sign_option': self.sign_option.value + "name": self.name, + "url": self.url, + "sign_check": self.sign_check.value, + "sign_option": self.sign_option.value, } @classmethod - def parse_args(cls, args: list[dict[str, str]]) -> list['CustomRepository']: + def parse_args(cls, args: list[dict[str, str]]) -> list["CustomRepository"]: configs = [] for arg in args: configs.append( CustomRepository( - arg['name'], - arg['url'], - SignCheck(arg['sign_check']), - SignOption(arg['sign_option']) - ) + arg["name"], + arg["url"], + SignCheck(arg["sign_check"]), + SignOption(arg["sign_option"]), + ), ) return configs @@ -214,17 +214,17 @@ class CustomServer: url: str def table_data(self) -> dict[str, str]: - return {'Url': self.url} + return {"Url": self.url} def json(self) -> dict[str, str]: - return {'url': self.url} + return {"url": self.url} @classmethod - def parse_args(cls, args: list[dict[str, str]]) -> list['CustomServer']: + def parse_args(cls, args: list[dict[str, str]]) -> list["CustomServer"]: configs = [] for arg in args: configs.append( - CustomServer(arg['url']) + CustomServer(arg["url"]), ) return configs @@ -246,11 +246,11 @@ class MirrorConfiguration: @property def region_names(self) -> str: - return '\n'.join([m.name for m in self.mirror_regions]) + return "\n".join([m.name for m in self.mirror_regions]) @property def custom_server_urls(self) -> str: - return '\n'.join([s.url for s in self.custom_servers]) + return "\n".join([s.url for s in self.custom_servers]) def json(self) -> _MirrorConfigurationSerialization: regions = {} @@ -258,45 +258,45 @@ class MirrorConfiguration: regions.update(m.json()) return { - 'mirror_regions': regions, - 'custom_servers': self.custom_servers, - 'optional_repositories': [r.value for r in self.optional_repositories], - 'custom_repositories': [c.json() for c in self.custom_repositories] + "mirror_regions": regions, + "custom_servers": self.custom_servers, + "optional_repositories": [r.value for r in self.optional_repositories], + "custom_repositories": [c.json() for c in self.custom_repositories], } def custom_servers_config(self) -> str: - config = '## Custom Servers\n' + config = "## Custom Servers\n" for server in self.custom_servers: - config += f'Server = {server.url}\n' + config += f"Server = {server.url}\n" return config.strip() def regions_config(self, speed_sort: bool = True) -> str: from ..mirrors import mirror_list_handler - config = '' + config = "" for mirror_region in self.mirror_regions: sorted_stati = mirror_list_handler.get_status_by_region( mirror_region.name, - speed_sort=speed_sort + speed_sort=speed_sort, ) - config += f'\n\n## {mirror_region.name}\n' + config += f"\n\n## {mirror_region.name}\n" for status in sorted_stati: - config += f'Server = {status.server_url}\n' + config += f"Server = {status.server_url}\n" return config def repositories_config(self) -> str: - config = '' + config = "" for repo in self.custom_repositories: - config += f'\n\n[{repo.name}]\n' - config += f'SigLevel = {repo.sign_check.value} {repo.sign_option.value}\n' - config += f'Server = {repo.url}\n' + config += f"\n\n[{repo.name}]\n" + config += f"SigLevel = {repo.sign_check.value} {repo.sign_option.value}\n" + config += f"Server = {repo.url}\n" return config @@ -304,26 +304,26 @@ class MirrorConfiguration: def parse_args( cls, args: dict[str, Any], - backwards_compatible_repo: list[Repository] = [] - ) -> 'MirrorConfiguration': + backwards_compatible_repo: list[Repository] = [], + ) -> "MirrorConfiguration": config = MirrorConfiguration() - mirror_regions = args.get('mirror_regions', []) + mirror_regions = args.get("mirror_regions", []) if mirror_regions: for region, urls in mirror_regions.items(): config.mirror_regions.append(MirrorRegion(region, urls)) - if args.get('custom_servers'): - config.custom_servers = CustomServer.parse_args(args['custom_servers']) + if args.get("custom_servers"): + config.custom_servers = CustomServer.parse_args(args["custom_servers"]) # backwards compatibility with the new custom_repository - if 'custom_mirrors' in args: - config.custom_repositories = CustomRepository.parse_args(args['custom_mirrors']) - if 'custom_repositories' in args: - config.custom_repositories = CustomRepository.parse_args(args['custom_repositories']) + if "custom_mirrors" in args: + config.custom_repositories = CustomRepository.parse_args(args["custom_mirrors"]) + if "custom_repositories" in args: + config.custom_repositories = CustomRepository.parse_args(args["custom_repositories"]) - if 'optional_repositories' in args: - config.optional_repositories = [Repository(r) for r in args['optional_repositories']] + if "optional_repositories" in args: + config.optional_repositories = [Repository(r) for r in args["optional_repositories"]] if backwards_compatible_repo: for r in backwards_compatible_repo: diff --git a/archinstall/lib/models/network_configuration.py b/archinstall/lib/models/network_configuration.py index 2a4ca9af..5cd619a0 100644 --- a/archinstall/lib/models/network_configuration.py +++ b/archinstall/lib/models/network_configuration.py @@ -23,11 +23,11 @@ class NicType(Enum): def display_msg(self) -> str: match self: case NicType.ISO: - return str(_('Copy ISO network configuration to installation')) + return str(_("Copy ISO network configuration to installation")) case NicType.NM: - return str(_('Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)')) + return str(_("Use NetworkManager (necessary to configure internet graphically in GNOME and KDE Plasma)")) case NicType.MANUAL: - return str(_('Manual configuration')) + return str(_("Manual configuration")) class _NicSerialization(TypedDict): @@ -48,30 +48,30 @@ class Nic: def table_data(self) -> dict[str, str | bool | list[str]]: return { - 'iface': self.iface if self.iface else '', - 'ip': self.ip if self.ip else '', - 'dhcp': self.dhcp, - 'gateway': self.gateway if self.gateway else '', - 'dns': self.dns + "iface": self.iface if self.iface else "", + "ip": self.ip if self.ip else "", + "dhcp": self.dhcp, + "gateway": self.gateway if self.gateway else "", + "dns": self.dns, } def json(self) -> _NicSerialization: return { - 'iface': self.iface, - 'ip': self.ip, - 'dhcp': self.dhcp, - 'gateway': self.gateway, - 'dns': self.dns + "iface": self.iface, + "ip": self.ip, + "dhcp": self.dhcp, + "gateway": self.gateway, + "dns": self.dns, } @staticmethod def parse_arg(arg: _NicSerialization) -> Nic: return Nic( - iface=arg.get('iface', None), - ip=arg.get('ip', None), - dhcp=arg.get('dhcp', True), - gateway=arg.get('gateway', None), - dns=arg.get('dns', []), + iface=arg.get("iface", None), + ip=arg.get("ip", None), + dhcp=arg.get("dhcp", True), + gateway=arg.get("gateway", None), + dns=arg.get("dns", []), ) def as_systemd_config(self) -> str: @@ -79,25 +79,25 @@ class Nic: network: list[tuple[str, str]] = [] if self.iface: - match.append(('Name', self.iface)) + match.append(("Name", self.iface)) if self.dhcp: - network.append(('DHCP', 'yes')) + network.append(("DHCP", "yes")) else: if self.ip: - network.append(('Address', self.ip)) + network.append(("Address", self.ip)) if self.gateway: - network.append(('Gateway', self.gateway)) + network.append(("Gateway", self.gateway)) for dns in self.dns: - network.append(('DNS', dns)) + network.append(("DNS", dns)) - config = {'Match': match, 'Network': network} + config = {"Match": match, "Network": network} - config_str = '' + config_str = "" for top, entries in config.items(): - config_str += f'[{top}]\n' - config_str += '\n'.join([f'{k}={v}' for k, v in entries]) - config_str += '\n\n' + config_str += f"[{top}]\n" + config_str += "\n".join([f"{k}={v}" for k, v in entries]) + config_str += "\n\n" return config_str @@ -113,15 +113,15 @@ class NetworkConfiguration: nics: list[Nic] = field(default_factory=list) def json(self) -> _NetworkConfigurationSerialization: - config: _NetworkConfigurationSerialization = {'type': self.type.value} + config: _NetworkConfigurationSerialization = {"type": self.type.value} if self.nics: - config['nics'] = [n.json() for n in self.nics] + config["nics"] = [n.json() for n in self.nics] return config @staticmethod def parse_arg(config: _NetworkConfigurationSerialization) -> NetworkConfiguration | None: - nic_type = config.get('type', None) + nic_type = config.get("type", None) if not nic_type: return None @@ -131,7 +131,7 @@ class NetworkConfiguration: case NicType.NM: return NetworkConfiguration(NicType.NM) case NicType.MANUAL: - nics_arg = config.get('nics', []) + nics_arg = config.get("nics", []) if nics_arg: nics = [Nic.parse_arg(n) for n in nics_arg] return NetworkConfiguration(NicType.MANUAL, nics) @@ -141,22 +141,22 @@ class NetworkConfiguration: def install_network_config( self, installation: Installer, - profile_config: ProfileConfiguration | None = None + profile_config: ProfileConfiguration | None = None, ) -> None: match self.type: case NicType.ISO: installation.copy_iso_network_config( - enable_services=True # Sources the ISO network configuration to the install medium. + enable_services=True, # Sources the ISO network configuration to the install medium. ) case NicType.NM: installation.add_additional_packages(["networkmanager"]) if profile_config and profile_config.profile: if profile_config.profile.is_desktop_profile(): installation.add_additional_packages(["network-manager-applet"]) - installation.enable_service('NetworkManager.service') + installation.enable_service("NetworkManager.service") case NicType.MANUAL: for nic in self.nics: installation.configure_nic(nic) - installation.enable_service('systemd-networkd') - installation.enable_service('systemd-resolved') + installation.enable_service("systemd-networkd") + installation.enable_service("systemd-resolved") diff --git a/archinstall/lib/models/packages.py b/archinstall/lib/models/packages.py index 53cc323f..cd2dcfe1 100644 --- a/archinstall/lib/models/packages.py +++ b/archinstall/lib/models/packages.py @@ -14,10 +14,10 @@ if TYPE_CHECKING: class Repository(Enum): - Core = 'core' - Extra = 'extra' - Multilib = 'multilib' - Testing = 'testing' + Core = "core" + Extra = "extra" + Multilib = "multilib" + Testing = "testing" def get_repository_list(self) -> list[str]: match self: @@ -29,9 +29,9 @@ class Repository(Enum): return [Repository.Multilib.value] case Repository.Testing: return [ - 'core-testing', - 'extra-testing', - 'multilib-testing' + "core-testing", + "extra-testing", + "multilib-testing", ] @@ -65,7 +65,7 @@ class PackageSearchResult: checkdepends: list[str] @staticmethod - def from_json(data: dict[str, Any]) -> 'PackageSearchResult': + def from_json(data: dict[str, Any]) -> "PackageSearchResult": return PackageSearchResult(**data) @property @@ -79,7 +79,7 @@ class PackageSearchResult: return self.pkg_version == other.pkg_version - def __lt__(self, other: 'PackageSearchResult') -> bool: + def __lt__(self, other: "PackageSearchResult") -> bool: return self.pkg_version < other.pkg_version @@ -93,16 +93,16 @@ class PackageSearch: results: list[PackageSearchResult] @staticmethod - def from_json(data: dict[str, Any]) -> 'PackageSearch': - results = [PackageSearchResult.from_json(r) for r in data['results']] + def from_json(data: dict[str, Any]) -> "PackageSearch": + results = [PackageSearchResult.from_json(r) for r in data["results"]] return PackageSearch( - version=data['version'], - limit=data['limit'], - valid=data['valid'], - num_pages=data['num_pages'], - page=data['page'], - results=results + version=data["version"], + limit=data["limit"], + valid=data["valid"], + num_pages=data["num_pages"], + page=data["page"], + results=results, ) @@ -137,7 +137,7 @@ class LocalPackage(BaseModel): return self.version == other.version - def __lt__(self, other: 'LocalPackage') -> bool: + def __lt__(self, other: "LocalPackage") -> bool: return self.version < other.version @@ -166,11 +166,11 @@ class AvailablePackage(BaseModel): # return all package info line by line def info(self) -> str: - output = '' + output = "" for key, value in self.model_dump().items(): - key = key.replace('_', ' ').capitalize() + key = key.replace("_", " ").capitalize() key = key.ljust(self.longest_key) - output += f'{key} : {value}\n' + output += f"{key} : {value}\n" return output @@ -183,15 +183,15 @@ class PackageGroup: @classmethod def from_available_packages( cls, - packages: dict[str, AvailablePackage] - ) -> dict[str, 'PackageGroup']: - pkg_groups: dict[str, 'PackageGroup'] = {} + packages: dict[str, AvailablePackage], + ) -> dict[str, "PackageGroup"]: + pkg_groups: dict[str, "PackageGroup"] = {} for pkg in packages.values(): - if 'None' in pkg.groups: + if "None" in pkg.groups: continue - groups = pkg.groups.split(' ') + groups = pkg.groups.split(" ") for group in groups: # same group names have multiple spaces in between @@ -204,6 +204,6 @@ class PackageGroup: return pkg_groups def info(self) -> str: - output = str(_('Package group:')) + '\n - ' - output += '\n - '.join(self.packages) + output = str(_("Package group:")) + "\n - " + output += "\n - ".join(self.packages) return output diff --git a/archinstall/lib/models/profile_model.py b/archinstall/lib/models/profile_model.py index 07a37de5..8d41e5c9 100644 --- a/archinstall/lib/models/profile_model.py +++ b/archinstall/lib/models/profile_model.py @@ -25,21 +25,23 @@ class ProfileConfiguration: def json(self) -> _ProfileConfigurationSerialization: from ..profile.profiles_handler import profile_handler + return { - 'profile': profile_handler.to_json(self.profile), - 'gfx_driver': self.gfx_driver.value if self.gfx_driver else None, - 'greeter': self.greeter.value if self.greeter else None + "profile": profile_handler.to_json(self.profile), + "gfx_driver": self.gfx_driver.value if self.gfx_driver else None, + "greeter": self.greeter.value if self.greeter else None, } @classmethod - def parse_arg(cls, arg: _ProfileConfigurationSerialization) -> 'ProfileConfiguration': + def parse_arg(cls, arg: _ProfileConfigurationSerialization) -> "ProfileConfiguration": from ..profile.profiles_handler import profile_handler - profile = profile_handler.parse_profile_config(arg['profile']) - greeter = arg.get('greeter', None) - gfx_driver = arg.get('gfx_driver', None) + + profile = profile_handler.parse_profile_config(arg["profile"]) + greeter = arg.get("greeter", None) + gfx_driver = arg.get("gfx_driver", None) return ProfileConfiguration( profile, GfxDriver(gfx_driver) if gfx_driver else None, - GreeterType(greeter) if greeter else None + GreeterType(greeter) if greeter else None, ) diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py index ca720e2a..64719a1e 100644 --- a/archinstall/lib/models/users.py +++ b/archinstall/lib/models/users.py @@ -13,37 +13,37 @@ if TYPE_CHECKING: class PasswordStrength(Enum): - VERY_WEAK = 'very weak' - WEAK = 'weak' - MODERATE = 'moderate' - STRONG = 'strong' + VERY_WEAK = "very weak" + WEAK = "weak" + MODERATE = "moderate" + STRONG = "strong" @property @override def value(self) -> str: # pylint: disable=invalid-overridden-method match self: case PasswordStrength.VERY_WEAK: - return str(_('very weak')) + return str(_("very weak")) case PasswordStrength.WEAK: - return str(_('weak')) + return str(_("weak")) case PasswordStrength.MODERATE: - return str(_('moderate')) + return str(_("moderate")) case PasswordStrength.STRONG: - return str(_('strong')) + return str(_("strong")) def color(self) -> str: match self: case PasswordStrength.VERY_WEAK: - return 'red' + return "red" case PasswordStrength.WEAK: - return 'red' + return "red" case PasswordStrength.MODERATE: - return 'yellow' + return "yellow" case PasswordStrength.STRONG: - return 'green' + return "green" @classmethod - def strength(cls, password: str) -> 'PasswordStrength': + def strength(cls, password: str) -> "PasswordStrength": digit = any(character.isdigit() for character in password) upper = any(character.isupper() for character in password) lower = any(character.islower() for character in password) @@ -57,8 +57,8 @@ class PasswordStrength(Enum): upper: bool, lower: bool, symbol: bool, - length: int - ) -> 'PasswordStrength': + length: int, + ) -> "PasswordStrength": # suggested evaluation # https://github.com/archlinux/archinstall/issues/1304#issuecomment-1146768163 if digit and upper and lower and symbol: @@ -106,28 +106,28 @@ class PasswordStrength(Enum): _UserSerialization = TypedDict( - '_UserSerialization', + "_UserSerialization", { - 'username': str, - '!password': NotRequired[str], - 'sudo': bool, - 'groups': list[str], - 'enc_password': str | None - } + "username": str, + "!password": NotRequired[str], + "sudo": bool, + "groups": list[str], + "enc_password": str | None, + }, ) class Password: def __init__( self, - plaintext: str = '', - enc_password: str | None = None + plaintext: str = "", + enc_password: str | None = None, ): if plaintext: enc_password = crypt_yescrypt(plaintext) if not plaintext and not enc_password: - raise ValueError('Either plaintext or enc_password must be provided') + raise ValueError("Either plaintext or enc_password must be provided") self._plaintext = plaintext self.enc_password = enc_password @@ -153,9 +153,9 @@ class Password: def hidden(self) -> str: if self._plaintext: - return '*' * len(self._plaintext) + return "*" * len(self._plaintext) else: - return '*' * 8 + return "*" * 8 @dataclass @@ -168,37 +168,37 @@ class User: @override def __str__(self) -> str: # safety overwrite to make sure password is not leaked - return f'User({self.username=}, {self.sudo=}, {self.groups=})' + return f"User({self.username=}, {self.sudo=}, {self.groups=})" def table_data(self) -> dict[str, str | bool | list[str]]: return { - 'username': self.username, - 'password': self.password.hidden(), - 'sudo': self.sudo, - 'groups': self.groups + "username": self.username, + "password": self.password.hidden(), + "sudo": self.sudo, + "groups": self.groups, } def json(self) -> _UserSerialization: return { - 'username': self.username, - 'enc_password': self.password.enc_password, - 'sudo': self.sudo, - 'groups': self.groups + "username": self.username, + "enc_password": self.password.enc_password, + "sudo": self.sudo, + "groups": self.groups, } @classmethod def parse_arguments( cls, - args: list[_UserSerialization] - ) -> list['User']: + args: list[_UserSerialization], + ) -> list["User"]: users: list[User] = [] for entry in args: - username = entry.get('username') + username = entry.get("username") password: Password | None = None - groups = entry.get('groups', []) - plaintext = entry.get('!password') - enc_password = entry.get('enc_password') + groups = entry.get("groups", []) + plaintext = entry.get("!password") + enc_password = entry.get("enc_password") # DEPRECATED: backwards compatibility if plaintext: @@ -212,8 +212,8 @@ class User: user = User( username=username, password=password, - sudo=entry.get('sudo', False) is True, - groups=groups + sudo=entry.get("sudo", False) is True, + groups=groups, ) users.append(user) diff --git a/archinstall/lib/networking.py b/archinstall/lib/networking.py index edc7054f..1b7679ef 100644 --- a/archinstall/lib/networking.py +++ b/archinstall/lib/networking.py @@ -18,16 +18,17 @@ from .pacman import Pacman class DownloadTimer: - ''' + """ Context manager for timing downloads with timeouts. - ''' + """ + def __init__(self, timeout: int = 5): - ''' + """ Args: timeout: The download timeout in seconds. The DownloadTimeout exception will be raised in the context after this many seconds. - ''' + """ self.time: float | None = None self.start_time: float | None = None self.timeout = timeout @@ -35,10 +36,10 @@ class DownloadTimer: self.previous_timer: int | None = None def raise_timeout(self, signl: int, frame: FrameType | None) -> None: - ''' + """ Raise the DownloadTimeout exception. - ''' - raise DownloadTimeout(f'Download timed out after {self.timeout} second(s).') + """ + raise DownloadTimeout(f"Download timed out after {self.timeout} second(s).") def __enter__(self) -> Self: if self.timeout > 0: @@ -69,9 +70,10 @@ class DownloadTimer: def get_hw_addr(ifname: str) -> str: import fcntl + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - ret = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', bytes(ifname, 'utf-8')[:15])) - return ':'.join(f'{b:02x}' for b in ret[18:24]) + ret = fcntl.ioctl(s.fileno(), 0x8927, struct.pack("256s", bytes(ifname, "utf-8")[:15])) + return ":".join(f"{b:02x}" for b in ret[18:24]) def list_interfaces(skip_loopback: bool = True) -> dict[str, str]: @@ -81,7 +83,7 @@ def list_interfaces(skip_loopback: bool = True) -> dict[str, str]: if skip_loopback and iface == "lo": continue - mac = get_hw_addr(iface).replace(':', '-').lower() + mac = get_hw_addr(iface).replace(":", "-").lower() interfaces[mac] = iface return interfaces @@ -104,17 +106,17 @@ def enrich_iface_types(interfaces: list[str]) -> dict[str, str]: for iface in interfaces: if os.path.isdir(f"/sys/class/net/{iface}/bridge/"): - result[iface] = 'BRIDGE' + result[iface] = "BRIDGE" elif os.path.isfile(f"/sys/class/net/{iface}/tun_flags"): # ethtool -i {iface} - result[iface] = 'TUN/TAP' + result[iface] = "TUN/TAP" elif os.path.isdir(f"/sys/class/net/{iface}/device"): if os.path.isdir(f"/sys/class/net/{iface}/wireless/"): - result[iface] = 'WIRELESS' + result[iface] = "WIRELESS" else: - result[iface] = 'PHYSICAL' + result[iface] = "PHYSICAL" else: - result[iface] = 'UNKNOWN' + result[iface] = "UNKNOWN" return result @@ -126,28 +128,25 @@ def fetch_data_from_url(url: str, params: dict[str, str] | None = None) -> str: if params is not None: encoded = urlencode(params) - full_url = f'{url}?{encoded}' + full_url = f"{url}?{encoded}" else: full_url = url try: response = urlopen(full_url, context=ssl_context) - data = response.read().decode('UTF-8') + data = response.read().decode("UTF-8") return data except URLError as e: - raise ValueError(f'Unable to fetch data from url: {url}\n{e}') + raise ValueError(f"Unable to fetch data from url: {url}\n{e}") except Exception as e: - raise ValueError(f'Unexpected error when parsing response: {e}') + raise ValueError(f"Unexpected error when parsing response: {e}") def calc_checksum(icmp_packet: bytes) -> int: # Calculate the ICMP checksum checksum = 0 for i in range(0, len(icmp_packet), 2): - checksum += (icmp_packet[i] << 8) + ( - struct.unpack('B', icmp_packet[i + 1:i + 2])[0] - if len(icmp_packet[i + 1:i + 2]) else 0 - ) + checksum += (icmp_packet[i] << 8) + (struct.unpack("B", icmp_packet[i + 1 : i + 2])[0] if len(icmp_packet[i + 1 : i + 2]) else 0) checksum = (checksum >> 16) + (checksum & 0xFFFF) checksum = ~checksum & 0xFFFF @@ -157,17 +156,17 @@ def calc_checksum(icmp_packet: bytes) -> int: def build_icmp(payload: bytes) -> bytes: # Define the ICMP Echo Request packet - icmp_packet = struct.pack('!BBHHH', 8, 0, 0, 0, 1) + payload + icmp_packet = struct.pack("!BBHHH", 8, 0, 0, 0, 1) + payload checksum = calc_checksum(icmp_packet) - return struct.pack('!BBHHH', 8, 0, checksum, 0, 1) + payload + return struct.pack("!BBHHH", 8, 0, checksum, 0, 1) + payload def ping(hostname, timeout: int = 5) -> int: watchdog = select.epoll() started = time.time() - random_identifier = f'archinstall-{random.randint(1000, 9999)}'.encode() + random_identifier = f"archinstall-{random.randint(1000, 9999)}".encode() # Create a raw socket (requires root, which should be fine on archiso) icmp_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) @@ -185,10 +184,10 @@ def ping(hostname, timeout: int = 5) -> int: try: for _fileno, _event in watchdog.poll(0.1): response, _ = icmp_socket.recvfrom(1024) - icmp_type = struct.unpack('!B', response[20:21])[0] + icmp_type = struct.unpack("!B", response[20:21])[0] # Check if it's an Echo Reply (ICMP type 0) - if icmp_type == 0 and response[-len(random_identifier):] == random_identifier: + if icmp_type == 0 and response[-len(random_identifier) :] == random_identifier: latency = round((time.time() - started) * 1000) break except OSError as e: diff --git a/archinstall/lib/output.py b/archinstall/lib/output.py index c869b20c..c7e49a76 100644 --- a/archinstall/lib/output.py +++ b/archinstall/lib/output.py @@ -16,13 +16,12 @@ if TYPE_CHECKING: class FormattedOutput: - @classmethod def _get_values( cls, - o: 'DataclassInstance', + o: "DataclassInstance", class_formatter: str | Callable | None = None, # type: ignore[type-arg] - filter_list: list[str] = [] + filter_list: list[str] = [], ) -> dict[str, Any]: """ the original values returned a dataclass as dict thru the call to some specific methods @@ -39,10 +38,10 @@ class FormattedOutput: func = getattr(o, class_formatter) return func(filter_list) - raise ValueError('Unsupported formatting call') - elif hasattr(o, 'table_data'): + raise ValueError("Unsupported formatting call") + elif hasattr(o, "table_data"): return o.table_data() - elif hasattr(o, 'json'): + elif hasattr(o, "json"): return o.json() elif is_dataclass(o): return asdict(o) @@ -55,9 +54,9 @@ class FormattedOutput: obj: list[Any], class_formatter: str | Callable | None = None, # type: ignore[type-arg] filter_list: list[str] = [], - capitalize: bool = False + capitalize: bool = False, ) -> str: - """ variant of as_table (subtly different code) which has two additional parameters + """variant of as_table (subtly different code) which has two additional parameters filter which is a list of fields which will be shon class_formatter a special method to format the outgoing data @@ -79,36 +78,36 @@ class FormattedOutput: filter_list = list(column_width.keys()) # create the header lines - output = '' + output = "" key_list = [] for key in filter_list: width = column_width[key] - key = key.replace('!', '').replace('_', ' ') + key = key.replace("!", "").replace("_", " ") if capitalize: key = key.capitalize() key_list.append(unicode_ljust(key, width)) - output += ' | '.join(key_list) + '\n' - output += '-' * len(output) + '\n' + output += " | ".join(key_list) + "\n" + output += "-" * len(output) + "\n" # create the data lines for record in raw_data: obj_data = [] for key in filter_list: width = column_width.get(key, len(key)) - value = record.get(key, '') + value = record.get(key, "") - if '!' in key: - value = '*' * len(value) + if "!" in key: + value = "*" * len(value) if isinstance(value, int | float) or (isinstance(value, str) and value.isnumeric()): obj_data.append(unicode_rjust(str(value), width)) else: obj_data.append(unicode_ljust(str(value), width)) - output += ' | '.join(obj_data) + '\n' + output += " | ".join(obj_data) + "\n" return output @@ -118,14 +117,14 @@ class FormattedOutput: Will format a list into a given number of columns """ chunks = [] - output = '' + output = "" for i in range(0, len(entries), cols): - chunks.append(entries[i:i + cols]) + chunks.append(entries[i : i + cols]) for row in chunks: - out_fmt = '{: <30} ' * len(row) - output += out_fmt.format(*row) + '\n' + out_fmt = "{: <30} " * len(row) + output += out_fmt.format(*row) + "\n" return output @@ -138,7 +137,7 @@ class Journald: except ModuleNotFoundError: return None - log_adapter = logging.getLogger('archinstall') + log_adapter = logging.getLogger("archinstall") log_fmt = logging.Formatter("[%(levelname)s]: %(message)s") log_ch = systemd.journal.JournalHandler() log_ch.setFormatter(log_fmt) @@ -149,11 +148,11 @@ class Journald: def _check_log_permissions() -> None: - filename = storage.get('LOG_FILE', None) - log_dir = storage.get('LOG_PATH', Path('./')) + filename = storage.get("LOG_FILE", None) + log_dir = storage.get("LOG_PATH", Path("./")) if not filename: - raise ValueError('No log file name defined') + raise ValueError("No log file name defined") log_file = log_dir / filename @@ -161,17 +160,17 @@ def _check_log_permissions() -> None: log_dir.mkdir(exist_ok=True, parents=True) log_file.touch(exist_ok=True) - with log_file.open('a') as fp: - fp.write('') + with log_file.open("a") as fp: + fp.write("") except PermissionError: # Fallback to creating the log file in the current folder - fallback_dir = Path('./').absolute() + fallback_dir = Path("./").absolute() fallback_log_file = fallback_dir / filename fallback_log_file.touch(exist_ok=True) - storage['LOG_PATH'] = fallback_dir - warn(f'Not enough permission to place log file at {log_file}, creating it in {fallback_log_file} instead') + storage["LOG_PATH"] = fallback_dir + warn(f"Not enough permission to place log file at {log_file}, creating it in {fallback_log_file} instead") def _supports_color() -> bool: @@ -184,20 +183,20 @@ def _supports_color() -> bool: Return True if the running system's terminal supports color, and False otherwise. """ - supported_platform = sys.platform != 'win32' or 'ANSICON' in os.environ + supported_platform = sys.platform != "win32" or "ANSICON" in os.environ # isatty is not always implemented, #6223. - is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + is_a_tty = hasattr(sys.stdout, "isatty") and sys.stdout.isatty() return supported_platform and is_a_tty class Font(Enum): - bold = '1' - italic = '3' - underscore = '4' - blink = '5' - reverse = '7' - conceal = '8' + bold = "1" + italic = "3" + underscore = "4" + blink = "5" + reverse = "7" + conceal = "8" def _stylize_output( @@ -216,29 +215,29 @@ def _stylize_output( Adds styling to a text given a set of color arguments. """ colors = { - 'black': '0', - 'red': '1', - 'green': '2', - 'yellow': '3', - 'blue': '4', - 'magenta': '5', - 'cyan': '6', - 'white': '7', - 'teal': '8;5;109', # Extended 256-bit colors (not always supported) - 'orange': '8;5;208', # https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors - 'darkorange': '8;5;202', - 'gray': '8;5;246', - 'grey': '8;5;246', - 'darkgray': '8;5;240', - 'lightgray': '8;5;256' + "black": "0", + "red": "1", + "green": "2", + "yellow": "3", + "blue": "4", + "magenta": "5", + "cyan": "6", + "white": "7", + "teal": "8;5;109", # Extended 256-bit colors (not always supported) + "orange": "8;5;208", # https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors + "darkorange": "8;5;202", + "gray": "8;5;246", + "grey": "8;5;246", + "darkgray": "8;5;240", + "lightgray": "8;5;256", } - foreground = {key: f'3{colors[key]}' for key in colors} - background = {key: f'4{colors[key]}' for key in colors} + foreground = {key: f"3{colors[key]}" for key in colors} + background = {key: f"4{colors[key]}" for key in colors} code_list = [] - if text == '' and reset: - return '\x1b[0m' + if text == "" and reset: + return "\x1b[0m" code_list.append(foreground[str(fg)]) @@ -248,34 +247,34 @@ def _stylize_output( for o in font: code_list.append(o.value) - ansi = ';'.join(code_list) + ansi = ";".join(code_list) - return f'\033[{ansi}m{text}\033[0m' + return f"\033[{ansi}m{text}\033[0m" def info( *msgs: str, level: int = logging.INFO, - fg: str = 'white', + fg: str = "white", bg: str | None = None, reset: bool = False, - font: list[Font] = [] + font: list[Font] = [], ) -> None: log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) def _timestamp() -> str: now = datetime.now(tz=UTC) - return now.strftime('%Y-%m-%d %H:%M:%S') + return now.strftime("%Y-%m-%d %H:%M:%S") def debug( *msgs: str, level: int = logging.DEBUG, - fg: str = 'white', + fg: str = "white", bg: str | None = None, reset: bool = False, - font: list[Font] = [] + font: list[Font] = [], ) -> None: log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) @@ -283,10 +282,10 @@ def debug( def error( *msgs: str, level: int = logging.ERROR, - fg: str = 'red', + fg: str = "red", bg: str | None = None, reset: bool = False, - font: list[Font] = [] + font: list[Font] = [], ) -> None: log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) @@ -294,10 +293,10 @@ def error( def warn( *msgs: str, level: int = logging.WARNING, - fg: str = 'yellow', + fg: str = "yellow", bg: str | None = None, reset: bool = False, - font: list[Font] = [] + font: list[Font] = [], ) -> None: log(*msgs, level=level, fg=fg, bg=bg, reset=reset, font=font) @@ -305,25 +304,25 @@ def warn( def log( *msgs: str, level: int = logging.INFO, - fg: str = 'white', + fg: str = "white", bg: str | None = None, reset: bool = False, - font: list[Font] = [] + font: list[Font] = [], ) -> None: # leave this check here as we need to setup the logging # right from the beginning when the modules are loaded _check_log_permissions() - text = orig_string = ' '.join([str(x) for x in msgs]) + text = orig_string = " ".join([str(x) for x in msgs]) # Attempt to colorize the output if supported # Insert default colors and override with **kwargs if _supports_color(): text = _stylize_output(text, fg, bg, reset, font) - log_file = storage['LOG_PATH'] / storage['LOG_FILE'] + log_file = storage["LOG_PATH"] / storage["LOG_FILE"] - with log_file.open('a') as fp: + with log_file.open("a") as fp: ts = _timestamp() level_name = logging.getLevelName(level) out = f"[{ts}] - {level_name} - {orig_string}\n" @@ -333,4 +332,5 @@ def log( if level != logging.DEBUG: from archinstall.tui.curses_menu import Tui + Tui.print(text) diff --git a/archinstall/lib/packages/__init__.py b/archinstall/lib/packages/__init__.py index d04d0645..ed9b67a7 100644 --- a/archinstall/lib/packages/__init__.py +++ b/archinstall/lib/packages/__init__.py @@ -1,11 +1,11 @@ from .packages import find_package, find_packages, group_search, installed_package, list_available_packages, package_search, validate_package_list __all__ = [ - 'find_package', - 'find_packages', - 'group_search', - 'installed_package', - 'list_available_packages', - 'package_search', - 'validate_package_list', + "find_package", + "find_packages", + "group_search", + "installed_package", + "list_available_packages", + "package_search", + "validate_package_list", ] diff --git a/archinstall/lib/packages/packages.py b/archinstall/lib/packages/packages.py index e2c476fd..487d0817 100644 --- a/archinstall/lib/packages/packages.py +++ b/archinstall/lib/packages/packages.py @@ -11,9 +11,9 @@ from ..models.packages import AvailablePackage, LocalPackage, PackageSearch, Pac from ..output import debug from ..pacman import Pacman -BASE_URL_PKG_SEARCH = 'https://archlinux.org/packages/search/json/' +BASE_URL_PKG_SEARCH = "https://archlinux.org/packages/search/json/" # BASE_URL_PKG_CONTENT = 'https://archlinux.org/packages/search/json/' -BASE_GROUP_URL = 'https://archlinux.org/groups/search/json/' +BASE_GROUP_URL = "https://archlinux.org/groups/search/json/" def _make_request(url: str, params: dict[str, str]) -> addinfourl: @@ -22,7 +22,7 @@ def _make_request(url: str, params: dict[str, str]) -> addinfourl: ssl_context.verify_mode = ssl.CERT_NONE encoded = urlencode(params) - full_url = f'{url}?{encoded}' + full_url = f"{url}?{encoded}" return urlopen(full_url, context=ssl_context) @@ -30,7 +30,7 @@ def _make_request(url: str, params: dict[str, str]) -> addinfourl: def group_search(name: str) -> list[PackageSearchResult]: # TODO UPSTREAM: Implement /json/ for the groups search try: - response = _make_request(BASE_GROUP_URL, {'name': name}) + response = _make_request(BASE_GROUP_URL, {"name": name}) except HTTPError as err: if err.code == 404: return [] @@ -38,9 +38,9 @@ def group_search(name: str) -> list[PackageSearchResult]: raise err # Just to be sure some code didn't slip through the exception - data = response.read().decode('utf-8') + data = response.read().decode("utf-8") - return [PackageSearchResult(**package) for package in json.loads(data)['results']] + return [PackageSearchResult(**package) for package in json.loads(data)["results"]] def package_search(package: str) -> PackageSearch: @@ -50,12 +50,12 @@ def package_search(package: str) -> PackageSearch: """ # TODO UPSTREAM: Implement bulk search, either support name=X&name=Y or split on space (%20 or ' ') # TODO: utilize pacman cache first, upstream second. - response = _make_request(BASE_URL_PKG_SEARCH, {'name': package}) + response = _make_request(BASE_URL_PKG_SEARCH, {"name": package}) if response.code != 200: raise PackageError(f"Could not locate package: [{response.code}] {response}") - data = response.read().decode('UTF-8') + data = response.read().decode("UTF-8") json_data = json.loads(data) return PackageSearch.from_json(json_data) @@ -107,7 +107,7 @@ def validate_package_list(packages: list[str]) -> tuple[list[str], list[str]]: def installed_package(package: str) -> LocalPackage | None: package_info = [] try: - package_info = Pacman.run(f'-Q --info {package}').decode().split('\n') + package_info = Pacman.run(f"-Q --info {package}").decode().split("\n") return _parse_package_output(package_info, LocalPackage) except SysCallError: pass @@ -117,7 +117,7 @@ def installed_package(package: str) -> LocalPackage | None: @lru_cache def list_available_packages( - repositories: tuple[Repository] + repositories: tuple[Repository], ) -> dict[str, AvailablePackage]: """ Returns a list of all available packages in the database @@ -129,13 +129,13 @@ def list_available_packages( try: Pacman.run("-Sy") except Exception as e: - debug(f'Failed to sync Arch Linux package database: {e}') + debug(f"Failed to sync Arch Linux package database: {e}") - for line in Pacman.run('-S --info'): + for line in Pacman.run("-S --info"): dec_line = line.decode().strip() current_package.append(dec_line) - if dec_line.startswith('Validated'): + if dec_line.startswith("Validated"): if current_package: avail_pkg = _parse_package_output(current_package, AvailablePackage) if avail_pkg.repository in filtered_repos: @@ -147,18 +147,18 @@ def list_available_packages( @lru_cache(maxsize=128) def _normalize_key_name(key: str) -> str: - return key.strip().lower().replace(' ', '_') + return key.strip().lower().replace(" ", "_") def _parse_package_output[PackageType: (AvailablePackage, LocalPackage)]( package_meta: list[str], - cls: type[PackageType] + cls: type[PackageType], ) -> PackageType: package = {} for line in package_meta: - if ':' in line: - key, value = line.split(':', 1) + if ":" in line: + key, value = line.split(":", 1) key = _normalize_key_name(key) package[key] = value.strip() diff --git a/archinstall/lib/pacman/__init__.py b/archinstall/lib/pacman/__init__.py index 45eb8284..4befba28 100644 --- a/archinstall/lib/pacman/__init__.py +++ b/archinstall/lib/pacman/__init__.py @@ -16,33 +16,32 @@ if TYPE_CHECKING: class Pacman: - def __init__(self, target: Path, silent: bool = False): self.synced = False self.silent = silent self.target = target @staticmethod - def run(args: str, default_cmd: str = 'pacman') -> SysCommand: + def run(args: str, default_cmd: str = "pacman") -> SysCommand: """ A centralized function to call `pacman` from. It also protects us from colliding with other running pacman sessions (if used locally). The grace period is set to 10 minutes before exiting hard if another pacman instance is running. """ - pacman_db_lock = Path('/var/lib/pacman/db.lck') + pacman_db_lock = Path("/var/lib/pacman/db.lck") if pacman_db_lock.exists(): - warn(str(_('Pacman is already running, waiting maximum 10 minutes for it to terminate.'))) + warn(str(_("Pacman is already running, waiting maximum 10 minutes for it to terminate."))) started = time.time() while pacman_db_lock.exists(): time.sleep(0.25) if time.time() - started > (60 * 10): - error(str(_('Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall.'))) + error(str(_("Pre-existing pacman lock never exited. Please clean up any existing pacman sessions before using archinstall."))) exit(1) - return SysCommand(f'{default_cmd} {args}') + return SysCommand(f"{default_cmd} {args}") def ask(self, error_message: str, bail_message: str, func: Callable, *args, **kwargs) -> None: # type: ignore[type-arg] while True: @@ -50,20 +49,20 @@ class Pacman: func(*args, **kwargs) break except Exception as err: - error(f'{error_message}: {err}') - if not self.silent and input('Would you like to re-try this download? (Y/n): ').lower().strip() in 'y': + error(f"{error_message}: {err}") + if not self.silent and input("Would you like to re-try this download? (Y/n): ").lower().strip() in "y": continue - raise RequirementError(f'{bail_message}: {err}') + raise RequirementError(f"{bail_message}: {err}") def sync(self) -> None: if self.synced: return self.ask( - 'Could not sync a new package database', - 'Could not sync mirrors', + "Could not sync a new package database", + "Could not sync mirrors", self.run, - '-Syy', - default_cmd='pacman' + "-Syy", + default_cmd="pacman", ) self.synced = True @@ -73,22 +72,22 @@ class Pacman: packages = [packages] for plugin in plugins.values(): - if hasattr(plugin, 'on_pacstrap'): - if (result := plugin.on_pacstrap(packages)): + if hasattr(plugin, "on_pacstrap"): + if result := plugin.on_pacstrap(packages): packages = result - info(f'Installing packages: {packages}') + info(f"Installing packages: {packages}") self.ask( - 'Could not strap in packages', - 'Pacstrap failed. See /var/log/archinstall/install.log or above message for error details', + "Could not strap in packages", + "Pacstrap failed. See /var/log/archinstall/install.log or above message for error details", SysCommand, - f'pacstrap -C /etc/pacman.conf -K {self.target} {" ".join(packages)} --noconfirm', - peek_output=True + f"pacstrap -C /etc/pacman.conf -K {self.target} {' '.join(packages)} --noconfirm", + peek_output=True, ) __all__ = [ - 'Pacman', - 'PacmanConfig', + "Pacman", + "PacmanConfig", ] diff --git a/archinstall/lib/pacman/config.py b/archinstall/lib/pacman/config.py index 6ea75d4e..54ee039a 100644 --- a/archinstall/lib/pacman/config.py +++ b/archinstall/lib/pacman/config.py @@ -27,7 +27,7 @@ class PacmanConfig: repos_to_enable = [] for repo in self._repositories: if repo == Repository.Testing: - repos_to_enable.extend(['core-testing', 'extra-testing', 'multilib-testing']) + repos_to_enable.extend(["core-testing", "extra-testing", "multilib-testing"]) else: repos_to_enable.append(repo.value) @@ -35,18 +35,18 @@ class PacmanConfig: for row, line in enumerate(content): # Check if this is a commented repository section that needs to be enabled - match = re.match(r'^#\s*\[(.*)\]', line) + match = re.match(r"^#\s*\[(.*)\]", line) if match and match.group(1) in repos_to_enable: # uncomment the repository section line, properly removing # and any spaces - content[row] = re.sub(r'^#\s*', '', line) + content[row] = re.sub(r"^#\s*", "", line) # also uncomment the next line (Include statement) if it exists and is commented - if row + 1 < len(content) and content[row + 1].lstrip().startswith('#'): - content[row + 1] = re.sub(r'^#\s*', '', content[row + 1]) + if row + 1 < len(content) and content[row + 1].lstrip().startswith("#"): + content[row + 1] = re.sub(r"^#\s*", "", content[row + 1]) # Write the modified content back to the file - with open(self._config_path, 'w') as f: + with open(self._config_path, "w") as f: f.writelines(content) def persist(self) -> None: diff --git a/archinstall/lib/plugins.py b/archinstall/lib/plugins.py index f1848219..376a3a76 100644 --- a/archinstall/lib/plugins.py +++ b/archinstall/lib/plugins.py @@ -16,15 +16,15 @@ plugins = {} # 1: List archinstall.plugin definitions # 2: Load the plugin entrypoint # 3: Initiate the plugin and store it as .name in plugins -for plugin_definition in metadata.entry_points().select(group='archinstall.plugin'): +for plugin_definition in metadata.entry_points().select(group="archinstall.plugin"): plugin_entrypoint = plugin_definition.load() try: plugins[plugin_definition.name] = plugin_entrypoint() except Exception as err: error( - f'Error: {err}', - f"The above error was detected when loading the plugin: {plugin_definition}" + f"Error: {err}", + f"The above error was detected when loading the plugin: {plugin_definition}", ) @@ -34,11 +34,11 @@ def _localize_path(path: Path) -> Path: """ url = urllib.parse.urlparse(str(path)) - if url.scheme and url.scheme in ('https', 'http'): - converted_path = Path(f'/tmp/{path.stem}_{hashlib.md5(os.urandom(12)).hexdigest()}.py') + if url.scheme and url.scheme in ("https", "http"): + converted_path = Path(f"/tmp/{path.stem}_{hashlib.md5(os.urandom(12)).hexdigest()}.py") with open(converted_path, "w") as temp_file: - temp_file.write(urllib.request.urlopen(url.geturl()).read().decode('utf-8')) + temp_file.write(urllib.request.urlopen(url.geturl()).read().decode("utf-8")) return converted_path else: @@ -49,7 +49,7 @@ def _import_via_path(path: Path, namespace: str | None = None) -> str | None: if not namespace: namespace = os.path.basename(path) - if namespace == '__init__.py': + if namespace == "__init__.py": namespace = path.parent.name try: @@ -62,8 +62,8 @@ def _import_via_path(path: Path, namespace: str | None = None) -> str | None: return namespace except Exception as err: error( - f'Error: {err}', - f"The above error was detected when loading the plugin: {path}" + f"Error: {err}", + f"The above error was detected when loading the plugin: {path}", ) try: @@ -91,29 +91,29 @@ def load_plugin(path: Path) -> None: # Path was not found in any known examples, check if it's an absolute path if os.path.isfile(path): namespace = _import_via_path(path) - elif parsed_url.scheme in ('https', 'http'): + elif parsed_url.scheme in ("https", "http"): localized = _localize_path(path) namespace = _import_via_path(localized) if namespace and namespace in sys.modules: # Version dependency via __archinstall__version__ variable (if present) in the plugin # Any errors in version inconsistency will be handled through normal error handling if not defined. - if hasattr(sys.modules[namespace], '__archinstall__version__'): - archinstall_major_and_minor_version = float(storage['__version__'][:_find_nth(storage['__version__'], '.', 2)]) + if hasattr(sys.modules[namespace], "__archinstall__version__"): + archinstall_major_and_minor_version = float(storage["__version__"][: _find_nth(storage["__version__"], ".", 2)]) if sys.modules[namespace].__archinstall__version__ < archinstall_major_and_minor_version: error(f"Plugin {sys.modules[namespace]} does not support the current Archinstall version.") # Locate the plugin entry-point called Plugin() # This in accordance with the entry_points() from setup.cfg above - if hasattr(sys.modules[namespace], 'Plugin'): + if hasattr(sys.modules[namespace], "Plugin"): try: plugins[namespace] = sys.modules[namespace].Plugin() info(f"Plugin {plugins[namespace]} has been loaded.") except Exception as err: error( - f'Error: {err}', - f"The above error was detected when initiating the plugin: {path}" + f"Error: {err}", + f"The above error was detected when initiating the plugin: {path}", ) else: warn(f"Plugin '{path}' is missing a valid entry-point or is corrupt.") diff --git a/archinstall/lib/profile/profile_menu.py b/archinstall/lib/profile/profile_menu.py index b5e09ef7..2c2c066e 100644 --- a/archinstall/lib/profile/profile_menu.py +++ b/archinstall/lib/profile/profile_menu.py @@ -24,7 +24,7 @@ if TYPE_CHECKING: class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): def __init__( self, - preset: ProfileConfiguration | None = None + preset: ProfileConfiguration | None = None, ): if preset: self._profile_config = preset @@ -37,36 +37,36 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): super().__init__( self._item_group, self._profile_config, - allow_reset=True + allow_reset=True, ) def _define_menu_options(self) -> list[MenuItem]: return [ MenuItem( - text=str(_('Type')), + text=str(_("Type")), action=self._select_profile, value=self._profile_config.profile, preview_action=self._preview_profile, - key='profile' + key="profile", ), MenuItem( - text=str(_('Graphics driver')), + text=str(_("Graphics driver")), action=self._select_gfx_driver, value=self._profile_config.gfx_driver if self._profile_config.profile and self._profile_config.profile.is_graphic_driver_supported() else None, preview_action=self._prev_gfx, enabled=self._profile_config.profile.is_graphic_driver_supported() if self._profile_config.profile else False, - dependencies=['profile'], - key='gfx_driver', + dependencies=["profile"], + key="gfx_driver", ), MenuItem( - text=str(_('Greeter')), + text=str(_("Greeter")), action=lambda x: select_greeter(preset=x), value=self._profile_config.greeter if self._profile_config.profile and self._profile_config.profile.is_greeter_supported() else None, enabled=self._profile_config.profile.is_graphic_driver_supported() if self._profile_config.profile else False, preview_action=self._prev_greeter, - dependencies=['profile'], - key='greeter', - ) + dependencies=["profile"], + key="greeter", + ), ] @override @@ -79,36 +79,36 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): if profile is not None: if not profile.is_graphic_driver_supported(): - self._item_group.find_by_key('gfx_driver').enabled = False - self._item_group.find_by_key('gfx_driver').value = None + self._item_group.find_by_key("gfx_driver").enabled = False + self._item_group.find_by_key("gfx_driver").value = None else: - self._item_group.find_by_key('gfx_driver').enabled = True - self._item_group.find_by_key('gfx_driver').value = GfxDriver.AllOpenSource + self._item_group.find_by_key("gfx_driver").enabled = True + self._item_group.find_by_key("gfx_driver").value = GfxDriver.AllOpenSource if not profile.is_greeter_supported(): - self._item_group.find_by_key('greeter').enabled = False - self._item_group.find_by_key('greeter').value = None + self._item_group.find_by_key("greeter").enabled = False + self._item_group.find_by_key("greeter").value = None else: - self._item_group.find_by_key('greeter').enabled = True - self._item_group.find_by_key('greeter').value = profile.default_greeter_type + self._item_group.find_by_key("greeter").enabled = True + self._item_group.find_by_key("greeter").value = profile.default_greeter_type else: - self._item_group.find_by_key('gfx_driver').value = None - self._item_group.find_by_key('greeter').value = None + self._item_group.find_by_key("gfx_driver").value = None + self._item_group.find_by_key("greeter").value = None return profile def _select_gfx_driver(self, preset: GfxDriver | None = None) -> GfxDriver | None: driver = preset - profile: Profile | None = self._item_group.find_by_key('profile').value + profile: Profile | None = self._item_group.find_by_key("profile").value if profile: if profile.is_graphic_driver_supported(): driver = select_driver(preset=preset) - if driver and 'Sway' in profile.current_selection_names(): + if driver and "Sway" in profile.current_selection_names(): if driver.is_nvidia(): - header = str(_('The proprietary Nvidia driver is not supported by Sway.')) + '\n' - header += str(_('It is likely that you will run into issues, are you okay with that?')) + '\n' + header = str(_("The proprietary Nvidia driver is not supported by Sway.")) + "\n" + header += str(_("It is likely that you will run into issues, are you okay with that?")) + "\n" group = MenuItemGroup.yes_no() group.focus_item = MenuItem.no() @@ -120,7 +120,7 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): allow_skip=False, columns=2, orientation=Orientation.HORIZONTAL, - alignment=Alignment.CENTER + alignment=Alignment.CENTER, ).run() if result.item() == MenuItem.no(): @@ -132,25 +132,25 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): if item.value: driver = item.get_value().value packages = item.get_value().packages_text() - return f'Driver: {driver}\n{packages}' + return f"Driver: {driver}\n{packages}" return None def _prev_greeter(self, item: MenuItem) -> str | None: if item.value: - return f'{_("Greeter")}: {item.value.value}' + return f"{_('Greeter')}: {item.value.value}" return None def _preview_profile(self, item: MenuItem) -> str | None: profile: Profile | None = item.value - text = '' + text = "" if profile: if (sub_profiles := profile.current_selection) is not None: - text += str(_('Selected profiles: ')) - text += ', '.join([p.name for p in sub_profiles]) + '\n' + text += str(_("Selected profiles: ")) + text += ", ".join([p.name for p in sub_profiles]) + "\n" if packages := profile.packages_text(include_sub_packages=True): - text += f'{packages}' + text += f"{packages}" if text: return text @@ -160,7 +160,7 @@ class ProfileMenu(AbstractSubMenu[ProfileConfiguration]): def select_greeter( profile: Profile | None = None, - preset: GreeterType | None = None + preset: GreeterType | None = None, ) -> GreeterType | None: if not profile or profile.is_greeter_supported(): items = [MenuItem(greeter.value, value=greeter) for greeter in GreeterType] @@ -178,8 +178,8 @@ def select_greeter( result = SelectMenu[GreeterType]( group, allow_skip=True, - frame=FrameProperties.min(str(_('Greeter'))), - alignment=Alignment.CENTER + frame=FrameProperties.min(str(_("Greeter"))), + alignment=Alignment.CENTER, ).run() match result.type_: @@ -188,7 +188,7 @@ def select_greeter( case ResultType.Selection: return result.get_value() case ResultType.Reset: - raise ValueError('Unhandled result type') + raise ValueError("Unhandled result type") return None @@ -199,10 +199,11 @@ def select_profile( allow_reset: bool = True, ) -> Profile | None: from archinstall.lib.profile.profiles_handler import profile_handler + top_level_profiles = profile_handler.get_top_level_profiles() if header is None: - header = str(_('This is a list of pre-programmed default_profiles')) + '\n' + header = str(_("This is a list of pre-programmed default_profiles")) + "\n" items = [MenuItem(p.name, value=p) for p in top_level_profiles] group = MenuItemGroup(items, sort_items=True) @@ -214,7 +215,7 @@ def select_profile( allow_reset=allow_reset, allow_skip=True, alignment=Alignment.CENTER, - frame=FrameProperties.min(str(_('Main profile'))) + frame=FrameProperties.min(str(_("Main profile"))), ).run() match result.type_: diff --git a/archinstall/lib/profile/profiles_handler.py b/archinstall/lib/profile/profiles_handler.py index 63469b6f..514df8b0 100644 --- a/archinstall/lib/profile/profiles_handler.py +++ b/archinstall/lib/profile/profiles_handler.py @@ -50,13 +50,13 @@ class ProfileHandler: if profile is not None: data = { - 'main': profile.name, - 'details': [profile.name for profile in profile.current_selection], - 'custom_settings': {profile.name: profile.custom_settings for profile in profile.current_selection} + "main": profile.name, + "details": [profile.name for profile in profile.current_selection], + "custom_settings": {profile.name: profile.custom_settings for profile in profile.current_selection}, } if self._url_path is not None: - data['path'] = self._url_path + data["path"] = self._url_path return data @@ -70,7 +70,7 @@ class ProfileHandler: # load all the default_profiles from url and custom # so that we can then apply whatever was specified # in the main/detail sections - if url_path := profile_config.get('path', None): + if url_path := profile_config.get("path", None): self._url_path = url_path local_path = Path(url_path) @@ -104,7 +104,7 @@ class ProfileHandler: # if custom_profile := self.get_profile_by_name('Custom'): # custom_profile.set_current_selection(custom_types) - if main := profile_config.get('main', None): + if main := profile_config.get("main", None): profile = self.get_profile_by_name(main) if main else None if not profile: @@ -112,14 +112,14 @@ class ProfileHandler: valid_sub_profiles: list[Profile] = [] invalid_sub_profiles: list[str] = [] - details: list[str] = profile_config.get('details', []) + details: list[str] = profile_config.get("details", []) if details: for detail in filter(None, details): # [2024-04-19] TODO: Backwards compatibility after naming change: https://github.com/archlinux/archinstall/pull/2421 # 'Kde' is deprecated, remove this block in a future version - if detail == 'Kde': - detail = 'KDE Plasma' + if detail == "Kde": + detail = "KDE Plasma" if sub_profile := self.get_profile_by_name(detail): valid_sub_profiles.append(sub_profile) @@ -127,9 +127,9 @@ class ProfileHandler: invalid_sub_profiles.append(detail) if invalid_sub_profiles: - info('No profile definition found: {}'.format(', '.join(invalid_sub_profiles))) + info("No profile definition found: {}".format(", ".join(invalid_sub_profiles))) - custom_settings = profile_config.get('custom_settings', {}) + custom_settings = profile_config.get("custom_settings", {}) profile.current_selection = valid_sub_profiles for sub_profile in valid_sub_profiles: @@ -186,28 +186,28 @@ class ProfileHandler: tailored = [p for p in self.profiles if p.is_tailored()] return [t for t in tailored if t.name in self._local_mac_addresses] - def install_greeter(self, install_session: 'Installer', greeter: GreeterType) -> None: + def install_greeter(self, install_session: "Installer", greeter: GreeterType) -> None: packages = [] service = None match greeter: case GreeterType.LightdmSlick: - packages = ['lightdm', 'lightdm-slick-greeter'] - service = ['lightdm'] + packages = ["lightdm", "lightdm-slick-greeter"] + service = ["lightdm"] case GreeterType.Lightdm: - packages = ['lightdm', 'lightdm-gtk-greeter'] - service = ['lightdm'] + packages = ["lightdm", "lightdm-gtk-greeter"] + service = ["lightdm"] case GreeterType.Sddm: - packages = ['sddm'] - service = ['sddm'] + packages = ["sddm"] + service = ["sddm"] case GreeterType.Gdm: - packages = ['gdm'] - service = ['gdm'] + packages = ["gdm"] + service = ["gdm"] case GreeterType.Ly: - packages = ['ly'] - service = ['ly'] + packages = ["ly"] + service = ["ly"] case GreeterType.CosmicSession: - packages = ['cosmic-greeter'] + packages = ["cosmic-greeter"] if packages: install_session.add_additional_packages(packages) @@ -216,35 +216,35 @@ class ProfileHandler: # slick-greeter requires a config change if greeter == GreeterType.LightdmSlick: - path = install_session.target.joinpath('etc/lightdm/lightdm.conf') + path = install_session.target.joinpath("etc/lightdm/lightdm.conf") with open(path) as file: filedata = file.read() - filedata = filedata.replace('#greeter-session=example-gtk-gnome', 'greeter-session=lightdm-slick-greeter') + filedata = filedata.replace("#greeter-session=example-gtk-gnome", "greeter-session=lightdm-slick-greeter") - with open(path, 'w') as file: + with open(path, "w") as file: file.write(filedata) - def install_gfx_driver(self, install_session: 'Installer', driver: GfxDriver) -> None: - debug(f'Installing GFX driver: {driver.value}') + def install_gfx_driver(self, install_session: "Installer", driver: GfxDriver) -> None: + debug(f"Installing GFX driver: {driver.value}") if driver in [GfxDriver.NvidiaOpenKernel, GfxDriver.NvidiaProprietary]: - headers = [f'{kernel}-headers' for kernel in install_session.kernels] + headers = [f"{kernel}-headers" for kernel in install_session.kernels] # Fixes https://github.com/archlinux/archinstall/issues/585 install_session.add_additional_packages(headers) elif driver in [GfxDriver.AllOpenSource, GfxDriver.AmdOpenSource]: # The order of these two are important if amdgpu is installed #808 - install_session.remove_mod('amdgpu') - install_session.remove_mod('radeon') + install_session.remove_mod("amdgpu") + install_session.remove_mod("radeon") - install_session.append_mod('amdgpu') - install_session.append_mod('radeon') + install_session.append_mod("amdgpu") + install_session.append_mod("radeon") driver_pkgs = driver.gfx_packages() pkg_names = [p.value for p in driver_pkgs] install_session.add_additional_packages(pkg_names) - def install_profile_config(self, install_session: 'Installer', profile_config: ProfileConfiguration) -> None: + def install_profile_config(self, install_session: "Installer", profile_config: ProfileConfiguration) -> None: profile = profile_config.profile if not profile: @@ -264,9 +264,9 @@ class ProfileHandler: """ try: data = fetch_data_from_url(url) - b_data = bytes(data, 'utf-8') + b_data = bytes(data, "utf-8") - with NamedTemporaryFile(delete=False, suffix='.py') as fp: + with NamedTemporaryFile(delete=False, suffix=".py") as fp: fp.write(b_data) filepath = Path(fp.name) @@ -274,7 +274,7 @@ class ProfileHandler: self.remove_custom_profiles(profiles) self.add_custom_profiles(profiles) except ValueError: - err = str(_('Unable to fetch profile from specified url: {}')).format(url) + err = str(_("Unable to fetch profile from specified url: {}")).format(url) error(err) def _load_profile_class(self, module: ModuleType) -> list[Profile]: @@ -292,7 +292,7 @@ class ProfileHandler: if isinstance(cls_, Profile): profiles.append(cls_) except Exception: - debug(f'Cannot import {module}, it does not appear to be a Profile class') + debug(f"Cannot import {module}, it does not appear to be a Profile class") return profiles @@ -305,7 +305,7 @@ class ProfileHandler: duplicates = [x for x in counter.items() if x[1] != 1] if len(duplicates) > 0: - err = str(_('Profiles must have unique name, but profile definitions with duplicate name found: {}')).format(duplicates[0][0]) + err = str(_("Profiles must have unique name, but profile definitions with duplicate name found: {}")).format(duplicates[0][0]) error(err) sys.exit(1) @@ -316,7 +316,7 @@ class ProfileHandler: """ with open(file) as fp: for line in fp.readlines(): - if '__packages__' in line: + if "__packages__" in line: return True return False @@ -325,15 +325,15 @@ class ProfileHandler: Process a file for profile definitions """ if self._is_legacy(file): - info(f'Cannot import {file} because it is no longer supported, please use the new profile format') + info(f"Cannot import {file} because it is no longer supported, please use the new profile format") return [] if not file.is_file(): - info(f'Cannot find profile file {file}') + info(f"Cannot find profile file {file}") return [] name = file.name.removesuffix(file.suffix) - debug(f'Importing profile: {file}') + debug(f"Importing profile: {file}") try: if spec := importlib.util.spec_from_file_location(name, file): @@ -342,7 +342,7 @@ class ProfileHandler: spec.loader.exec_module(imported) return self._load_profile_class(imported) except Exception as e: - error(f'Unable to parse file {file}: {e}') + error(f"Unable to parse file {file}: {e}") return [] @@ -350,11 +350,11 @@ class ProfileHandler: """ Search the profile path for profile definitions """ - profiles_path = Path(__file__).parents[2] / 'default_profiles' + profiles_path = Path(__file__).parents[2] / "default_profiles" profiles = [] - for file in profiles_path.glob('**/*.py'): + for file in profiles_path.glob("**/*.py"): # ignore the abstract default_profiles class - if 'profile.py' in file.name: + if "profile.py" in file.name: continue profiles += self._process_profile_file(file) diff --git a/archinstall/lib/storage.py b/archinstall/lib/storage.py index d8474fe1..cc12de08 100644 --- a/archinstall/lib/storage.py +++ b/archinstall/lib/storage.py @@ -9,6 +9,6 @@ from pathlib import Path from typing import Any storage: dict[str, Any] = { - 'LOG_PATH': Path('/var/log/archinstall'), - 'LOG_FILE': Path('install.log'), + "LOG_PATH": Path("/var/log/archinstall"), + "LOG_FILE": Path("install.log"), } diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 5b5efe8e..441b850f 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -23,7 +23,7 @@ class Language: @property def display_name(self) -> str: name = self.name_en - return f'{name} ({self.translation_percent}%)' + return f"{name} ({self.translation_percent}%)" def is_match(self, lang_or_translated_lang: str) -> bool: if self.name_en == lang_or_translated_lang: @@ -38,8 +38,8 @@ class Language: class TranslationHandler: def __init__(self) -> None: - self._base_pot = 'base.pot' - self._languages = 'languages.json' + self._base_pot = "base.pot" + self._languages = "languages.json" self._total_messages = self._get_total_active_messages() self._translated_languages = self._get_translations() @@ -58,17 +58,17 @@ class TranslationHandler: languages = [] for short_form in defined_languages: - mapping_entry: dict[str, str] = next(filter(lambda x: x['abbr'] == short_form, mappings)) - abbr = mapping_entry['abbr'] - lang = mapping_entry['lang'] - translated_lang = mapping_entry.get('translated_lang', None) + mapping_entry: dict[str, str] = next(filter(lambda x: x["abbr"] == short_form, mappings)) + abbr = mapping_entry["abbr"] + lang = mapping_entry["lang"] + translated_lang = mapping_entry.get("translated_lang", None) try: # get a translation for a specific language - translation = gettext.translation('base', localedir=self._get_locales_dir(), languages=(abbr, lang)) + translation = gettext.translation("base", localedir=self._get_locales_dir(), languages=(abbr, lang)) # calculate the percentage of total translated text to total number of messages - if abbr == 'en': + if abbr == "en": percent = 100 else: num_translations = self._get_catalog_size(translation) @@ -108,9 +108,9 @@ class TranslationHandler: Get total messages that could be translated """ locales = self._get_locales_dir() - with open(f'{locales}/{self._base_pot}') as fp: + with open(f"{locales}/{self._base_pot}") as fp: lines = fp.readlines() - msgid_lines = [line for line in lines if 'msgid' in line] + msgid_lines = [line for line in lines if "msgid" in line] return len(msgid_lines) - 1 # don't count the first line which contains the metadata @@ -121,7 +121,7 @@ class TranslationHandler: try: return next(filter(lambda x: x.name_en == name, self._translated_languages)) except Exception: - raise ValueError(f'No language with name found: {name}') + raise ValueError(f"No language with name found: {name}") def get_language_by_abbr(self, abbr: str) -> Language: """ @@ -143,7 +143,7 @@ class TranslationHandler: Get the locales directory path """ cur_path = Path(__file__).parent.parent - locales_dir = Path.joinpath(cur_path, 'locales') + locales_dir = Path.joinpath(cur_path, "locales") return locales_dir def _provided_translations(self) -> list[str]: @@ -155,7 +155,7 @@ class TranslationHandler: translation_files = [] for filename in filenames: - if len(filename) == 2 or filename in ['pt_BR', 'zh-CN', 'zh-TW']: + if len(filename) == 2 or filename in ["pt_BR", "zh-CN", "zh-TW"]: translation_files.append(filename) return translation_files diff --git a/archinstall/lib/utils/unicode.py b/archinstall/lib/utils/unicode.py index 402c14a5..db0fb667 100644 --- a/archinstall/lib/utils/unicode.py +++ b/archinstall/lib/utils/unicode.py @@ -4,7 +4,7 @@ from functools import lru_cache @lru_cache(maxsize=128) def _is_wide_character(char: str) -> bool: - return unicodedata.east_asian_width(char) in 'FW' + return unicodedata.east_asian_width(char) in "FW" def _count_wchars(string: str) -> int: @@ -12,7 +12,7 @@ def _count_wchars(string: str) -> int: return sum(_is_wide_character(c) for c in string) -def unicode_ljust(string: str, width: int, fillbyte: str = ' ') -> str: +def unicode_ljust(string: str, width: int, fillbyte: str = " ") -> str: """Return a left-justified unicode string of length width. >>> unicode_ljust('Hello', 15, '*') 'Hello**********' @@ -26,7 +26,7 @@ def unicode_ljust(string: str, width: int, fillbyte: str = ' ') -> str: return string.ljust(width - _count_wchars(string), fillbyte) -def unicode_rjust(string: str, width: int, fillbyte: str = ' ') -> str: +def unicode_rjust(string: str, width: int, fillbyte: str = " ") -> str: """Return a right-justified unicode string of length width. >>> unicode_rjust('Hello', 15, '*') '**********Hello' diff --git a/archinstall/lib/utils/util.py b/archinstall/lib/utils/util.py index 9a6f5a86..451bf0fe 100644 --- a/archinstall/lib/utils/util.py +++ b/archinstall/lib/utils/util.py @@ -20,14 +20,14 @@ def get_password( header: str | None = None, allow_skip: bool = False, preset: str | None = None, - skip_confirmation: bool = False + skip_confirmation: bool = False, ) -> Password | None: failure: str | None = None while True: user_hdr = None if failure is not None: - user_hdr = f'{header}\n{failure}\n' + user_hdr = f"{header}\n{failure}\n" elif header is not None: user_hdr = header @@ -37,7 +37,7 @@ def get_password( alignment=Alignment.CENTER, allow_skip=allow_skip, default_text=preset, - hide_input=True + hide_input=True, ).input() if allow_skip and not result.has_item(): @@ -49,22 +49,22 @@ def get_password( return password if header is not None: - confirmation_header = f'{header}{_("Password")}: {password.hidden()}\n' + confirmation_header = f"{header}{_('Password')}: {password.hidden()}\n" else: - confirmation_header = f'{_("Password")}: {password.hidden()}\n' + confirmation_header = f"{_('Password')}: {password.hidden()}\n" result = EditMenu( - str(_('Confirm password')), + str(_("Confirm password")), header=confirmation_header, alignment=Alignment.CENTER, allow_skip=False, - hide_input=True + hide_input=True, ).input() if password._plaintext == result.text(): return password - failure = str(_('The confirmation password did not match, please try again')) + failure = str(_("The confirmation password did not match, please try again")) def prompt_dir( @@ -72,7 +72,7 @@ def prompt_dir( header: str | None = None, validate: bool = True, allow_skip: bool = False, - preset: str | None = None + preset: str | None = None, ) -> Path | None: def validate_path(path: str) -> str | None: dest_path = Path(path) @@ -80,7 +80,7 @@ def prompt_dir( if dest_path.exists() and dest_path.is_dir(): return None - return str(_('Not a valid directory')) + return str(_("Not a valid directory")) if validate: validate_func = validate_path @@ -93,7 +93,7 @@ def prompt_dir( alignment=Alignment.CENTER, allow_skip=allow_skip, validator=validate_func, - default_text=preset + default_text=preset, ).input() if allow_skip and not result.has_item(): @@ -115,9 +115,9 @@ def is_subpath(first: Path, second: Path) -> bool: def format_cols(items: list[str], header: str | None = None) -> str: if header: - text = f'{header}:\n' + text = f"{header}:\n" else: - text = '' + text = "" nr_items = len(items) if nr_items <= 4: @@ -131,5 +131,5 @@ def format_cols(items: list[str], header: str | None = None) -> str: text += FormattedOutput.as_columns(items, col) # remove whitespaces on each row - text = '\n'.join([t.strip() for t in text.split('\n')]) + text = "\n".join([t.strip() for t in text.split("\n")]) return text diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 147ac2fe..9172df0d 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -31,7 +31,7 @@ def ask_user_questions() -> None: global_menu = GlobalMenu(arch_config_handler.config) if not arch_config_handler.args.advanced: - global_menu.set_enabled('parallel_downloads', False) + global_menu.set_enabled("parallel_downloads", False) global_menu.run() @@ -42,7 +42,7 @@ def perform_installation(mountpoint: Path) -> None: Only requirement is that the block devices are formatted and setup prior to entering this function. """ - info('Starting installation...') + info("Starting installation...") config = arch_config_handler.config @@ -60,7 +60,7 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Mount all the drives to the desired mountpoint if disk_config.config_type != DiskLayoutType.Pre_mount: @@ -80,14 +80,14 @@ def perform_installation(mountpoint: Path) -> None: optional_repositories=optional_repositories, mkinitcpio=run_mkinitcpio, hostname=arch_config_handler.config.hostname, - locale_config=locale_config + locale_config=locale_config, ) if mirror_config := config.mirror_config: installation.set_mirrors(mirror_config, on_target=True) if config.swap: - installation.setup_swap('zram') + installation.setup_swap("zram") if config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") @@ -101,7 +101,7 @@ def perform_installation(mountpoint: Path) -> None: if network_config: network_config.install_network_config( installation, - config.profile_config + config.profile_config, ) if users := config.users: @@ -113,7 +113,7 @@ def perform_installation(mountpoint: Path) -> None: else: info("No audio server will be installed") - if config.packages and config.packages[0] != '': + if config.packages and config.packages[0] != "": installation.add_additional_packages(config.packages) if profile_config := config.profile_config: @@ -129,7 +129,7 @@ def perform_installation(mountpoint: Path) -> None: installation.enable_espeakup() if root_pw := config.root_enc_password: - root_user = User('root', root_pw, False) + root_user = User("root", root_pw, False) installation.set_user_password(root_user) if (profile_config := config.profile_config) and profile_config.profile: @@ -156,7 +156,7 @@ def perform_installation(mountpoint: Path) -> None: case PostInstallationAction.EXIT: pass case PostInstallationAction.REBOOT: - os.system('reboot') + os.system("reboot") case PostInstallationAction.CHROOT: try: installation.drop_to_shell() @@ -178,13 +178,13 @@ def guided() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") guided() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/archinstall/scripts/list.py b/archinstall/scripts/list.py index 7d9408c3..8e29a982 100644 --- a/archinstall/scripts/list.py +++ b/archinstall/scripts/list.py @@ -4,7 +4,7 @@ from pathlib import Path print("The following are viable --script options:") for script in [Path(x) for x in glob.glob(f"{Path(__file__).parent}/*.py")]: - if script.stem in ['__init__', 'list']: + if script.stem in ["__init__", "list"]: continue print(f" {script.stem}") diff --git a/archinstall/scripts/minimal.py b/archinstall/scripts/minimal.py index 81537a28..d30296d9 100644 --- a/archinstall/scripts/minimal.py +++ b/archinstall/scripts/minimal.py @@ -29,12 +29,12 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Strap in the base system, add a boot loader and configure # some other minor details as specified by this profile and user. if installation.minimal_installation(): - installation.set_hostname('minimal-arch') + installation.set_hostname("minimal-arch") installation.add_bootloader(Bootloader.Systemd) network_config = config.network_config @@ -42,15 +42,15 @@ def perform_installation(mountpoint: Path) -> None: if network_config: network_config.install_network_config( installation, - config.profile_config + config.profile_config, ) - installation.add_additional_packages(['nano', 'wget', 'git']) + installation.add_additional_packages(["nano", "wget", "git"]) profile_config = ProfileConfiguration(MinimalProfile()) profile_handler.install_profile_config(installation, profile_config) - user = User('devel', Password(plaintext='devel'), False) + user = User("devel", Password(plaintext="devel"), False) installation.create_users(user) # Once this is done, we output some useful information to the user @@ -81,13 +81,13 @@ def _minimal() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") _minimal() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/archinstall/scripts/only_hd.py b/archinstall/scripts/only_hd.py index 05fa5b42..bcb66a2b 100644 --- a/archinstall/scripts/only_hd.py +++ b/archinstall/scripts/only_hd.py @@ -15,11 +15,11 @@ def ask_user_questions() -> None: global_menu = GlobalMenu(arch_config_handler.config) global_menu.disable_all() - global_menu.set_enabled('archinstall_language', True) - global_menu.set_enabled('disk_config', True) - global_menu.set_enabled('disk_encryption', True) - global_menu.set_enabled('swap', True) - global_menu.set_enabled('__config__', True) + global_menu.set_enabled("archinstall_language", True) + global_menu.set_enabled("disk_config", True) + global_menu.set_enabled("disk_encryption", True) + global_menu.set_enabled("swap", True) + global_menu.set_enabled("__config__", True) global_menu.run() @@ -43,7 +43,7 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Mount all the drives to the desired mountpoint # This *can* be done outside of the installation, but the installer can deal with it. @@ -73,13 +73,13 @@ def _only_hd() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") _only_hd() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/archinstall/scripts/unattended.py b/archinstall/scripts/unattended.py index b59fbb4c..9bf51a93 100644 --- a/archinstall/scripts/unattended.py +++ b/archinstall/scripts/unattended.py @@ -11,10 +11,10 @@ for p in profile_handler.get_mac_addr_profiles(): # that fits the requirements for this machine specifically). info(f'Found a tailored profile for this machine called: "{p.name}"') - print('Starting install in:') + print("Starting install in:") for i in range(10, 0, -1): - Tui.print(f'{i}...') + Tui.print(f"{i}...") time.sleep(1) - install_session = storage['installation_session'] + install_session = storage["installation_session"] p.install(install_session) diff --git a/archinstall/tui/__init__.py b/archinstall/tui/__init__.py index 8708c8c5..6df5dd97 100644 --- a/archinstall/tui/__init__.py +++ b/archinstall/tui/__init__.py @@ -4,17 +4,17 @@ from .result import Result, ResultType from .types import Alignment, Chars, FrameProperties, FrameStyle, Orientation, PreviewStyle __all__ = [ - 'Alignment', - 'Chars', - 'EditMenu', - 'FrameProperties', - 'FrameStyle', - 'MenuItem', - 'MenuItemGroup', - 'Orientation', - 'PreviewStyle', - 'Result', - 'ResultType', - 'SelectMenu', - 'Tui', + "Alignment", + "Chars", + "EditMenu", + "FrameProperties", + "FrameStyle", + "MenuItem", + "MenuItemGroup", + "Orientation", + "PreviewStyle", + "Result", + "ResultType", + "SelectMenu", + "Tui", ] diff --git a/archinstall/tui/curses_menu.py b/archinstall/tui/curses_menu.py index c11e753f..2ad43c6f 100644 --- a/archinstall/tui/curses_menu.py +++ b/archinstall/tui/curses_menu.py @@ -54,11 +54,11 @@ class AbstractCurses[ValueT](metaclass=ABCMeta): def clear_help_win(self) -> None: self._help_window.erase() - def _set_help_viewport(self) -> 'Viewport': + def _set_help_viewport(self) -> "Viewport": max_height, max_width = Tui.t().max_yx height = max_height - 10 - max_help_width = max([len(line) for line in Help.get_help_text().split('\n')]) + max_help_width = max([len(line) for line in Help.get_help_text().split("\n")]) x_start = int((max_width / 2) - (max_help_width / 2)) return Viewport( @@ -66,7 +66,7 @@ class AbstractCurses[ValueT](metaclass=ABCMeta): height, x_start, int((max_height / 2) - height / 2), - frame=FrameProperties.min(str(_('Archinstall help'))) + frame=FrameProperties.min(str(_("Archinstall help"))), ) def _confirm_interrupt(self, warning: str) -> bool: @@ -76,7 +76,7 @@ class AbstractCurses[ValueT](metaclass=ABCMeta): header=warning, alignment=Alignment.CENTER, columns=2, - orientation=Orientation.HORIZONTAL + orientation=Orientation.HORIZONTAL, ).run() match result.type_: @@ -87,23 +87,23 @@ class AbstractCurses[ValueT](metaclass=ABCMeta): return False def help_entry(self) -> ViewportEntry: - return ViewportEntry(str(_('Press Ctrl+h for help')), 0, 0, STYLE.NORMAL) + return ViewportEntry(str(_("Press Ctrl+h for help")), 0, 0, STYLE.NORMAL) def _show_help(self) -> None: if not self._help_window: - debug('no help window set') + debug("no help window set") return help_text = Help.get_help_text() - lines = help_text.split('\n') + lines = help_text.split("\n") - entries = [ViewportEntry('', 0, 0, STYLE.NORMAL)] - entries += [ViewportEntry(f' {e} ', idx + 1, 0, STYLE.NORMAL) for idx, e in enumerate(lines)] + entries = [ViewportEntry("", 0, 0, STYLE.NORMAL)] + entries += [ViewportEntry(f" {e} ", idx + 1, 0, STYLE.NORMAL) for idx, e in enumerate(lines)] self._help_window.update(entries, 0) def get_header_entries(self, header: str) -> list[ViewportEntry]: full_header = [] - rows = header.split('\n') + rows = header.split("\n") for cur_row, line in enumerate(rows): full_header += [ViewportEntry(line, cur_row, 0, STYLE.NORMAL)] @@ -128,7 +128,7 @@ class AbstractViewport: max_width: int, max_height: int, frame: FrameProperties, - scroll_pct: int | None = None + scroll_pct: int | None = None, ) -> list[ViewportEntry]: if not entries: return [] @@ -154,7 +154,7 @@ class AbstractViewport: top_ve, bottom_ve, *frame_border, - *entries + *entries, ] return framed_entries @@ -167,7 +167,7 @@ class AbstractViewport: def _get_right_frame( self, dim: _FrameDim, - scroll_percentage: int | None = None + scroll_percentage: int | None = None, ) -> list[ViewportEntry]: right_frame = {} scroll_height = int(dim.height * scroll_percentage // 100) if scroll_percentage else 0 @@ -192,9 +192,9 @@ class AbstractViewport: dim: _FrameDim, h_bar: str, frame: FrameProperties, - scroll_percentage: int | None = None + scroll_percentage: int | None = None, ) -> ViewportEntry: - top = self._replace_str(h_bar, 1, f' {frame.header} ') if frame.header else h_bar + top = self._replace_str(h_bar, 1, f" {frame.header} ") if frame.header else h_bar if scroll_percentage is None: top = Chars.Upper_left + top + Chars.Upper_right @@ -207,7 +207,7 @@ class AbstractViewport: self, dim: _FrameDim, h_bar: str, - scroll_pct: int | None = None + scroll_pct: int | None = None, ) -> ViewportEntry: if scroll_pct is None: bottom = Chars.Lower_left + h_bar + Chars.Lower_right @@ -221,9 +221,9 @@ class AbstractViewport: entries: list[ViewportEntry], max_width: int, max_height: int, - frame: FrameProperties + frame: FrameProperties, ) -> _FrameDim: - rows = self._assemble_entries(entries).split('\n') + rows = self._assemble_entries(entries).split("\n") header_len = len(frame.header) if frame.header else 0 header_len += 3 # for header padding @@ -250,7 +250,7 @@ class AbstractViewport: def _adjust_entries( self, entries: list[ViewportEntry], - frame_dim: _FrameDim + frame_dim: _FrameDim, ) -> list[ViewportEntry]: for entry in entries: # top row frame offset @@ -269,23 +269,23 @@ class AbstractViewport: return 0 return max(values) - def _replace_str(self, text: str, index: int = 0, replacement: str = '') -> str: + def _replace_str(self, text: str, index: int = 0, replacement: str = "") -> str: len_replace = len(replacement) - return f'{text[:index]}{replacement}{text[index + len_replace:]}' + return f"{text[:index]}{replacement}{text[index + len_replace :]}" def _assemble_entries(self, entries: list[ViewportEntry]) -> str: if not entries: - return '' + return "" max_col = self._max_col(entries) - view = [max_col * ' '] * self._num_unique_rows(entries) + view = [max_col * " "] * self._num_unique_rows(entries) for e in entries: view[e.row] = self._replace_str(view[e.row], e.col, e.text) view = [v.rstrip() for v in view] - return '\n'.join(view) + return "\n".join(view) class EditViewport(AbstractViewport): @@ -299,7 +299,7 @@ class EditViewport(AbstractViewport): process_key: Callable[[int], int | None], frame: FrameProperties, alignment: Alignment = Alignment.CENTER, - hide_input: bool = False + hide_input: bool = False, ) -> None: super().__init__() @@ -333,7 +333,7 @@ class EditViewport(AbstractViewport): 1, self._edit_width - 2, self.y_start + 1, - self.x_start + x_offset + 1 + self.x_start + x_offset + 1, ) def update(self) -> None: @@ -343,10 +343,10 @@ class EditViewport(AbstractViewport): self._main_win.erase() framed = self.add_frame( - [ViewportEntry('', 0, 0, STYLE.NORMAL)], + [ViewportEntry("", 0, 0, STYLE.NORMAL)], self._edit_width, 3, - frame=self._frame + frame=self._frame, ) x_offset = 0 @@ -359,7 +359,7 @@ class EditViewport(AbstractViewport): row.row, row.col + x_offset, row.text, - row.style + row.style, ) self._main_win.refresh() @@ -395,7 +395,7 @@ class Viewport(AbstractViewport): y_start: int, enable_scroll: bool = False, frame: FrameProperties | None = None, - alignment: Alignment = Alignment.LEFT + alignment: Alignment = Alignment.LEFT, ): super().__init__() @@ -422,7 +422,7 @@ class Viewport(AbstractViewport): self, entries: list[ViewportEntry], cur_pos: int = 0, - scroll_pos: int | None = None + scroll_pos: int | None = None, ) -> None: # self._state = self._get_viewport_state(lines, cur_pos, scroll_pos) # visible_entries = self._adjust_entries_row(self._state.displayed_entries) @@ -434,7 +434,7 @@ class Viewport(AbstractViewport): self.width, self.height, frame=self._frame, - scroll_pct=scroll_pos + scroll_pct=scroll_pos, ) x_offset = 0 @@ -449,7 +449,7 @@ class Viewport(AbstractViewport): entry.row, entry.col + x_offset, entry.text, - entry.style + entry.style, ) self._main_win.refresh() @@ -467,7 +467,7 @@ class EditMenu(AbstractCurses[str]): reset_warning_msg: str | None = None, alignment: Alignment = Alignment.CENTER, default_text: str | None = None, - hide_input: bool = False + hide_input: bool = False, ): super().__init__() @@ -489,9 +489,9 @@ class EditMenu(AbstractCurses[str]): self._hide_input = hide_input if self._interrupt_warning is None: - self._interrupt_warning = str(_('Are you sure you want to reset this setting?')) + '\n' + self._interrupt_warning = str(_("Are you sure you want to reset this setting?")) + "\n" - title = f'* {title}' if not self._allow_skip else title + title = f"* {title}" if not self._allow_skip else title self._frame = FrameProperties(title, FrameStyle.MAX) self._help_vp: Viewport | None = None @@ -500,7 +500,7 @@ class EditMenu(AbstractCurses[str]): self._info_vp: Viewport | None = None self._set_default_info = True - self._only_ascii_text = ViewportEntry(str(_('Only ASCII characters are supported')), 0, 0, STYLE.NORMAL) + self._only_ascii_text = ViewportEntry(str(_("Only ASCII characters are supported")), 0, 0, STYLE.NORMAL) self._init_viewports() @@ -528,7 +528,7 @@ class EditMenu(AbstractCurses[str]): self._process_edit_key, frame=self._frame, alignment=self._alignment, - hide_input=self._hide_input + hide_input=self._hide_input, ) y_offset += 3 @@ -570,7 +570,7 @@ class EditMenu(AbstractCurses[str]): entry = ViewportEntry(err, 0, 0, STYLE.ERROR) self._info_vp.update([entry], 0) self._set_default_info = False - self._real_input = '' + self._real_input = "" return None return text @@ -689,19 +689,19 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): column_spacing: int = 10, header: str | None = None, frame: FrameProperties | None = None, - cursor_char: str = '>', + cursor_char: str = ">", search_enabled: bool = True, allow_skip: bool = False, allow_reset: bool = False, reset_warning_msg: str | None = None, preview_style: PreviewStyle = PreviewStyle.NONE, - preview_size: float | Literal['auto'] = 0.2, + preview_size: float | Literal["auto"] = 0.2, preview_frame: FrameProperties | None = None, ): super().__init__() self._multi = multi - self._cursor_char = f'{cursor_char} ' + self._cursor_char = f"{cursor_char} " self._search_enabled = search_enabled self._allow_skip = allow_skip self._allow_reset = allow_reset @@ -726,7 +726,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): self._header_entries = self.get_header_entries(header) if self._interrupt_warning is None: - self._interrupt_warning = str(_('Are you sure you want to reset this setting?')) + '\n' + self._interrupt_warning = str(_("Are you sure you want to reset this setting?")) + "\n" if self._orientation == Orientation.HORIZONTAL: self._horizontal_cols = columns @@ -751,7 +751,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): self._item_group, total_cols=self._horizontal_cols, total_rows=self._menu_vp.height, - with_frame=self._frame is not None + with_frame=self._frame is not None, ) def run(self) -> Result[ValueT]: @@ -801,11 +801,11 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): def _footer_entries(self) -> list[ViewportEntry]: if self._active_search: filter_pattern = self._item_group.filter_pattern - return [ViewportEntry(f'/{filter_pattern}', 0, 0, STYLE.NORMAL)] + return [ViewportEntry(f"/{filter_pattern}", 0, 0, STYLE.NORMAL)] return [] - def _init_viewports(self, arg_prev_size: float | Literal['auto']) -> None: + def _init_viewports(self, arg_prev_size: float | Literal["auto"]) -> None: footer_height = 2 # possible filter at the bottom y_offset = 0 @@ -819,7 +819,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): header_height, 0, y_offset, - alignment=self._alignment + alignment=self._alignment, ) y_offset += header_height @@ -837,14 +837,14 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): 0, y_offset, frame=self._frame, - alignment=self._alignment + alignment=self._alignment, ) self._preview_vp = Viewport( self._max_width, prev_size, 0, menu_height + y_offset, - frame=self._preview_frame + frame=self._preview_frame, ) case PreviewStyle.RIGHT: menu_width = self._max_width - prev_size @@ -855,7 +855,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): 0, y_offset, frame=self._frame, - alignment=self._alignment + alignment=self._alignment, ) self._preview_vp = Viewport( prev_size, @@ -863,7 +863,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): menu_width, y_offset, frame=self._preview_frame, - alignment=self._alignment + alignment=self._alignment, ) case PreviewStyle.TOP: menu_height = available_height - prev_size @@ -874,7 +874,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): 0, prev_size + y_offset, frame=self._frame, - alignment=self._alignment + alignment=self._alignment, ) self._preview_vp = Viewport( self._max_width, @@ -882,7 +882,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): 0, y_offset, frame=self._preview_frame, - alignment=self._alignment + alignment=self._alignment, ) case PreviewStyle.NONE: self._menu_vp = Viewport( @@ -891,27 +891,27 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): 0, y_offset, frame=self._frame, - alignment=self._alignment + alignment=self._alignment, ) self._footer_vp = Viewport( self._max_width, footer_height, 0, - self._max_height - footer_height + self._max_height - footer_height, ) def _determine_prev_size( self, - preview_size: float | Literal['auto'], - offset: int = 0 + preview_size: float | Literal["auto"], + offset: int = 0, ) -> int: - if not isinstance(preview_size, float) and preview_size != 'auto': + if not isinstance(preview_size, float) and preview_size != "auto": raise ValueError('preview size must be a float or "auto"') prev_size: int = 0 - if preview_size == 'auto': + if preview_size == "auto": match self._preview_style: case PreviewStyle.RIGHT: menu_width = self._item_group.get_max_width() + 5 @@ -962,7 +962,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): self, viewport: Viewport, entries: list[ViewportEntry], - cur_pos: int = 0 + cur_pos: int = 0, ) -> None: if entries: viewport.update(entries, cur_pos=cur_pos) @@ -987,7 +987,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): cur_pos = len(self._cursor_char) for col_idx, cell in enumerate(row): - cur_text = '' + cur_text = "" style = STYLE.NORMAL if cell == self._item_group.focus_item: @@ -1002,7 +1002,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): if col_idx < len(row) - 1: spacer_len = cols_widths[col_idx] - len(menu_item_text) - entries += [ViewportEntry(' ' * spacer_len, row_idx, cur_pos, STYLE.NORMAL)] + entries += [ViewportEntry(" " * spacer_len, row_idx, cur_pos, STYLE.NORMAL)] cur_pos += spacer_len return entries @@ -1022,7 +1022,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): return col_widths def _menu_item_text(self, item: MenuItem) -> str: - item_text = '' + item_text = "" if self._multi and not item.is_empty(): item_text += self._multi_prefix(item) @@ -1046,7 +1046,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): self._preview_vp.update([]) return - preview_text = action_text.split('\n') + preview_text = action_text.split("\n") entries = [ViewportEntry(e, idx, 0, STYLE.NORMAL) for idx, e in enumerate(preview_text)] total_prev_rows = max([e.row for e in entries]) + 1 # rows start with 0 and we need the count @@ -1061,7 +1061,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): def _get_scroll_pct( self, total_prev_rows: int, - available_rows: int + available_rows: int, ) -> int | None: assert self._preview_vp is not None @@ -1082,7 +1082,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): self, entries: list[ViewportEntry], total_prev_rows: int, - available_rows: int + available_rows: int, ) -> list[ViewportEntry]: assert self._preview_vp is not None @@ -1103,7 +1103,7 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): def _calc_prev_scroll_pos( self, entries: list[ViewportEntry], - total_prev_rows: int + total_prev_rows: int, ) -> None: if self._prev_scroll_pos >= total_prev_rows: self._prev_scroll_pos = total_prev_rows - 2 @@ -1112,11 +1112,11 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): def _multi_prefix(self, item: MenuItem) -> str: if item.read_only: - return ' ' + return " " elif self._item_group.is_item_selected(item): - return '[x] ' + return "[x] " else: - return '[ ] ' + return "[ ] " def _handle_interrupt(self) -> bool: if self._allow_reset and self._interrupt_warning: @@ -1150,8 +1150,8 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): if len(key_handles) > 1: decoded = MenuKeys.decode(key) - handles = ', '.join([k.name for k in key_handles]) - raise ValueError(f'Multiple key matches for key {decoded}: {handles}') + handles = ", ".join([k.name for k in key_handles]) + raise ValueError(f"Multiple key matches for key {decoded}: {handles}") elif len(key_handles) == 0: return None @@ -1181,24 +1181,24 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): return None case MenuKeys.MENU_DOWN | MenuKeys.MENU_RIGHT: - self._focus_item('next') + self._focus_item("next") case MenuKeys.MENU_UP | MenuKeys.MENU_LEFT: - self._focus_item('prev') + self._focus_item("prev") case MenuKeys.MENU_START: - self._focus_item('first') + self._focus_item("first") case MenuKeys.MENU_END: - self._focus_item('last') + self._focus_item("last") case MenuKeys.MULTI_SELECT: if self._multi: self._item_group.select_current_item() case MenuKeys.ENABLE_SEARCH: if self._search_enabled and not self._active_search: self._active_search = True - self._item_group.set_filter_pattern('') + self._item_group.set_filter_pattern("") case MenuKeys.ESC: if self._active_search: self._active_search = False - self._item_group.set_filter_pattern('') + self._item_group.set_filter_pattern("") else: if self._allow_skip: return Result(ResultType.Skip, None) @@ -1213,19 +1213,19 @@ class SelectMenu[ValueT](AbstractCurses[ValueT]): return None - def _focus_item(self, direction: Literal['next', 'prev', 'first', 'last']) -> None: + def _focus_item(self, direction: Literal["next", "prev", "first", "last"]) -> None: # reset the preview scroll as the newly focused item # may have a different preview row count and it'll blow up self._prev_scroll_pos = 0 match direction: - case 'next': + case "next": self._item_group.focus_next() - case 'prev': + case "prev": self._item_group.focus_prev() - case 'first': + case "first": self._item_group.focus_first() - case 'last': + case "last": self._item_group.focus_last() @@ -1245,7 +1245,7 @@ class Tui: return self._screen @staticmethod - def t() -> 'Tui': + def t() -> "Tui": assert Tui._t is not None return Tui._t @@ -1256,7 +1256,7 @@ class Tui: Tui.t().stop() - def init(self) -> 'Tui': + def init(self) -> "Tui": self._screen = curses.initscr() curses.noecho() curses.cbreak() @@ -1298,11 +1298,11 @@ class Tui: text: str, row: int = 0, col: int = 0, - endl: str | None = '\n', - clear_screen: bool = False + endl: str | None = "\n", + clear_screen: bool = False, ) -> None: if clear_screen: - os.system('clear') + os.system("clear") if Tui._t is None: print(text, end=endl) @@ -1339,7 +1339,7 @@ class Tui: return Tui.t()._main_loop(component) def _sig_win_resize(self, signum: int, frame: FrameType | None) -> None: - if hasattr(self, '_component') and self._component is not None: # pylint: disable=no-member + if hasattr(self, "_component") and self._component is not None: # pylint: disable=no-member self._component.resize_win() # pylint: disable=no-member def _main_loop[ValueT](self, component: AbstractCurses[ValueT]) -> Result[ValueT]: diff --git a/archinstall/tui/help.py b/archinstall/tui/help.py index f0ebbb3d..955099df 100644 --- a/archinstall/tui/help.py +++ b/archinstall/tui/help.py @@ -10,10 +10,10 @@ if TYPE_CHECKING: class HelpTextGroupId(Enum): - GENERAL = 'General' - NAVIGATION = 'Navigation' - SELECTION = 'Selection' - SEARCH = 'Search' + GENERAL = "General" + NAVIGATION = "Navigation" + SELECTION = "Selection" + SEARCH = "Search" @dataclass @@ -31,7 +31,7 @@ class HelpGroup: return max([len(e.description) for e in self.group_entries]) def get_key_width(self) -> int: - return max([len(', '.join(e.keys)) for e in self.group_entries]) + return max([len(", ".join(e.keys)) for e in self.group_entries]) class Help: @@ -45,9 +45,9 @@ class Help: return HelpGroup( group_id=HelpTextGroupId.GENERAL, group_entries=[ - HelpText(str(_('Show help')), ['Ctrl+h']), - HelpText(str(_('Exit help')), ['Esc']), - ] + HelpText(str(_("Show help")), ["Ctrl+h"]), + HelpText(str(_("Exit help")), ["Esc"]), + ], ) @staticmethod @@ -55,14 +55,14 @@ class Help: return HelpGroup( group_id=HelpTextGroupId.NAVIGATION, group_entries=[ - HelpText(str(_('Preview scroll up')), ['PgUp']), - HelpText(str(_('Preview scroll down')), ['PgDown']), - HelpText(str(_('Move up')), ['k', '↑']), - HelpText(str(_('Move down')), ['j', '↓']), - HelpText(str(_('Move right')), ['l', '→']), - HelpText(str(_('Move left')), ['h', '←']), - HelpText(str(_('Jump to entry')), ['1..9']) - ] + HelpText(str(_("Preview scroll up")), ["PgUp"]), + HelpText(str(_("Preview scroll down")), ["PgDown"]), + HelpText(str(_("Move up")), ["k", "↑"]), + HelpText(str(_("Move down")), ["j", "↓"]), + HelpText(str(_("Move right")), ["l", "→"]), + HelpText(str(_("Move left")), ["h", "←"]), + HelpText(str(_("Jump to entry")), ["1..9"]), + ], ) @staticmethod @@ -70,13 +70,13 @@ class Help: return HelpGroup( group_id=HelpTextGroupId.SELECTION, group_entries=[ - HelpText(str(_('Skip selection (if available)')), ['Esc']), - HelpText(str(_('Reset selection (if available)')), ['Ctrl+c']), - HelpText(str(_('Select on single select')), ['Enter']), - HelpText(str(_('Select on multi select')), ['Space', 'Tab']), - HelpText(str(_('Reset')), ['Ctrl-C']), - HelpText(str(_('Skip selection menu')), ['Esc']), - ] + HelpText(str(_("Skip selection (if available)")), ["Esc"]), + HelpText(str(_("Reset selection (if available)")), ["Ctrl+c"]), + HelpText(str(_("Select on single select")), ["Enter"]), + HelpText(str(_("Select on multi select")), ["Space", "Tab"]), + HelpText(str(_("Reset")), ["Ctrl-C"]), + HelpText(str(_("Skip selection menu")), ["Esc"]), + ], ) @staticmethod @@ -84,14 +84,14 @@ class Help: return HelpGroup( group_id=HelpTextGroupId.SEARCH, group_entries=[ - HelpText(str(_('Start search mode')), ['/']), - HelpText(str(_('Exit search mode')), ['Esc']), - ] + HelpText(str(_("Start search mode")), ["/"]), + HelpText(str(_("Exit search mode")), ["Esc"]), + ], ) @staticmethod def get_help_text() -> str: - help_output = '' + help_output = "" help_texts = [ Help.general(), Help.navigation(), @@ -102,16 +102,13 @@ class Help: max_key_width = max([help.get_key_width() for help in help_texts]) for help_group in help_texts: - help_output += f'{help_group.group_id.value}\n' + help_output += f"{help_group.group_id.value}\n" divider_len = max_desc_width + max_key_width - help_output += '-' * divider_len + '\n' + help_output += "-" * divider_len + "\n" for entry in help_group.group_entries: - help_output += ( - entry.description.ljust(max_desc_width, ' ') - + ', '.join(entry.keys) + '\n' - ) + help_output += entry.description.ljust(max_desc_width, " ") + ", ".join(entry.keys) + "\n" - help_output += '\n' + help_output += "\n" return help_output diff --git a/archinstall/tui/menu_item.py b/archinstall/tui/menu_item.py index 46cf0570..59486176 100644 --- a/archinstall/tui/menu_item.py +++ b/archinstall/tui/menu_item.py @@ -35,21 +35,21 @@ class MenuItem: return self.value @classmethod - def yes(cls) -> 'MenuItem': + def yes(cls) -> "MenuItem": if cls._yes is None: - cls._yes = cls(str(_('Yes')), value=True) + cls._yes = cls(str(_("Yes")), value=True) return cls._yes @classmethod - def no(cls) -> 'MenuItem': + def no(cls) -> "MenuItem": if cls._no is None: - cls._no = cls(str(_('No')), value=True) + cls._no = cls(str(_("No")), value=True) return cls._no def is_empty(self) -> bool: - return self.text == '' or self.text is None + return self.text == "" or self.text is None def has_value(self) -> bool: if self.value is None: @@ -76,10 +76,10 @@ class MenuItemGroup: default_item: MenuItem | None = None, sort_items: bool = False, sort_case_sensitive: bool = True, - checkmarks: bool = False + checkmarks: bool = False, ) -> None: if len(menu_items) < 1: - raise ValueError('Menu must have at least one item') + raise ValueError("Menu must have at least one item") if sort_items: if sort_case_sensitive: @@ -87,7 +87,7 @@ class MenuItemGroup: else: menu_items = sorted(menu_items, key=lambda x: x.text.lower()) - self._filter_pattern: str = '' + self._filter_pattern: str = "" self._checkmarks: bool = checkmarks self._menu_items: list[MenuItem] = menu_items @@ -99,27 +99,27 @@ class MenuItemGroup: self.focus_first() if self.focus_item not in self.items: - raise ValueError(f'Selected item not in menu: {focus_item}') + raise ValueError(f"Selected item not in menu: {focus_item}") def add_item(self, item: MenuItem) -> None: self._menu_items.append(item) - delattr(self, 'items') # resetting the cache + delattr(self, "items") # resetting the cache def find_by_key(self, key: str) -> MenuItem: for item in self._menu_items: if item.key == key: return item - raise ValueError(f'No key found for: {key}') + raise ValueError(f"No key found for: {key}") def get_enabled_items(self) -> list[MenuItem]: return [it for it in self.items if self.is_enabled(it)] @staticmethod - def yes_no() -> 'MenuItemGroup': + def yes_no() -> "MenuItemGroup": return MenuItemGroup( [MenuItem.yes(), MenuItem.no()], - sort_items=True + sort_items=True, ) def set_preview_for_all(self, action: Callable[[Any], str | None]) -> None: @@ -180,35 +180,35 @@ class MenuItemGroup: def get_item_text(self, item: MenuItem) -> str: if item.is_empty(): - return '' + return "" max_width = self._max_items_text_width display_text = item.get_display_value() default_text = self._default_suffix(item) - text = unicode_ljust(str(item.text), max_width, ' ') - spacing = ' ' * 4 + text = unicode_ljust(str(item.text), max_width, " ") + spacing = " " * 4 if display_text: - text = f'{text}{spacing}{display_text}' + text = f"{text}{spacing}{display_text}" elif self._checkmarks: from .types import Chars if item.has_value(): if item.get_value() is not False: - text = f'{text}{spacing}{Chars.Check}' + text = f"{text}{spacing}{Chars.Check}" else: text = item.text if default_text: - text = f'{text} {default_text}' + text = f"{text} {default_text}" - return text.rstrip(' ') + return text.rstrip(" ") def _default_suffix(self, item: MenuItem) -> str: if self.default_item == item: - return str(_(' (default)')) - return '' + return str(_(" (default)")) + return "" @cached_property def items(self) -> list[MenuItem]: @@ -222,21 +222,21 @@ class MenuItemGroup: return self._filter_pattern def has_filter(self) -> bool: - return self._filter_pattern != '' + return self._filter_pattern != "" def set_filter_pattern(self, pattern: str) -> None: self._filter_pattern = pattern - delattr(self, 'items') # resetting the cache + delattr(self, "items") # resetting the cache self._reload_focus_item() def append_filter(self, pattern: str) -> None: self._filter_pattern += pattern - delattr(self, 'items') # resetting the cache + delattr(self, "items") # resetting the cache self._reload_focus_item() def reduce_filter(self) -> None: self._filter_pattern = self._filter_pattern[:-1] - delattr(self, 'items') # resetting the cache + delattr(self, "items") # resetting the cache self._reload_focus_item() def _reload_focus_item(self) -> None: @@ -302,7 +302,7 @@ class MenuItemGroup: self, items: list[MenuItem], start_item: MenuItem, - direction: int + direction: int, ) -> MenuItem | None: start_index = self.items.index(start_item) n = len(items) @@ -362,7 +362,7 @@ class MenuItemsState: item_group: MenuItemGroup, total_cols: int, total_rows: int, - with_frame: bool + with_frame: bool, ) -> None: self._item_group = item_group self._total_cols = total_cols @@ -390,11 +390,7 @@ class MenuItemsState: start, end = 0, 0 - if ( - len(self._view_items) == 0 - or self._prev_row_idx == -1 - or self._item_group.has_filter() - ): # initial setup or filter + if len(self._view_items) == 0 or self._prev_row_idx == -1 or self._item_group.has_filter(): # initial setup or filter if focus_row_idx < self._total_rows: start = 0 end = self._total_rows @@ -434,14 +430,14 @@ class MenuItemsState: self, items: list[MenuItem], start_row: int, - total_rows: int + total_rows: int, ) -> list[list[MenuItem]]: groups: list[list[MenuItem]] = [] nr_items = self._total_cols * min(total_rows, len(items)) for x in range(start_row, nr_items, self._total_cols): groups.append( - items[x:x + self._total_cols] + items[x : x + self._total_cols], ) return groups diff --git a/archinstall/tui/types.py b/archinstall/tui/types.py index de64dc95..56ab5a29 100644 --- a/archinstall/tui/types.py +++ b/archinstall/tui/types.py @@ -48,7 +48,7 @@ class MenuKeys(Enum): SCROLL_DOWN = frozenset({338}) @classmethod - def from_ord(cls, key: int) -> list['MenuKeys']: + def from_ord(cls, key: int) -> list["MenuKeys"]: matches = [] for group in MenuKeys: @@ -60,7 +60,7 @@ class MenuKeys(Enum): @classmethod def decode(cls, key: int) -> str: byte_str = curses.keyname(key) - return byte_str.decode('utf-8') + return byte_str.decode("utf-8") class FrameStyle(Enum): @@ -75,7 +75,7 @@ class FrameProperties: h_frame_style: FrameStyle = FrameStyle.MAX @classmethod - def max(cls, header: str) -> 'FrameProperties': + def max(cls, header: str) -> "FrameProperties": return FrameProperties( header, FrameStyle.MAX, @@ -83,7 +83,7 @@ class FrameProperties: ) @classmethod - def min(cls, header: str) -> 'FrameProperties': + def min(cls, header: str) -> "FrameProperties": return FrameProperties( header, FrameStyle.MIN, diff --git a/docs/conf.py b/docs/conf.py index 8ad9caf9..c7b6835b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,7 +2,7 @@ import os import re import sys -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath("..")) def process_docstring(app, what, name, obj, options, lines) -> None: # type: ignore[no-untyped-def] @@ -12,7 +12,7 @@ def process_docstring(app, what, name, obj, options, lines) -> None: # type: ig def setup(app) -> None: # type: ignore[no-untyped-def] - app.connect('autodoc-process-docstring', process_docstring) + app.connect("autodoc-process-docstring", process_docstring) # Configuration file for the Sphinx documentation builder. @@ -34,33 +34,33 @@ def setup(app) -> None: # type: ignore[no-untyped-def] # -- Project information ----------------------------------------------------- -project = 'python-archinstall' -copyright = '2022, Anton Hvornum' -author = 'Anton Hvornum' +project = "python-archinstall" +copyright = "2022, Anton Hvornum" +author = "Anton Hvornum" # The full version, including alpha/beta/rc tags -release = 'v2.3.0' +release = "v2.3.0" # -- General configuration --------------------------------------------------- -master_doc = 'index' +master_doc = "index" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.todo', - 'sphinx_rtd_theme' + "sphinx.ext.autodoc", + "sphinx.ext.inheritance_diagram", + "sphinx.ext.todo", + "sphinx_rtd_theme", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- @@ -68,14 +68,14 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # a list of builtin themes. # # html_theme = 'alabaster' -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" html_logo = "_static/logo.png" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # If false, no module index is generated. html_domain_indices = True @@ -104,7 +104,7 @@ html_show_sourcelink = False # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'archinstalldoc' +htmlhelp_basename = "archinstalldoc" # -- Options for manual page output -------------------------------------------- diff --git a/examples/auto_discovery_mounted.py b/examples/auto_discovery_mounted.py index df792bb2..43345e99 100644 --- a/examples/auto_discovery_mounted.py +++ b/examples/auto_discovery_mounted.py @@ -3,7 +3,7 @@ from pathlib import Path from archinstall.lib.disk.device_handler import device_handler from archinstall.lib.models.device_model import DiskLayoutConfiguration, DiskLayoutType -root_mount_dir = Path('/mnt/archinstall') +root_mount_dir = Path("/mnt/archinstall") mods = device_handler.detect_pre_mounted_mods(root_mount_dir) diff --git a/examples/full_automated_installation.py b/examples/full_automated_installation.py index 2cc09755..1dc851d3 100644 --- a/examples/full_automated_installation.py +++ b/examples/full_automated_installation.py @@ -23,14 +23,14 @@ from archinstall.lib.models.users import Password, User from archinstall.lib.profile.profiles_handler import profile_handler # we're creating a new ext4 filesystem installation -fs_type = FilesystemType('ext4') -device_path = Path('/dev/sda') +fs_type = FilesystemType("ext4") +device_path = Path("/dev/sda") # get the physical disk device device = device_handler.get_device(device_path) if not device: - raise ValueError('No device found for given path') + raise ValueError("No device found for given path") # create a new modification for the specific device device_modification = DeviceModification(device, wipe=True) @@ -41,9 +41,9 @@ boot_partition = PartitionModification( type=PartitionType.Primary, start=Size(1, Unit.MiB, device.device_info.sector_size), length=Size(512, Unit.MiB, device.device_info.sector_size), - mountpoint=Path('/boot'), + mountpoint=Path("/boot"), fs_type=FilesystemType.Fat32, - flags=[PartitionFlag.BOOT] + flags=[PartitionFlag.BOOT], ) device_modification.add_partition(boot_partition) @@ -68,15 +68,15 @@ home_partition = PartitionModification( type=PartitionType.Primary, start=start_home, length=length_home, - mountpoint=Path('/home'), + mountpoint=Path("/home"), fs_type=fs_type, - mount_options=[] + mount_options=[], ) device_modification.add_partition(home_partition) disk_config = DiskLayoutConfiguration( config_type=DiskLayoutType.Default, - device_modifications=[device_modification] + device_modifications=[device_modification], ) # disk encryption configuration (Optional) @@ -84,7 +84,7 @@ disk_encryption = DiskEncryption( encryption_password=Password(plaintext="enc_password"), encryption_type=EncryptionType.Luks, partitions=[home_partition], - hsm_device=None + hsm_device=None, ) # initiate file handler with the disk config and the optional disk encryption config @@ -94,22 +94,22 @@ fs_handler = FilesystemHandler(disk_config, disk_encryption) # WARNING: this will potentially format the filesystem and delete all data fs_handler.perform_filesystem_operations(show_countdown=False) -mountpoint = Path('/tmp') +mountpoint = Path("/tmp") with Installer( mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=['linux'] + kernels=["linux"], ) as installation: installation.mount_ordered_layout() - installation.minimal_installation(hostname='minimal-arch') - installation.add_additional_packages(['nano', 'wget', 'git']) + installation.minimal_installation(hostname="minimal-arch") + installation.add_additional_packages(["nano", "wget", "git"]) # Optionally, install a profile of choice. # In this case, we install a minimal profile that is empty profile_config = ProfileConfiguration(MinimalProfile()) profile_handler.install_profile_config(installation, profile_config) -user = User('archinstall', Password(plaintext='password'), True) +user = User("archinstall", Password(plaintext="password"), True) installation.create_users(user) diff --git a/examples/interactive_installation.py b/examples/interactive_installation.py index 6bb42220..08d66bd4 100644 --- a/examples/interactive_installation.py +++ b/examples/interactive_installation.py @@ -30,7 +30,7 @@ def ask_user_questions() -> None: global_menu = GlobalMenu(arch_config_handler.config) if not arch_config_handler.args.advanced: - global_menu.set_enabled('parallel_downloads', False) + global_menu.set_enabled("parallel_downloads", False) global_menu.run() @@ -41,7 +41,7 @@ def perform_installation(mountpoint: Path) -> None: Only requirement is that the block devices are formatted and setup prior to entering this function. """ - info('Starting installation...') + info("Starting installation...") config = arch_config_handler.config @@ -59,7 +59,7 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Mount all the drives to the desired mountpoint if disk_config.config_type != DiskLayoutType.Pre_mount: @@ -79,14 +79,14 @@ def perform_installation(mountpoint: Path) -> None: optional_repositories=optional_repositories, mkinitcpio=run_mkinitcpio, hostname=arch_config_handler.config.hostname, - locale_config=locale_config + locale_config=locale_config, ) if mirror_config := config.mirror_config: installation.set_mirrors(mirror_config, on_target=True) if config.swap: - installation.setup_swap('zram') + installation.setup_swap("zram") if config.bootloader == Bootloader.Grub and SysInfo.has_uefi(): installation.add_additional_packages("grub") @@ -100,7 +100,7 @@ def perform_installation(mountpoint: Path) -> None: if network_config: network_config.install_network_config( installation, - config.profile_config + config.profile_config, ) if users := config.users: @@ -112,7 +112,7 @@ def perform_installation(mountpoint: Path) -> None: else: info("No audio server will be installed") - if config.packages and config.packages[0] != '': + if config.packages and config.packages[0] != "": installation.add_additional_packages(config.packages) if profile_config := config.profile_config: @@ -128,7 +128,7 @@ def perform_installation(mountpoint: Path) -> None: installation.enable_espeakup() if root_pw := config.root_enc_password: - root_user = User('root', root_pw, False) + root_user = User("root", root_pw, False) installation.set_user_password(root_user) if (profile_config := config.profile_config) and profile_config.profile: @@ -155,7 +155,7 @@ def perform_installation(mountpoint: Path) -> None: case PostInstallationAction.EXIT: pass case PostInstallationAction.REBOOT: - os.system('reboot') + os.system("reboot") case PostInstallationAction.CHROOT: try: installation.drop_to_shell() @@ -177,13 +177,13 @@ def guided() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") guided() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/examples/mac_address_installation.py b/examples/mac_address_installation.py index 45823535..325b286c 100644 --- a/examples/mac_address_installation.py +++ b/examples/mac_address_installation.py @@ -11,10 +11,10 @@ for _profile in profile_handler.get_mac_addr_profiles(): # that fits the requirements for this machine specifically). info(f'Found a tailored profile for this machine called: "{_profile.name}"') - print('Starting install in:') + print("Starting install in:") for i in range(10, 0, -1): - Tui.print(f'{i}...') + Tui.print(f"{i}...") time.sleep(1) - install_session = storage['installation_session'] + install_session = storage["installation_session"] _profile.install(install_session) diff --git a/examples/minimal_installation.py b/examples/minimal_installation.py index ce115f04..bff70311 100644 --- a/examples/minimal_installation.py +++ b/examples/minimal_installation.py @@ -30,12 +30,12 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Strap in the base system, add a boot loader and configure # some other minor details as specified by this profile and user. if installation.minimal_installation(): - installation.set_hostname('minimal-arch') + installation.set_hostname("minimal-arch") installation.add_bootloader(Bootloader.Systemd) network_config = config.network_config @@ -43,15 +43,15 @@ def perform_installation(mountpoint: Path) -> None: if network_config: network_config.install_network_config( installation, - config.profile_config + config.profile_config, ) - installation.add_additional_packages(['nano', 'wget', 'git']) + installation.add_additional_packages(["nano", "wget", "git"]) profile_config = ProfileConfiguration(MinimalProfile()) profile_handler.install_profile_config(installation, profile_config) - user = User('devel', Password(plaintext='devel'), False) + user = User("devel", Password(plaintext="devel"), False) installation.create_users(user) # Once this is done, we output some useful information to the user @@ -82,13 +82,13 @@ def _minimal() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") _minimal() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/examples/only_hd_installation.py b/examples/only_hd_installation.py index 0556b1bb..624da7b3 100644 --- a/examples/only_hd_installation.py +++ b/examples/only_hd_installation.py @@ -15,11 +15,11 @@ def ask_user_questions() -> None: global_menu = GlobalMenu(arch_config_handler.config) global_menu.disable_all() - global_menu.set_enabled('archinstall_language', True) - global_menu.set_enabled('disk_config', True) - global_menu.set_enabled('disk_encryption', True) - global_menu.set_enabled('swap', True) - global_menu.set_enabled('__config__', True) + global_menu.set_enabled("archinstall_language", True) + global_menu.set_enabled("disk_config", True) + global_menu.set_enabled("disk_encryption", True) + global_menu.set_enabled("swap", True) + global_menu.set_enabled("__config__", True) global_menu.run() @@ -43,7 +43,7 @@ def perform_installation(mountpoint: Path) -> None: mountpoint, disk_config, disk_encryption=disk_encryption, - kernels=config.kernels + kernels=config.kernels, ) as installation: # Mount all the drives to the desired mountpoint # This *can* be done outside of the installation, but the installer can deal with it. @@ -73,13 +73,13 @@ def _only_hd() -> None: if not arch_config_handler.args.silent: with Tui(): if not config.confirm_config(): - debug('Installation aborted') + debug("Installation aborted") _only_hd() if arch_config_handler.config.disk_config: fs_handler = FilesystemHandler( arch_config_handler.config.disk_config, - arch_config_handler.config.disk_encryption + arch_config_handler.config.disk_encryption, ) fs_handler.perform_filesystem_operations() diff --git a/tests/conftest.py b/tests/conftest.py index 2f5a18e0..97108bee 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,41 +3,41 @@ from pathlib import Path import pytest -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def config_fixture() -> Path: - return Path(__file__).parent / 'data' / 'test_config.json' + return Path(__file__).parent / "data" / "test_config.json" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def creds_fixture() -> Path: - return Path(__file__).parent / 'data' / 'test_creds.json' + return Path(__file__).parent / "data" / "test_creds.json" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def encrypted_creds_fixture() -> Path: - return Path(__file__).parent / 'data' / 'test_encrypted_creds.json' + return Path(__file__).parent / "data" / "test_encrypted_creds.json" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def deprecated_creds_config() -> Path: - return Path(__file__).parent / 'data' / 'test_deprecated_creds_config.json' + return Path(__file__).parent / "data" / "test_deprecated_creds_config.json" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def deprecated_mirror_config() -> Path: - return Path(__file__).parent / 'data' / 'test_deprecated_mirror_config.json' + return Path(__file__).parent / "data" / "test_deprecated_mirror_config.json" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def mirrorlist_no_country_fixture() -> Path: - return Path(__file__).parent / 'data' / 'mirrorlists' / 'test_no_country' + return Path(__file__).parent / "data" / "mirrorlists" / "test_no_country" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def mirrorlist_with_country_fixture() -> Path: - return Path(__file__).parent / 'data' / 'mirrorlists' / 'test_with_country' + return Path(__file__).parent / "data" / "mirrorlists" / "test_with_country" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def mirrorlist_multiple_countries_fixture() -> Path: - return Path(__file__).parent / 'data' / 'mirrorlists' / 'test_multiple_countries' + return Path(__file__).parent / "data" / "mirrorlists" / "test_multiple_countries" diff --git a/tests/test_args.py b/tests/test_args.py index dc5c783e..35f2b0ea 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -20,7 +20,7 @@ from archinstall.lib.translationhandler import translation_handler def test_default_args(monkeypatch: MonkeyPatch) -> None: - monkeypatch.setattr('sys.argv', ['archinstall']) + monkeypatch.setattr("sys.argv", ["archinstall"]) handler = ArchConfigHandler() args = handler.args assert args == Arguments( @@ -31,179 +31,187 @@ def test_default_args(monkeypatch: MonkeyPatch) -> None: creds_decryption_key=None, silent=False, dry_run=False, - script='guided', - mountpoint=Path('/mnt'), + script="guided", + mountpoint=Path("/mnt"), skip_ntp=False, debug=False, offline=False, no_pkg_lookups=False, plugin=None, skip_version_check=False, - advanced=False + advanced=False, ) def test_correct_parsing_args( monkeypatch: MonkeyPatch, config_fixture: Path, - creds_fixture: Path + creds_fixture: Path, ) -> None: - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--config', - str(config_fixture), - '--config-url', - 'https://example.com', - '--creds', - str(creds_fixture), - '--script', - 'execution_script', - '--mount-point', - '/tmp', - '--skip-ntp', - '--debug', - '--offline', - '--no-pkg-lookups', - '--plugin', - 'pytest_plugin.py', - '--skip-version-check', - '--advanced', - '--dry-run', - '--silent' - ]) + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--config", + str(config_fixture), + "--config-url", + "https://example.com", + "--creds", + str(creds_fixture), + "--script", + "execution_script", + "--mount-point", + "/tmp", + "--skip-ntp", + "--debug", + "--offline", + "--no-pkg-lookups", + "--plugin", + "pytest_plugin.py", + "--skip-version-check", + "--advanced", + "--dry-run", + "--silent", + ], + ) handler = ArchConfigHandler() args = handler.args assert args == Arguments( config=config_fixture, - config_url='https://example.com', + config_url="https://example.com", creds=creds_fixture, silent=True, dry_run=True, - script='execution_script', - mountpoint=Path('/mnt'), + script="execution_script", + mountpoint=Path("/mnt"), skip_ntp=True, debug=True, offline=True, no_pkg_lookups=True, - plugin='pytest_plugin.py', + plugin="pytest_plugin.py", skip_version_check=True, - advanced=True + advanced=True, ) def test_config_file_parsing( monkeypatch: MonkeyPatch, config_fixture: Path, - creds_fixture: Path + creds_fixture: Path, ) -> None: - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--config', - str(config_fixture), - '--creds', - str(creds_fixture), - ]) + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--config", + str(config_fixture), + "--creds", + str(creds_fixture), + ], + ) handler = ArchConfigHandler() arch_config = handler.config # the version is retrieved dynamically from an installed archinstall package # as there is no version present in the test environment we'll set it manually - arch_config.version = '3.0.2' + arch_config.version = "3.0.2" # TODO: Use the real values from the test fixture instead of clearing out the entries arch_config.disk_config.device_modifications = [] # type: ignore[union-attr] assert arch_config == ArchConfig( - version='3.0.2', + version="3.0.2", locale_config=LocaleConfiguration( - kb_layout='us', - sys_lang='en_US', - sys_enc='UTF-8' + kb_layout="us", + sys_lang="en_US", + sys_enc="UTF-8", ), - archinstall_language=translation_handler.get_language_by_abbr('en'), + archinstall_language=translation_handler.get_language_by_abbr("en"), disk_config=DiskLayoutConfiguration( config_type=DiskLayoutType.Default, device_modifications=[], lvm_config=None, - mountpoint=None + mountpoint=None, ), profile_config=ProfileConfiguration( - profile=profile_handler.parse_profile_config({ - "custom_settings": { - "Hyprland": { - "seat_access": "polkit" + profile=profile_handler.parse_profile_config( + { + "custom_settings": { + "Hyprland": { + "seat_access": "polkit", + }, + "Sway": { + "seat_access": "seatd", + }, }, - "Sway": { - "seat_access": "seatd" - } - }, - "details": [ - "Sway", - "Hyprland" - ], - "main": "Desktop" - }), + "details": [ + "Sway", + "Hyprland", + ], + "main": "Desktop", + } + ), gfx_driver=GfxDriver.AllOpenSource, - greeter=GreeterType.Lightdm + greeter=GreeterType.Lightdm, ), mirror_config=MirrorConfiguration( mirror_regions=[ MirrorRegion( - name='Australia', - urls=['http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch'] - ) + name="Australia", + urls=["http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch"], + ), ], - custom_servers=[CustomServer('https://mymirror.com/$repo/os/$arch')], + custom_servers=[CustomServer("https://mymirror.com/$repo/os/$arch")], optional_repositories=[Repository.Testing], custom_repositories=[ CustomRepository( - name='myrepo', - url='https://myrepo.com/$repo/os/$arch', + name="myrepo", + url="https://myrepo.com/$repo/os/$arch", sign_check=SignCheck.Required, - sign_option=SignOption.TrustAll - ) - ] + sign_option=SignOption.TrustAll, + ), + ], ), network_config=NetworkConfiguration( type=NicType.MANUAL, nics=[ Nic( - iface='eno1', - ip='192.168.1.15/24', + iface="eno1", + ip="192.168.1.15/24", dhcp=True, - gateway='192.168.1.1', + gateway="192.168.1.1", dns=[ - '192.168.1.1', - '9.9.9.9' - ] - ) - ] + "192.168.1.1", + "9.9.9.9", + ], + ), + ], ), bootloader=Bootloader.Systemd, uki=False, audio_config=AudioConfiguration(Audio.PIPEWIRE), - hostname='archy', - kernels=['linux-zen'], + hostname="archy", + kernels=["linux-zen"], ntp=True, packages=["firefox"], parallel_downloads=66, swap=False, - timezone='UTC', + timezone="UTC", users=[ User( - username='user_name', - password=Password(enc_password='password_hash'), + username="user_name", + password=Password(enc_password="password_hash"), sudo=True, - groups=['wheel'] - ) + groups=["wheel"], + ), ], disk_encryption=None, - services=['service_1', 'service_2'], - root_enc_password=Password(enc_password='password_hash'), - custom_commands=["echo 'Hello, World!'"] + services=["service_1", "service_2"], + root_enc_password=Password(enc_password="password_hash"), + custom_commands=["echo 'Hello, World!'"], ) @@ -211,11 +219,14 @@ def test_deprecated_mirror_config_parsing( monkeypatch: MonkeyPatch, deprecated_mirror_config: Path, ) -> None: - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--config', - str(deprecated_mirror_config), - ]) + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--config", + str(deprecated_mirror_config), + ], + ) handler = ArchConfigHandler() arch_config = handler.config @@ -223,20 +234,20 @@ def test_deprecated_mirror_config_parsing( assert arch_config.mirror_config == MirrorConfiguration( mirror_regions=[ MirrorRegion( - name='Australia', - urls=['http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch'] - ) + name="Australia", + urls=["http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch"], + ), ], custom_servers=[], optional_repositories=[Repository.Testing], custom_repositories=[ CustomRepository( - name='my_mirror', - url='example.com', + name="my_mirror", + url="example.com", sign_check=SignCheck.Optional, - sign_option=SignOption.TrustedOnly - ) - ] + sign_option=SignOption.TrustedOnly, + ), + ], ) @@ -244,24 +255,27 @@ def test_deprecated_creds_config_parsing( monkeypatch: MonkeyPatch, deprecated_creds_config: Path, ) -> None: - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--creds', - str(deprecated_creds_config), - ]) + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--creds", + str(deprecated_creds_config), + ], + ) handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.root_enc_password == Password(plaintext='rootPwd') + assert arch_config.root_enc_password == Password(plaintext="rootPwd") assert arch_config.users == [ User( - username='user_name', - password=Password(plaintext='userPwd'), + username="user_name", + password=Password(plaintext="userPwd"), sudo=True, - groups=['wheel'] - ) + groups=["wheel"], + ), ] @@ -269,23 +283,28 @@ def test_encrypted_creds_with_arg( monkeypatch: MonkeyPatch, encrypted_creds_fixture: Path, ) -> None: - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--creds', str(encrypted_creds_fixture), - '--creds-decryption-key', 'master' - ]) + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--creds", + str(encrypted_creds_fixture), + "--creds-decryption-key", + "master", + ], + ) handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') + assert arch_config.root_enc_password == Password(enc_password="$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7") assert arch_config.users == [ User( - username='t', - password=Password(enc_password='$y$j9T$3KxMigAEnjtzbjalhLewE.$gmuoQtc9RNY/PmO/GxHHYvkZNO86Eeftg1Oc7L.QSO/'), + username="t", + password=Password(enc_password="$y$j9T$3KxMigAEnjtzbjalhLewE.$gmuoQtc9RNY/PmO/GxHHYvkZNO86Eeftg1Oc7L.QSO/"), sudo=True, - groups=[] - ) + groups=[], + ), ] @@ -293,21 +312,25 @@ def test_encrypted_creds_with_env_var( monkeypatch: MonkeyPatch, encrypted_creds_fixture: Path, ) -> None: - os.environ['ARCHINSTALL_CREDS_DECRYPTION_KEY'] = 'master' - monkeypatch.setattr('sys.argv', [ - 'archinstall', - '--creds', str(encrypted_creds_fixture), - ]) + os.environ["ARCHINSTALL_CREDS_DECRYPTION_KEY"] = "master" + monkeypatch.setattr( + "sys.argv", + [ + "archinstall", + "--creds", + str(encrypted_creds_fixture), + ], + ) handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') + assert arch_config.root_enc_password == Password(enc_password="$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7") assert arch_config.users == [ User( - username='t', - password=Password(enc_password='$y$j9T$3KxMigAEnjtzbjalhLewE.$gmuoQtc9RNY/PmO/GxHHYvkZNO86Eeftg1Oc7L.QSO/'), + username="t", + password=Password(enc_password="$y$j9T$3KxMigAEnjtzbjalhLewE.$gmuoQtc9RNY/PmO/GxHHYvkZNO86Eeftg1Oc7L.QSO/"), sudo=True, - groups=[] - ) + groups=[], + ), ] diff --git a/tests/test_configuration_output.py b/tests/test_configuration_output.py index fa5757c0..d9e282a2 100644 --- a/tests/test_configuration_output.py +++ b/tests/test_configuration_output.py @@ -11,18 +11,18 @@ def test_user_config_roundtrip( monkeypatch: MonkeyPatch, config_fixture: Path, ) -> None: - monkeypatch.setattr('sys.argv', ['archinstall', '--config', str(config_fixture)]) + monkeypatch.setattr("sys.argv", ["archinstall", "--config", str(config_fixture)]) handler = ArchConfigHandler() arch_config = handler.config # the version is retrieved dynamically from an installed archinstall package # as there is no version present in the test environment we'll set it manually - arch_config.version = '3.0.2' + arch_config.version = "3.0.2" config_output = ConfigurationOutput(arch_config) - test_out_dir = Path('/tmp/') + test_out_dir = Path("/tmp/") test_out_file = test_out_dir / config_output.user_configuration_file config_output.save(test_out_dir) @@ -34,15 +34,15 @@ def test_user_config_roundtrip( # it will ignore the modification; as this test will run on various local systems # and the CI pipeline there's no good way specify a real device so we'll simply # copy the expected result to the actual result - result['disk_config']['config_type'] = expected['disk_config']['config_type'] - result['disk_config']['device_modifications'] = expected['disk_config']['device_modifications'] + result["disk_config"]["config_type"] = expected["disk_config"]["config_type"] + result["disk_config"]["device_modifications"] = expected["disk_config"]["device_modifications"] assert json.dumps( - result['mirror_config'], - sort_keys=True + result["mirror_config"], + sort_keys=True, ) == json.dumps( - expected['mirror_config'], - sort_keys=True + expected["mirror_config"], + sort_keys=True, ) @@ -50,14 +50,14 @@ def test_creds_roundtrip( monkeypatch: MonkeyPatch, creds_fixture: Path, ) -> None: - monkeypatch.setattr('sys.argv', ['archinstall', '--creds', str(creds_fixture)]) + monkeypatch.setattr("sys.argv", ["archinstall", "--creds", str(creds_fixture)]) handler = ArchConfigHandler() arch_config = handler.config config_output = ConfigurationOutput(arch_config) - test_out_dir = Path('/tmp/') + test_out_dir = Path("/tmp/") test_out_file = test_out_dir / config_output.user_credentials_file config_output.save(test_out_dir, creds=True) diff --git a/tests/test_mirrorlist.py b/tests/test_mirrorlist.py index 196e0ac9..03a5247d 100644 --- a/tests/test_mirrorlist.py +++ b/tests/test_mirrorlist.py @@ -10,10 +10,10 @@ def test_mirrorlist_no_country(mirrorlist_no_country_fixture: Path) -> None: regions = handler.get_mirror_regions() assert len(regions) == 1 - assert regions[0].name == 'Local' + assert regions[0].name == "Local" assert regions[0].urls == [ - 'https://geo.mirror.pkgbuild.com/$repo/os/$arch', - 'https://america.mirror.pkgbuild.com/$repo/os/$arch' + "https://geo.mirror.pkgbuild.com/$repo/os/$arch", + "https://america.mirror.pkgbuild.com/$repo/os/$arch", ] @@ -24,10 +24,10 @@ def test_mirrorlist_with_country(mirrorlist_with_country_fixture: Path) -> None: regions = handler.get_mirror_regions() assert len(regions) == 1 - assert regions[0].name == 'United States' + assert regions[0].name == "United States" assert regions[0].urls == [ - 'https://geo.mirror.pkgbuild.com/$repo/os/$arch', - 'https://america.mirror.pkgbuild.com/$repo/os/$arch' + "https://geo.mirror.pkgbuild.com/$repo/os/$arch", + "https://america.mirror.pkgbuild.com/$repo/os/$arch", ] @@ -38,13 +38,13 @@ def test_mirrorlist_multiple_countries(mirrorlist_multiple_countries_fixture: Pa regions = handler.get_mirror_regions() assert len(regions) == 2 - assert regions[0].name == 'United States' + assert regions[0].name == "United States" assert regions[0].urls == [ - 'https://geo.mirror.pkgbuild.com/$repo/os/$arch', - 'https://america.mirror.pkgbuild.com/$repo/os/$arch' + "https://geo.mirror.pkgbuild.com/$repo/os/$arch", + "https://america.mirror.pkgbuild.com/$repo/os/$arch", ] - assert regions[1].name == 'Australia' + assert regions[1].name == "Australia" assert regions[1].urls == [ - 'https://au.mirror.pkgbuild.com/$repo/os/$arch' + "https://au.mirror.pkgbuild.com/$repo/os/$arch", ]