From 1ae1f2ff1144d502830834ba5a64262f7d195e91 Mon Sep 17 00:00:00 2001 From: Himadri Bhattacharjee <107522312+lavafroth@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:42:53 +0000 Subject: Refactor installer and general design patterns (#1895) * fix: refactor clear_vt100_escape_codes * fix: check for structure being a dict after handling potential parsing errors * refactor: use short circuit logic than if-elif-else chains * fix: use or for nullish moutpoint attribute * fix: better error handling for JSON from urls and paths * chore: json_stream_to_structure documentation * refactor: dry up relative and chroot path for custom command scripts * refactor: use write_text for pathlib.Path object * refactor: use sets to find intersection instead of filter and list * refactor: replace loop with dictionary comprehension in preparing luks partition * refactor: use walrus operator to check if luks_handler exists * refactor: use read_text and splitlines for potential Path object * fix: use keepends in splitlines for compatibility * fix: use keepends in splitlines for compatibility * feat: set pacman_conf Path as an attribute of installer * fix: empty string is a part of any string, avoid tuples * refactor: use iterator patterns to uncomment multilib and testing blocks * fix: don't json.loads an already loaded structure * fix: use fstab_path uniformly in genfstab * fix: remove unused variable matched * refactor: create separate class to modify pacman.conf in a single pass * fix: remove unused attribute pacman_conf from installer * fix: remove unused attribute pacman_conf from installer * feat: add persist method for pacman.conf, rewrite only when needed * fix: use path.write_text for locale.conf * use `or` operator for nullish new_conf * refactor: Installer.target is always a pathlib.Path object, do not check for string type * fix: use Optional[str] in function type definition instead of sumtype of str and None * fix: mypy type annotation * fix: make flake8 happy * chore: move pacman config and repo into pacman module * refactor: use Pacman object instead of Installer's pacstrap method * fix: break after first sync * fix: keep old build script for now * use nullish operator for base_packages and disk_encryption of Installer * feat: use shutil.which instead of rolling our own implementation * fix: check for binary only if list is not empty * fix: import Enum and fix mypy errors * refactor: use nullish operator for default values * refactor: linear search for key in Installer._trace_log only once * fix: use logs instead of the entirety of self._trace_log when searching for key * refactor: do not copy slice of bytes for search * refactor: use rfind only once to iterate over logs, do not raise ValueError in clear_vt100_escape_codes since TYPE_CHECKING will take care of it. * refactor: try decoding trace log before falling back to strigification * refactor: use an empty dict as default for callbacks in SysCommand.__init__ * refactor: use nullish or operator for slice start and end when not specified * refactor: use nullish or operator for SysCommand session * refactor: use pre-existing decode method in __repr__ for SysCommand * fix: overindentation * fix: use shallow copy of callbacks to prevent mutating the key-value relationships of the argument dict * refactor: use truthy value of self.session is not None for json encoding SysCommand * refactor: directly assign to SysCommand.session in create_session since it short circuits to True if already present * refactor: use dict.items() instead of manually retrieving the value using the key * refactor: user_config_to_json method sounds pretty self explanatory * refactor: store path validity as boolean for return * refactor: use pathlib.Path.write_text to write configs to destinations * fix: cannot use assignment expressions with expression * fix: use config_output.save for saving both config and creds * refactor: switch dictionary keys and values for options to avoid redundancy * refactor: use itertools.takewhile to collect locale.gen entries until the empty line * refactor: use iterative approach for nvidia driver fix * refactor: install packages if not nvidia * refactor: return early if no profile is selected * refactor: use strip to remove commented lines * fix: install additional packages only when we have a driver * fix: path with one command is matched as relative to '.' * fix: remove translation for debug log --------- Co-authored-by: Anton Hvornum --- archinstall/lib/configuration.py | 101 +++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 58 deletions(-) (limited to 'archinstall/lib/configuration.py') diff --git a/archinstall/lib/configuration.py b/archinstall/lib/configuration.py index 8c7b11fa..aeeddbb8 100644 --- a/archinstall/lib/configuration.py +++ b/archinstall/lib/configuration.py @@ -26,7 +26,7 @@ class ConfigurationOutput: self._config = config self._user_credentials: Dict[str, Any] = {} self._user_config: Dict[str, Any] = {} - self._default_save_path = Path(storage.get('LOG_PATH', '.')) + self._default_save_path = storage.get('LOG_PATH', Path('.')) self._user_config_file = 'user_configuration.json' self._user_creds_file = "user_credentials.json" @@ -44,17 +44,17 @@ class ConfigurationOutput: return self._user_config_file def _process_config(self): - for key in self._config: + for key, value in self._config.items(): if key in self._sensitive: - self._user_credentials[key] = self._config[key] + self._user_credentials[key] = value elif key in self._ignore: pass else: - self._user_config[key] = self._config[key] + self._user_config[key] = value # special handling for encryption password - if key == 'disk_encryption' and self._config[key] is not None: - self._user_credentials['encryption_password'] = self._config[key].encryption_password + if key == 'disk_encryption' and value: + self._user_credentials['encryption_password'] = value.encryption_password def user_config_to_json(self) -> str: return json.dumps({ @@ -72,42 +72,33 @@ class ConfigurationOutput: print(_('\nThis is your chosen configuration:')) debug(" -- Chosen configuration --") - user_conig = self.user_config_to_json() - info(user_conig) - + info(self.user_config_to_json()) print() def _is_valid_path(self, dest_path: Path) -> bool: - if (not dest_path.exists()) or not (dest_path.is_dir()): + 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' ) - return False - return True + return dest_path_ok def save_user_config(self, dest_path: Path): if self._is_valid_path(dest_path): target = dest_path / self._user_config_file - - with open(target, 'w') as config_file: - config_file.write(self.user_config_to_json()) - - os.chmod(str(dest_path / self._user_config_file), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + target.write_text(self.user_config_to_json()) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) def save_user_creds(self, dest_path: Path): if self._is_valid_path(dest_path): if user_creds := self.user_credentials_to_json(): target = dest_path / self._user_creds_file - - with open(target, 'w') as config_file: - config_file.write(user_creds) - - os.chmod(str(target), stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) + target.write_text(user_creds) + os.chmod(target, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP) def save(self, dest_path: Optional[Path] = None): - if not dest_path: - dest_path = self._default_save_path + dest_path = dest_path or self._default_save_path if self._is_valid_path(dest_path): self.save_user_config(dest_path) @@ -116,33 +107,33 @@ class ConfigurationOutput: def save_config(config: Dict): def preview(selection: str): - if options["user_config"] == selection: - serialized = config_output.user_config_to_json() - return f"{config_output.user_configuration_file}\n{serialized}" - elif options["user_creds"] == selection: - if maybe_serial := config_output.user_credentials_to_json(): - return f"{config_output.user_credentials_file}\n{maybe_serial}" - else: + match options[selection]: + case "user_config": + serialized = config_output.user_config_to_json() + return f"{config_output.user_configuration_file}\n{serialized}" + case "user_creds": + if maybe_serial := config_output.user_credentials_to_json(): + return f"{config_output.user_credentials_file}\n{maybe_serial}" return str(_("No configuration")) - elif options["all"] == selection: - output = f"{config_output.user_configuration_file}\n" - if config_output.user_credentials_to_json(): - output += f"{config_output.user_credentials_file}\n" - return output[:-1] + case "all": + output = [config_output.user_configuration_file] + if config_output.user_credentials_to_json(): + output.append(config_output.user_credentials_file) + return '\n'.join(output) return None try: config_output = ConfigurationOutput(config) options = { - "user_config": str(_("Save user configuration (including disk layout)")), - "user_creds": str(_("Save user credentials")), - "all": str(_("Save all")), + str(_("Save user configuration (including disk layout)")): "user_config", + str(_("Save user credentials")): "user_creds", + str(_("Save all")): "all", } save_choice = Menu( _("Choose which configuration to save"), - list(options.values()), + list(options), sort=False, skip=True, preview_size=0.75, @@ -170,27 +161,21 @@ def save_config(config: Dict): prompt = _( "Do you want to save {} configuration file(s) in the following location?\n\n{}" - ).format( - list(options.keys())[list(options.values()).index(str(save_choice.value))], - dest_path.absolute(), - ) + ).format(options[str(save_choice.value)], dest_path.absolute()) + save_confirmation = Menu(prompt, Menu.yes_no(), default_option=Menu.yes()).run() if save_confirmation == Menu.no(): return - debug( - _("Saving {} configuration files to {}").format( - list(options.keys())[list(options.values()).index(str(save_choice.value))], - dest_path.absolute(), - ) - ) - - if options["user_config"] == save_choice.value: - config_output.save_user_config(dest_path) - elif options["user_creds"] == save_choice.value: - config_output.save_user_creds(dest_path) - elif options["all"] == save_choice.value: - config_output.save_user_config(dest_path) - config_output.save_user_creds(dest_path) + debug("Saving {} configuration files to {}".format(options[str(save_choice.value)], dest_path.absolute())) + + match options[str(save_choice.value)]: + case "user_config": + config_output.save_user_config(dest_path) + case "user_creds": + config_output.save_user_creds(dest_path) + case "all": + config_output.save(dest_path) + except KeyboardInterrupt: return -- cgit v1.2.3-54-g00ecf