Source code for courier

#!/usr/bin/env python
"""
Courier.courier
~~~~~~~~~~~~~~~

This module handles user input.

:copyright: (c) 2023 by Joshua Rose.
:license: MIT, see LICENSE for more details.
"""

from glob import glob
import logging
import os
import pathlib
import sys

from util.codescan import Codescan
from util.package import Package
from util.update import load_logging_ini
from util.update import loc_package_file


[docs]class Courier: """Handles user input and file IO.""" def __init__(self): load_logging_ini() self.logger = logging.getLogger() self.bashrc_path = self.bashrc_exists() self.exists = bool(self.bashrc_path) != False if self.exists: self.logger.debug( f" 📂 Found bashrc in {Package.color_path(str(self.bashrc_path))}" ) self.add_bashrc_alias() else: self.logger.warning( " ❌ Could not find bashrc file. Courier may behave unexpectedly." ) self.proc_args(sys.argv) loc_package_file()
[docs] @staticmethod def close_file(file): """Closes a file for unit testing. :param file: TextIOWrapper """ if not file.closed: file.close()
[docs] @staticmethod def get_file_path(): """Fetch immediate parent folder of current directory. :return: String """ return os.path.basename(os.path.normpath(os.getcwd()))
[docs] @staticmethod def assert_file_path(): """Assert if file path is set to the root directory name. :return: Boolean the root directory has been set. """ new_file_path = Courier.get_file_path() return new_file_path == "Courier"
[docs] @staticmethod def read_docs(file="help.txt"): """Read documentation file from folder. Colored documentation is going to be added, so I'm still finding a way to implement that, however it will be done. :param file: (optional) file from /docs as reference. :return: file contents excluding escape characters. :rtype: list[str] """ path = os.getcwd() if Courier.assert_file_path() is not True: logging.debug(Package.color_path(os.getcwd())) os.chdir("..") data = [] # By using 'with' I can ensure that file is closed; thus avoiding # leaving open resources where there need not be any. with open(os.path.join("docs", file), "r", encoding="utf") as _file: data = _file.read().strip().splitlines() Courier.close_file(_file) os.chdir(path) logging.debug(f" ✈️ Moving to {Package.color_path(os.getcwd())}") return data
[docs] @staticmethod def print_formatted_list(lines): """Print list as a paragraph. :param lines: List of strings removed of escape characters. """ for line in lines: print(line)
[docs] @staticmethod def proc_args(args, get_test=False): """Process given command line arguments when Courier is called. :param args: List of CLI arguments, called through sys.argv() """ for i in args: if i.endswith(".py"): args.remove(i) if len(args) == 0: Courier.print_formatted_list(Courier.read_docs(file="help.txt")) return for _, argument in enumerate(args): match argument: case "help": if len(args) == 1: Courier.print_formatted_list(Courier.read_docs(file="help.txt")) return if len(args) >= 2: match args[args.index("help") + 1]: case "--list" | "-l" | "list": Courier.print_formatted_list( Courier.read_docs(file="menus.txt") ) return case "get" | "g": Courier.print_formatted_list( Courier.read_docs(file="get.txt") ) return case "install" | "i": Courier.print_formatted_list( Courier.read_docs(file="install.txt") ) return case "update" | "u" | "--update": Courier.print_formatted_list( Courier.read_docs(file="update.txt") ) return case "codescan" | "cs" | "scan": Courier.print_formatted_list( Courier.read_docs(file="codescan.txt") ) return case "--menu" | "menu": if len(args) >= 3: match args[args.index("menu") + 1]: case "menu": Courier.print_formatted_list( Courier.read_docs(file="menus.txt") ) return case "development": Courier.print_formatted_list( Courier.read_docs( file="development.txt" ) ) return case "testing": Courier.print_formatted_list( Courier.read_docs(file="testing.txt") ) return case "general": Courier.print_formatted_list( Courier.read_docs(file="help.txt") ) return case "help": Courier.print_formatted_list( Courier.read_docs(file="help.txt") ) return case _: Courier.print_formatted_list( Courier.read_docs(file="menus.txt") ) return else: Courier.print_formatted_list( Courier.read_docs(file="menus.txt") ) return case "help": print( "The help command displays mandatory and compulsary arguments for Courier." ) return case _: print(f"Optional argument '{args[1]}' not found") print("Run courier for a list of commands.") return case "--do-nothing": return case "--debug": load_logging_ini("config_debug.ini") Courier.print_formatted_list(Courier.read_docs(file="help.txt")) return case "--clear": os.system("clear") Courier.print_formatted_list(Courier.read_docs(file="help.txt")) return case "codescan": match len(args): case 1: Codescan.install_dependencies() return case 2: Courier.print_formatted_list( Courier.read_docs(file="codescan.txt") ) return case "install": match len(args): case 1: print("Syntax: courier install <package> [version]") return case 2: Package.update_package(args[args.index("install") + 1]) return case 3: Courier.print_formatted_list( Courier.read_docs(file="install.txt") ) return case "get": match len(args): case 1: print("Syntax: courier get <package>") return case 2: Package.search(args[len(args) - 1], get_test) return case 3: Courier.print_formatted_list( Courier.read_docs(file="get.txt") ) return case _: Courier.print_formatted_list(Courier.read_docs(file="help.txt")) return
[docs] def get_package_created(self): """Format return value of the previous timestamp of update.json""" Courier.assert_file_path() self.logger.debug("Package file %s update.json %s was created %s ")
[docs] @staticmethod def bashrc_exists(): """Locate (if present) the user bash configuration file. This function tests under the following directories: home/<user>/ home/<user>/dotfiles/ :return: :class:`pathlib.Path <Path>` object :class:`Literal <False>` boolean """ for file in glob(os.path.join(str(pathlib.Path.home()), "*")): if "bashrc" in file: return pathlib.Path(file) if os.path.exists(os.path.join(str(pathlib.Path.home()), "dotfiles")): for file in glob(os.path.join(str(pathlib.Path.home()), "dotfiles", "*")): if "bashrc" in file: return pathlib.Path(file) return False
[docs] def add_bashrc_alias(self): """Eliminates need for 'python' prefix before file This function also removes the need for a '.py' suffix when calling courier.py. This alias references the src directory such that 'src' is the working directory upon runtime. Courier will not work without this as multiple path locations and tests are configured around 'src' being the working runtime directory. """ if self.exists: contents = [] with open(self.bashrc_path, "r") as file: contents = file.read().strip() file.close() file = os.path.join(os.getcwd(), "courier.py") # BUG can't be called globally for some reason? alias = f"alias courier={sys.executable} {file}" if alias in contents: return with open(self.bashrc_path, "a") as file: file.write("\n# Generated by Courier\n") file.write(f"{alias}\n") file.close() self.logger.debug( f" 👀 Added alias to {Package.color_path(str(self.bashrc_path))}" )
courier = Courier()