Example #1
0
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
Example #2
0
 def reset_parser(self) -> None:
     """
     Erases all data from previous config file parsing attempt by
     creating a new instance of config_parser
     """
     self.config_parser = configparser.ConfigParser(allow_no_value=True)
     self.config_parser.optionxform = str
     logger.trace(f"config_parser reset")
Example #3
0
 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")
Example #4
0
def run(config: config_loader.Config) -> None:
    """
    Runs the program depending on the options given
    in the command line (`config.cli_args.ordered()`).
    """
    for argument, value in config.cli_args.ordered():
        logger.trace(f"Processing option {repr(argument)}, {repr(value)}")
        # cli option --something
        if argument == "something":
            pass
Example #5
0
 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}")
Example #6
0
 def read_config_and_check_syntax(self) -> None:
     while True:
         try:
             self.read_config_file()
             break
         except FileNotFoundError:
             logger.trace(f"FileNotFoundError was raised, handling it")
             logger.opt(raw=True).info(
                 "Config file missing, creating new default config file\n")
             self.create_config_file()
             logger.trace(f"Finished handling FileNotFoundError")
             continue
         except UnicodeDecodeError as err:
             raise errors.WrongConfiguration(
                 f"Could not read configuration", err)
         except AttributeError as err:
             # happens when the config file syntax is so currpt that config_parser cannot read it
             raise errors.WrongConfiguration(
                 f"Could not read configuration, file syntax might be wrong",
                 err)
Example #7
0
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
Example #8
0
    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()
Example #9
0
 def parse_config_and_check_values(self) -> None:
     """
     Parses the configuration files into usable attributes
     and checks whether the values are syntactically correct.
     For example, check if an IPv4 address consists of four chained integers in range(256).
     """
     try:
         self.some_value = self.config_parser.getint(
             "Values", "preset_value")
         for path in self.configured_paths:
             checked_path = self.getpath("Local Paths", path.internal_name)
             self.check_path(checked_path)
             setattr(self, path.internal_name, checked_path)
             logger.trace(
                 f"Config.{path.internal_name} = {str(getattr(self, path.internal_name))}"
             )
             setattr(getattr(self, "_" + path.internal_name), "conf_path",
                     getattr(self, path.internal_name))
             logger.trace(
                 f"Config._{path.internal_name}.conf_path = {str(getattr(self, '_' + path.internal_name).conf_path)}"
             )
         for section, definitions, defattr, prefix in zip(
             ("Log level colors", "Highlight colors"),
             (self.log_levels, self.highlight_definitions),
             ("level", "type"),
             ("log_level", "highlight"),
         ):
             for definition in definitions:
                 property = "_".join((prefix, getattr(definition,
                                                      defattr).lower()))
                 setattr(self, property,
                         self.get_and_check_color(section, property))
                 logger.opt(colors=False).debug(
                     f"Config.{property} = {getattr(self, property)}")
         for keyword_highlight_group in ("_".join(
             (prefix, word)) for prefix, word in zip(
                 repeat("keywords"),
                 map(lambda x: x.type, self.highlight_definitions))):
             self.check_and_set_keyword_highlight_groups(
                 keyword_highlight_group)
     except ValueError as err:
         exc_type, exc_value, exc_traceback = sys.exc_info()
         lines = traceback.format_exc().splitlines()
         section, variable, value = False, False, False
         for line in lines:
             if "config_parser" in line:
                 match = re.search(self.error_regex, line)
                 if match:
                     section = match.group("section")
                     variable = match.group("variable")
         value = lines[-1].split()[-1]
         if section and variable and value:
             message = f"{self.path_to_config_file.name}: '{variable}' in section '{section}' has an unacceptable value of {value}"
             logger.trace(
                 f"Raising WrongConfiguration error because of wrong section/value"
             )
             raise errors.WrongConfiguration(message, err)
         else:
             logger.trace(f"Re-raising ValueError")
             raise
     except configparser.NoOptionError as err:
         logger.trace(
             f"Raising WrongConfiguration error because of NoOptionError")
         raise errors.WrongConfiguration(
             f"{self.path_to_config_file.name}: {err.message}", err)
     except configparser.NoSectionError as err:
         logger.trace(
             f"Raising WrongConfiguration error because of NoSectionError")
         logger.exception(err)
         raise errors.WrongConfiguration(
             f"{self.path_to_config_file.name}: {err.message}", err)
     except OSError as err:
         # OSError [WinError 123] Occurs when there are reserved characters in a path
         # But Linux and Mac will throw a different kind of OSError
         # So, for now, instead of differentiating the kinds of OSErrors, we'll just reraise them all
         logger.trace(
             f"Reraising OSError, assuming it is because of bad syntax in path value"
         )
         raise
     except Exception as err:
         logger.trace(
             f"Raising WrongConfiguration error because of some general Exception"
         )
         logger.exception(err)
         raise errors.WrongConfiguration(
             f"There is something wrong with {self.path_to_config_file.name}. Please check it carefully or delete it to have it recreated.",
             err)
Example #10
0
 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")
Example #11
0
 def choose_from(self, options: collections.OrderedDict,
                 options_log_level: utils.Log_level) -> str:
     self.show_options(options, options_log_level)
     choice = None
     choices = tuple(key.upper() for key in options.keys())
     logger.trace(f"{choices = }")
     while choice not in choices:
         try:
             if choice == False:
                 logger.trace(f"Empyting wait_key output")
                 _ = self.wait_key()
                 logger.trace(f"choice changing from False to None")
                 choice = None
                 continue
             else:
                 logger.trace(f"{choice = }")
                 logger.trace(f"Waiting for key")
                 choice = self.wait_key()
                 logger.trace(f"{choice = }")
         except UnicodeDecodeError:
             logger.trace(
                 "Handling UnicodeDecodeError, changing choice to False")
             choice = False
     return choice