Source code for jmbuilder._globals

"""Global Module for `JMBuilder`

This module contains all global variables, constants, and classes
used by the `JMBuilder` module. It provides access to various path
variables and other configurations used throughout the module.

Copyright (c) 2023-2024 Ryuu Mitsuki.


Available Classes
-----------------
JMSetupConfRetriever
    This class provide an easy way to retrieve and access the setup
    configurations for `JMBuilder` module.

Available Constants
-------------------
AUTHOR : str
    The author name (as well as the creator) of the `JMBuilder` module.

BASEDIR : str
    Provides the path to the base directory of the `JMBuilder` package.

    This is an alias for `_JMCustomPath().basedir`.

CONFDIR : str
    Provides the path to the directory that contains configuration
    files for configuring the `JMBuilder` module. The path itself
    is relative to `BASEDIR`.

    This is an alias for `_JMCustomPath().confdir`.

LOGSDIR : str
    Provides the path to the directory used for logging by the
    `JMBuilder` module. The path itself is relative to `BASEDIR`.

    This is an alias for `_JMCustomPath().logsdir`.

STDOUT : TextIO
    An alias for `sys.stdout`, representing the standard output console.

STDERR : TextIO
    An alias for `sys.stderr`, representing the standard error console.

TMPDIR : str or pathlib.Path
    Provides the path to the temporary directory used by the
    `JMBuilder` module. The path itself is relative to `BASEDIR`.

    This is an alias for `_JMCustomPath().tmpdir`.

VERSION : str
    A string representing the JMBuilder's version information.

"""

import os as _os
import sys as _sys
import json as _json
import collections as _collections
from pathlib import Path as _Path
from typing import (
    TextIO,
    Type,
    Union
)

if '_global_imported' in globals():
    raise RuntimeError(
        "Cannot import the '_globals' module more than once.")
_global_imported: bool = True

class _JMCustomPath:
    """
    Custom class to manage read-only path variables for `JMBuilder` module.

    This class provides read-only properties for common path variables
    such as `basedir`, `tmpdir`, `logsdir` and more.

    Parameters
    ----------
    _type : Type[str] or Type[pathlib.Path], optional
        The class type used for casting the path variables. Defaults to
        Python built-in string class (`str`).

        This parameter only supported the following types:
            - `str`
            - `pathlib.Path`

    Raises
    ------
    TypeError
        If `_type` is not a class of `str` neither `pathlib.Path`.

    Notes
    -----
    The path variables are read-only properties, and attempts to modify
    them will raise an `AttributeError`.

    Examples
    --------
      # Use `pathlib.Path` to cast the path
      >>> _JMCustomPath(Path).tmpdir
      PosixPath('.../tmp')  # if Windows, it will be `WindowsPath`

      # `str` type is the default value
      >>> _JMCustomPath().basedir
      '/path/to/base/directory'

      # User can also check the class type that currently
      # used for casting
      >>> _JMCustomPath(Path).type
      <class 'pathlib.Path'>

      # Attempt to modify the value will cause an error
      >>> _JMCustomPath().basedir = '/path/to/another/directory'
      Traceback (most recent call last):
        ...
      AttributeError: can't set attribute

    """

    def __init__(self, _type: Union[Type[str], Type[_Path]] = str):
        """Initialize self. See ``help(type(self))``, for accurate signature."""
        self.__type = _type
        err = 'Invalid type of `_type`: %r. ' + \
              'Expected "str" and "pathlib.Path"'

        if not isinstance(_type, type):
            err = TypeError(err % type(_type).__name__)
            raise err

        if isinstance(_type, type) and \
                 _type.__name__ not in ('str', 'Path', 'pathlib.Path'):
            err = TypeError(err % _type.__name__)
            raise err

        self.__basedir: str = self.__type(str(_Path(__file__).resolve().parent))
        self.__tmpdir:  str = self.__type(_os.path.join(self.__basedir, 'tmp'))
        self.__logsdir: str = self.__type(_os.path.join(self.__basedir, 'logs'))
        self.__confdir: str = self.__type(_os.path.join(self.__basedir, '.config'))

    def __repr__(self) -> str:
        """
        Return the string represents the class name and the class type.

        Returns
        -------
        str
            A string representation of this class.
        """
        return f'{self.__class__.__name__}(type: {self.__type.__name__!r})'

    @property
    def basedir(self) -> Union[str, _Path]:
        """
        The JMBuilder's base directory path based on parent directory of this file.

        Returns
        -------
        str or pathlib.Path
            The current working directory path.
        """
        return self.__basedir

    @property
    def tmpdir(self) -> Union[str, _Path]:
        """
        The path to 'tmp' directory relative to `basedir`.

        Returns
        -------
        str or pathlib.Path
            The path to temporary directory.
        """
        return self.__tmpdir

    @property
    def logsdir(self) -> Union[str, _Path]:
        """
        The path to 'logs' directory relative to `basedir`.

        Returns
        -------
        str or pathlib.Path
            The path to logs directory.
        """
        return self.__logsdir

    @property
    def confdir(self) -> Union[str, _Path]:
        """
        The path to '.config' directory relative to `basedir`.

        Returns
        -------
        str or pathlib.Path
            The path to the specified directory that contains configuration
            files for configuring `JMBuilder` module.
        """
        return self.__confdir

    @property
    def type(self) -> Union[Type[str], Type[_Path]]:
        """
        Return the current class type for casting the path.

        Returns
        -------
        Type[str] or Type[pathlib.Path]
            The class type.
        """
        return self.__type


