Source code for util.update

"""
Courier.util.update
~~~~~~~~~~~~~~~~~~~~

This modules is reponsible for updating Courier as
a program. This module is also responsibile for
utility functions and the management of aesthetic
dependencies such as colorama and logging.

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

from datetime import datetime
import json
import logging
from logging.config import fileConfig
import os

import colorama

from .setup import get_date


[docs]def file_exists(file, mode): """Test if file exists in the current working directory. :param file: Filename as a string :param mode: Given filemode eg: read, write, append (as `str`) :return: `False` if file is not present or `TextIOWrapper` if present :rtype: bool """ try: _file = open(file, mode) try: if isinstance(_file, bool): return _file finally: if not isinstance(_file, bool): _file.close() return except FileNotFoundError: logging.error("file not found") return
[docs]def last_updated(): """Last update in datetime format. This is assuming that >>> if project_folder != 'Courier': os.chdir('..') has run as this function is dependant on the current working directory containing the `PACKAGE` file. :return: Current date as a `datetime` object. :rtype: str """ if file_exists("update.json", "r"): with open("update.json", "r") as _file: data = json.load(_file) _file.close() timestamp = datetime.fromtimestamp(data["created"]) date = get_date(timestamp) return date else: return False
[docs]def load_logging_ini(config="config_info.ini"): """Load logging configuration file. :param config: Configuration file as `str`. """ fileConfig(config)
[docs]def scan_dir(files=True, folders=True): """A better version of the os.scandir function, as it takes multiple args. :param files: (optional) List files in current directory :param folders: (optional) List folders in current directory :return: A list of current files and folders found :rtype: list[DirEntry] """ items = [] for item in os.scandir(os.getcwd()): items.append(item) if not files: [items.remove(item) for item in items if os.path.isfile(item)] if not folders: [items.remove(item) for item in items if os.path.isdir(item)] return items
[docs]def get_project_folder(): """Dedicated function for testing. :return: The base-name of of the current project folder. :rtype: str """ project_folder = os.path.basename(os.path.normpath(os.getcwd())) return project_folder
[docs]def switch_root(): """Auto switch root when not applicable. :return: Full current project path as `str` :rtype: str """ project_folder = get_project_folder() if project_folder != "Courier": os.chdir("..") return project_folder
[docs]def create_package(): """Dump json object to `PACKAGE`. `create_package()` raises a permission error if the user does not have write permissions to current working directory. """ try: with open("update.json", "w", True, "UTF-8") as fp: ts = datetime.now().timestamp() data = {"created": ts, "dependencies": {}} json.dump(data, fp) fp.close() except PermissionError: raise PermissionError
[docs]def get_package_name(): """Get package name. Note that this function also allows for unit tests. :return: A directory entry of the current file. :rtype: bool """ switch_root() # Switch root before asking if its in the switched directory for file in scan_dir(files=True, folders=False): if file.name == "update.json": return file
[docs]def loc_package_file(name=get_package_name(), debug=False, mode="r"): """Locate the package file & create package file If the file does not exist it will be created in the current working directory of wherever `courier.py` was called from. :param name: DirEntry of current package name as file :param debug: If a unit test is to be conducted allow for artifically invoked edge cases. :param mode: Edit mode of file to be called as read, write or append. :return: File object if package has been opened or None :rtype: `FileIOWrapper` """ if not name or debug: logger.info(f"Set {GREEN}'update.json'{RESET} in {MAGENTA}{cwd}{RESET}") return create_package() else: with open("update.json", mode) as fp: try: try: return fp finally: fp.close() finally: if "w" in list(mode): fp.close()
logger = logging.getLogger() GREEN = colorama.Fore.GREEN RESET = colorama.Fore.RESET MAGENTA = colorama.Fore.MAGENTA cwd = os.getcwd()