def configure_path(self, path: Configured_Path, manually: bool) -> None: if manually: logger.trace(f"Configuring path {path.internal_name} manually") while True: try: # Special treatment of ... if False: pass # Normal paths else: logger.trace(f"Normal treatment") input_path = utils.input_path( f"Input path to the {path.comment}: ") created_path = utils.provide_dir(input_path) break except OSError as err: # Happens if, for example, a folder or a file cannot be created because it already exists. logger.error(err) continue else: logger.trace(f"Configuring paths automatically") # Special case if False: pass # Normal case else: created_path = utils.provide_dir(path.conf_path) logger.debug(f"created_path {str(created_path)}") # update the path in the list of configured paths path.conf_path = created_path logger.trace(f"_{path.internal_name}.conf_path = {created_path}")
def __init__(self, cfg_path: pathlib.Path, autoconfig: bool) -> None: self.path_to_config_file = cfg_path self.path_to_home = utils.provide_dir(self.path_to_config_file.parent) self.path_to_starter_home = pathlib.Path.home() / utils.home_dir_name self._local_sub_dir = Configured_Path( internal_name="local_sub_dir", comment="Just an example of a subdirectory", preconf_path=self.path_to_starter_home / "subdir", ) self.configured_paths = (self._local_sub_dir, ) self.read_config_and_check_syntax() self.parse_config_and_check_values() self.set_logging_colors() # Let's create convenient highlighting shortcut functions # so that we can write e.g. `config.noteworthy(word)` instead of # config.highlight(word, config.highlight_noteworthy) logger.debug( f"Creating convenient shortcut functions for keyword highlighting:" ) for highlight_definition in self.highlight_definitions: setattr( self, highlight_definition.type, partial(self.highlight, color_tag=getattr( self, "_".join( ("highlight", highlight_definition.type))))) logger.debug( f"Config.{highlight_definition.type} = lambda word: Config.highlight(word, Config.highlight_{highlight_definition.type}" ) while (wrong_paths := self.filter_wrong_paths(self.configured_paths)): self.wizard(wrong_paths, autoconfig) self.create_config_file() self.read_config_and_check_syntax() self.parse_config_and_check_values()
def cli_start(version) -> None: """ Entry point for the starter start from command line """ parser = argument_parser() argcomplete.autocomplete(parser) cli_args = parser.parse_args(namespace=OrderedNamespace()) logger.trace(f"Parsed arguments: {cli_args}") logger.debug(f"Ordered command line arguments: {tuple(cli_args.ordered())}") path_to_config = path_to_dir / utils.configuration_file_name try: if cli_args.version: logger.info(f"{version}") with config_loader.Config(path_to_config, cli_args.autoconfig) as config: config.cli_args = cli_args run(config) except errors.WrongConfiguration as err: logger.trace(f"Handling WrongConfiguration error (will just type it out for the user)") if err.payload is not None: logger.error(f"{err.message}: {err.payload.__class__.__name__} {err.payload}") logger.error(f"Correct `{str(path_to_config)}` manually or delete it to create a default one.") except KeyboardInterrupt as e: logger.error(f"Keyboard Interrupt") except Exception as err: logger.exception(f"Uncaught exception {repr(err)} occurred.") finally: pass
def set_default_log_level_colors(self) -> None: section = "Log level colors" for log_level in self.log_levels: variable = "_".join(("log_level", log_level.level.lower())) self.config_parser.set(section, variable, log_level.default_color) logger.debug( f"self.config_parser.set({repr(section)}, {repr(variable)}, {repr(log_level.default_color)})" )
def read_config_file(self) -> None: """ Reads current configuration file """ self.reset_parser() logger.trace(f"config_parser might raise FileNotFoundError") self.config_parser.read_file(open(self.path_to_config_file)) logger.trace(f"config_parser did not raise FileNotFoundError") logger.debug(f"{self.path_to_config_file.name} read successfully")
def set_default_highlight( self, section: Literal["Highlight colors", "Highlight keywords"], attr_prefix: Literal["highlight", "keyword"], nt_attr: Literal["default_color", "default_keywords"], ) -> None: for highlight_definition in self.highlight_definitions: variable = "_".join((attr_prefix, highlight_definition.type)) value = getattr(highlight_definition, nt_attr) self.config_parser.set(section, variable, value) logger.debug( f"self.config_parser.set({repr(section)}, {repr(variable)}, {repr(value)}" )
def check_and_set_keyword_highlight_groups( self, keyword_highlight_group: str) -> None: wl = tuple( filter( bool, re.split( self.color_delimiter, self.config_parser.get("Highlight keywords", keyword_highlight_group)))) setattr(self, keyword_highlight_group, wl) for word in wl: self.highlight_keywords[word] = keyword_highlight_group.replace( "keywords", "highlight") logger.debug( f"Config.highlight_keywords[{repr(word)}] = {repr(self.highlight_keywords[word])}" )
def provide_dir(directory: pathlib.Path) -> pathlib.Path: """ Checks if there is a directory of name `dir_name` in the user home path. If not, it will try to create one. """ if directory.exists() and directory.is_dir(): logger.trace(f"Found directory {str(directory)}") else: while True: try: directory.mkdir() logger.success(f"Created directory {str(directory)}") break except FileNotFoundError: provide_dir(directory.parent) continue except FileExistsError: logger.debug(f"{directory} already exists") break return directory
def wizard(self, wrong_paths: Tuple[Configured_Path], autoconfig: bool) -> None: """ Configuration wizard helps the user to correct the paths which don't exist """ wiz_menu = menu.Text_Menu(menu_name="Configuration Wizard", heading=r""" ____ ____ __ _ ____ _ ____ _ _ ____ ____ ___ _ ____ __ _ |___ [__] | \| |--- | |__, |__| |--< |--| | | [__] | \| _ _ _ ___ ____ ____ ___ |/\| | /__ |--| |--< |__> """) heading_was_shown = False number_to_word = dict(( (1, "one"), (2, "two"), (3, "three"), (4, "four"), (5, "five"), (6, "six"), (7, "seven"), (8, "eight"), (9, "nine"), (10, "ten"), (11, "eleven"), (12, "twelve"), )) for path in wrong_paths: if not heading_was_shown: wiz_menu.show_heading("INFO") heading_was_shown = True reason = ( f"{path.internal_name} ({path.conf_path}) does not exist!") comment = path.comment options = OrderedDict(( ("A", f""), ("C", "Create this path automatically"), ("M", "Manually input correct path to use or to create"), ("Q", f"Quit to edit {self.highlight(path.internal_name, self.highlight_noteworthy)} in {self.highlight(self.path_to_config_file.name, self.highlight_noteworthy)} manually" ), )) number_of_remaining_paths = len( wrong_paths) - 1 - wrong_paths.index(path) if number_of_remaining_paths >= 2: options[ "A"] = f"Automatically configure this and {self.noteworthy(number_to_word[number_of_remaining_paths])} remaining path settings" elif number_of_remaining_paths == 1: options[ "A"] = f"Automatically configure this and {self.noteworthy(number_to_word[number_of_remaining_paths])} remaining path setting" else: # number_of_remaining_paths == 0 del options["A"] if autoconfig: choice = "C" else: choice = None if not choice: wiz_menu.show_reason(reason, "IMPORTANT") wiz_menu.show_comment( "".join((" " * len(utils.warn("")), comment[0].capitalize() + comment[1:])), "INFO") choice = wiz_menu.choose_from(options, "PROCEDURE") if choice == "A": autoconfig = True choice = "C" logger.debug(f"Your choice: {choice}") if choice == "C": self.configure_path(path, manually=False) elif choice == "M": self.configure_path(path, manually=True) elif choice == "Q": # create config file to save the progress until now self.create_config_file() logger.trace(f"Raising WrongConfiguration error") raise errors.WrongConfiguration( f"Who needs a wizard, when you can edit `{self.path_to_config_file.name}` yourself, right?", None) self.create_config_file()
def create_config_file(self) -> None: """ Creates the default config file """ self.reset_parser() self.config_parser.add_section("Local Paths") self.config_parser.set( "Local Paths", "# You can write paths in Windows format or Linux/POSIX format.") self.config_parser.set( "Local Paths", "# A trailing '/' at the end of the final directory in a POSIX path" ) self.config_parser.set( "Local Paths", "# or a '\\' at the end of the final directory of a Windows path") self.config_parser.set("Local Paths", "# does not interfere with the path parser.") self.config_parser.set("Local Paths", "") for path in self.configured_paths: logger.trace( f"Setting {path.internal_name} to path.conf_path = {str(path.conf_path)} in the config file" ) self.config_parser.set( "Local Paths", f"# {path.comment[0].capitalize()}{path.comment[1:]}") self.config_parser.set("Local Paths", f"{path.internal_name}", f"{path.conf_path}") self.config_parser.add_section("Values") self.config_parser.set("Values", "# Some config values") self.config_parser.set("Values", "") self.config_parser.set("Values", "preset_value", "42") self.config_parser.add_section("Log level colors") self.config_parser.set( "Log level colors", "# Default colors of log levels which are output to the user.") self.config_parser.set( "Log level colors", "# Acceptable values are the keywords from the AnsiParser class") self.config_parser.set( "Log level colors", "# in https://github.com/Delgan/loguru/blob/master/loguru/_colorizer.py" ) self.config_parser.set( "Log level colors", "# e.g. 'black', 'red', 'green', 'cyan', 'white' ...") self.config_parser.set( "Log level colors", "# optionally prefixed with 'light-', e.g. 'light-blue', 'light-yellow' etc." ) self.config_parser.set( "Log level colors", "# You can also add style and background, e.g. 'light-green, underline, LIGHT-BLACK'" ) self.config_parser.set( "Log level colors", "# Separate the words with a comma and a space.") self.config_parser.set( "Log level colors", "# Value 'normal' can be used to disable color highlighting.") self.config_parser.set("Log level colors", "") self.set_default_log_level_colors() self.config_parser.add_section("Highlight colors") self.config_parser.set( "Highlight colors", "# Certain words or substrings in log lines will be highlighted") self.config_parser.set( "Highlight colors", "# and temporarily override the log line's default color.") self.config_parser.set( "Highlight colors", "# Use the same color-defining rules as in the section Log level colors" ) self.config_parser.set( "Highlight colors", "# to define the colors of each highlight type.") self.config_parser.set( "Log level colors", "# Value 'normal' can be used to disable color highlighting.") self.config_parser.set("Highlight colors", "") self.set_default_highlight(section="Highlight colors", attr_prefix="highlight", nt_attr="default_color") self.config_parser.add_section("Highlight keywords") self.config_parser.set( "Highlight keywords", "# Define which words or substrings will be highlighted with which highlight type." ) self.config_parser.set("Highlight keywords", "# Separate words with a comma and a space.") self.config_parser.set( "Highlight keywords", "# These keywords are highlighted only in certain predefined output lines" ) self.config_parser.set( "Highlight keywords", "# where they are expected, e.g. when retrieved as values from RSI properties." ) self.config_parser.set( "Highlight keywords", "# Defining a keyword here guarantees by no means") self.config_parser.set( "Highlight keywords", "# that it would also be highlighted in any other output line.") self.config_parser.set( "Highlight keywords", "# Experts: You can use regular expressions here, but they must not contain ', '" ) self.config_parser.set( "Highlight keywords", "# because this is used as a keyword separator. If needed, use '\\ \\,' in the regex." ) self.config_parser.set("Highlight keywords", "") self.set_default_highlight(section="Highlight keywords", attr_prefix="keywords", nt_attr="default_keywords") with open(self.path_to_config_file, mode="w", encoding="utf-8") as configfh: self.config_parser.write(configfh) logger.debug(f"Config file written")
def wrapped(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() logger.debug("Function '{}' executed in {:f} s", name, end - start) return result