[docs]class JMSetupConfRetriever: """ A class that retrieves and provides all setup configuration. Notes ----- This class only retrieves the setup configuration without any modification methods to their values. """ def __init__(self): """Initialize self.""" setupfile: str = _os.path.join(_JMCustomPath(str).confdir, 'setup.json') # Get the properties configs: dict = {} with open(setupfile, 'r', encoding='utf-8') as setup_file: configs = _json.load(setup_file) # Create an empty named tuple frozen_ver = _collections.namedtuple( 'FrozenJMVersion', ['major', 'minor', 'patch'], module=__package__ ) # Change the named tuple's documentation frozen_ver.__doc__ = f'{configs.get("Program-Name")}\'s ' + \ 'version information as a frozen named tuple.' key_names: list = ['progname', 'version', 'author', 'license'] self.__jmsetup_data: dict = dict(zip(key_names, configs.values())) # Convert the version info to FrozenJMVersion (a named tuple) self.__jmsetup_data['version'] = frozen_ver(*self.__jmsetup_data['version']) @property def progname(self) -> str: """ Get the program name from setup configuration. Returns ------- str : A string representing the program name. """ return self.__jmsetup_data['progname'] @property def version(self) -> 'FrozenJMVersion': """ Get the module version from setup configuration. Returns ------- FrozenJMVersion : A frozen named tuple representing the module version. """ return self.__jmsetup_data['version'] @property def author(self) -> str: """ Get the author name from setup configuration. Returns ------- str : A string representing the author name. """ return self.__jmsetup_data['author'] @property def license(self) -> str: """ Get the license name from setup configuration. Returns ------- str : A string representing the license name. """ return self.__jmsetup_data['license']
# Aliases __jmsetup__ = JMSetupConfRetriever() __jmpath__ = _JMCustomPath() BASEDIR: Union[str, _Path] = __jmpath__.basedir TMPDIR: Union[str, _Path] = __jmpath__.tmpdir LOGSDIR: Union[str, _Path] = __jmpath__.logsdir CONFDIR: Union[str, _Path] = __jmpath__.confdir STDOUT: TextIO = _sys.stdout STDERR: TextIO = _sys.stderr AUTHOR: str = __jmsetup__.author VERSION: str = '.'.join(map(str, __jmsetup__.version)) VERSION_INFO: str = __jmsetup__.version __author__ = AUTHOR __version__ = VERSION __version_info__ = VERSION_INFO __all__ = [ 'BASEDIR', 'CONFDIR', 'LOGSDIR', 'TMPDIR', 'STDOUT', 'STDERR', 'JMSetupConfRetriever' ] # Remove unnecessary variables del Type, TextIO, Union