Source code for pawnlib.typing.defines

from pawnlib.config import pawn, pconf
# from os.path import exists as os_path_exists, expanduser as os_path_expanduser
from glob import glob
from dotenv import load_dotenv

import os


class _AttributeHolder(object):
    """Abstract base class that provides __repr__.

    The __repr__ method returns a string in the format::
        ClassName(attr=name, attr=name, ...)
    The attributes are determined either by a class-level attribute,
    '_kwarg_names', or by inspecting the instance __dict__.
    """

    def __repr__(self):
        type_name = type(self).__name__
        arg_strings = []
        star_args = {}
        for arg in self._get_args():
            arg_strings.append(repr(arg))
        for name, value in self._get_kwargs():
            if name.isidentifier():
                arg_strings.append('%s=%r' % (name, value))
            else:
                star_args[name] = value
        if star_args:
            arg_strings.append('**%s' % repr(star_args))
        return '%s(%s)' % (type_name, ', '.join(arg_strings))

    def _get_kwargs(self):
        return sorted(self.__dict__.items())

    def _get_args(self):
        return []

    def _set_args(self, key, value):
        self.__dict__[key] = value

    def _update(self, **kwargs):
        self.__dict__.update(kwargs)


[docs]class Namespace(_AttributeHolder): """Simple object for storing attributes. Implements equality by attribute names and values, and provides a simple string representation. Example: .. code-block:: python from pawnlib.typing import defines namespace = defines.Namespace(s=2323, sdsd="Sdsd") namespace.s # >> 2323 namespace.sdsd # >> 'Sdsd' """ def __init__(self, **kwargs): for name in kwargs: setattr(self, name, kwargs[name]) def __eq__(self, other): if not isinstance(other, Namespace): return NotImplemented return vars(self) == vars(other) def __contains__(self, key): return key in self.__dict__
[docs]def set_namespace_default_value(namespace=None, key='', default=""): """ Set a default value when a key in a namespace has no value :param namespace: :param key: :param default: :return: Example: .. code-block:: python from pawnlib.config import pawn, pconf from pawnlib.typing import set_namespace_default_value pawn.set( data={"aaaa": "bbbb"} ) pawn.console.log(pconf()) undefined_key = set_namespace_default_value( namespace=pconf().data, key="cccc", default="ddddd" ) pawn.console.log(undefined_key) """ if key and hasattr(namespace, key): return getattr(namespace, key) return default
[docs]def fill_required_data_arguments(required={}): """ Fill the required data arguments. :param required: A dictionary of required arguments. :type required: dict :return: The filled arguments. :rtype: argparse.Namespace Example: .. code-block:: python required = {"arg1": "value1", "arg2": "value2"} args = fill_required_data_arguments(required) # args.arg1 == "value1" # args.arg2 == "value2" """ none_string = "__NOT_DEFINED_VALUE__" if getattr(pconf(), "data", None) and getattr(pconf().data, "args", None): args = pconf().data.args for req_key, req_value in required.items(): args_value = getattr(args, req_key, none_string) if args_value == none_string: # pawn.console.debug(f"Define the data args -> {req_key}, {req_value}") setattr(args, req_key, req_value) else: # pawn.console.debug(f"New definition: {required}") args = Namespace(**required) return args
[docs]def load_env_with_defaults(defaults=None, required_keys=None, force_reload=False, verbose=False, dotenv_path=None): """ Load environment variables from a .env file with additional features like defaults, required keys, reload options, and custom logging. :param defaults: A dictionary of default values for environment variables if they are not found in the .env file. Values can be static (e.g., 'default_key') or functions for dynamic defaults (e.g., lambda: 'default'). :type defaults: dict :param required_keys: A list of environment variables that are required. If any of these are missing after loading, a warning or error message will be displayed. :type required_keys: list :param force_reload: If True, overwrites existing environment variables with values from the .env file. :type force_reload: bool :param verbose: If True, enables detailed logging for each loaded variable and missing required keys. :type verbose: bool :param dotenv_path: Optional path to the .env file. Defaults to the ".env" file in the current directory. :type dotenv_path: str Example usage: 1. Basic loading of environment variables from .env file: >>> load_env_with_defaults() 2. Loading with default values for missing environment variables: >>> defaults = { ... 'DATABASE_URL': 'sqlite:///default.db', ... 'API_KEY': 'default_key' ... } >>> load_env_with_defaults(defaults=defaults) 3. Loading required environment variables and checking for missing keys: >>> required_keys = ['DATABASE_URL', 'API_KEY'] >>> load_env_with_defaults(required_keys=required_keys) 4. Forcing reload of .env variables even if they are already set in the environment: >>> load_env_with_defaults(force_reload=True) 5. Enabling verbose logging to see detailed load progress: >>> load_env_with_defaults(verbose=True) 6. Specifying a custom .env file path: >>> load_env_with_defaults(dotenv_path='/custom/path/.env') 7. Combining multiple options: >>> load_env_with_defaults(defaults=defaults, required_keys=required_keys, force_reload=True, verbose=True, dotenv_path='/custom/path/.env') Notes: - `defaults`: Use this to specify fallback values for environment variables not found in the .env file. You can provide static values or dynamic values as functions. - `required_keys`: If any keys in this list are missing after loading, they will be logged (if `verbose=True`) or raise an exception if required. - `force_reload`: Overwrites existing environment variables with .env file values if set to True. - `verbose`: Outputs detailed logs for each environment variable loaded, including defaults and any missing required keys. - `dotenv_path`: Allows specifying a custom .env file location, defaulting to the .env file in the current directory if not provided. """ dotenv_path = dotenv_path or f"{pawn.get_path()}/.env" if not _is_file(dotenv_path): verbose and pawn.console.log(".env file not found") else: verbose and pawn.console.log(f".env file found at '{dotenv_path}'") load_dotenv(dotenv_path=dotenv_path, override=force_reload) # Logging and applying defaults if provided if defaults: for key, value in defaults.items(): if os.getenv(key) is None: os.environ[key] = value verbose and pawn.console.log(f"{key} not found in .env. Using default: {value}") else: verbose and pawn.console.log(f"{key} loaded from .env: {os.getenv(key)}") else: for key, value in os.environ.items(): verbose and pawn.console.log(f"Loaded {key}: {value}") if required_keys: missing_keys = [key for key in required_keys if key not in os.environ] if missing_keys: warning_msg = f"Missing required environment variables: {', '.join(missing_keys)}" pawn.console.log(warning_msg) raise EnvironmentError(warning_msg) # Confirm completion verbose and pawn.console.log("Environment variables loaded successfully.")
def _is_file(filename: str) -> bool: """ Check if the file exists. :param filename: (str) Name of the file to check. :return: (bool) True if the file exists, False otherwise. Example: .. code-block:: python is_file('example.txt') # >> True """ if not filename: return False if "*" in filename: if len(glob(filename)) > 0: return True else: return False else: return os.path.exists(os.path.expanduser(filename))