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()