Source code for lories.core.configs.directories

# -*- coding: utf-8 -*-
"""
lories.core.configs.directories
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


"""

from __future__ import annotations

import os
from collections.abc import Mapping
from pathlib import Path, PosixPath, WindowsPath
from typing import Dict, Optional

# FIXME: Remove this once Python >= 3.9 is a requirement
try:
    from typing import LiteralString

except ImportError:
    from typing_extensions import LiteralString


class Directories:
    TYPE = "directories"

    LIB = "lib_dir"
    LOG = "log_dir"
    TMP = "tmp_dir"
    DATA = "data_dir"
    CONF = "conf_dir"

    KEYS = [LIB, LOG, TMP, DATA, CONF]

    def __init__(
        self,
        lib_dir: str = None,
        log_dir: str = None,
        tmp_dir: str = None,
        data_dir: str = None,
        conf_dir: str = None,
    ):
        self.lib = lib_dir
        self.log = log_dir
        self.tmp = tmp_dir
        self.data = data_dir
        self.conf = conf_dir

    # noinspection PyProtectedMember
    def __repr__(self) -> str:
        attrs = ["conf", "data", "tmp", "log", "lib"]
        return f"{type(self).__name__}({', '.join(f'{attr}={getattr(self, attr)._dir}' for attr in attrs)})"

    def __str__(self) -> str:
        attrs = ["conf", "data", "tmp", "log", "lib"]
        return f"[{self.TYPE}]\n" + "\n".join(f'{attr} = "{str(getattr(self, attr))}"' for attr in attrs)

    def __copy__(self) -> Directories:
        return self.copy()

    def __deepcopy__(self, memo) -> Directories:
        return self.__copy__()

    # noinspection PyProtectedMember
    def to_dict(self) -> Dict[str, Optional[str]]:
        dirs = {
            self.LIB: self._lib._dir,
            self.LOG: self._log._dir,
            self.TMP: self._tmp._dir,
            self.DATA: self._data._dir,
            self.CONF: self._conf._dir,
        }
        return dirs

    @property
    def lib(self) -> Directory:
        return self._lib

    # noinspection PyShadowingBuiltins
    @lib.setter
    def lib(self, dir: str | Directory) -> None:
        if isinstance(dir, Directory):
            dir = str(dir)
        self._lib = Directory(dir, default="lib")

    @property
    def log(self) -> Directory:
        return self._log

    # noinspection PyShadowingBuiltins
    @log.setter
    def log(self, dir: str | Directory) -> None:
        if isinstance(dir, Directory):
            dir = str(dir)
        self._log = Directory(dir, default="log")

    @property
    def tmp(self) -> Directory:
        return self._tmp

    # noinspection PyShadowingBuiltins
    @tmp.setter
    def tmp(self, dir: str | Directory) -> None:
        if isinstance(dir, Directory):
            dir = str(dir)
        self._tmp = Directory(dir, default="tmp")

    @property
    def data(self) -> Directory:
        return self._data

    # noinspection PyShadowingBuiltins
    @data.setter
    def data(self, dir: str | Directory) -> None:
        if isinstance(dir, Directory):
            dir = str(dir)
        self._data = Directory(dir, default="data")

    @property
    def conf(self) -> Directory:
        return self._conf

    # noinspection PyShadowingBuiltins
    @conf.setter
    def conf(self, dir: str | Path) -> None:
        if isinstance(dir, Path):
            dir = str(dir)
        self._conf = Directory(dir, default="conf", base=self.data)

    # noinspection PyProtectedMember
    def copy(self) -> Directories:
        return Directories(
            self._lib._dir,
            self._log._dir,
            self._tmp._dir,
            self._data._dir,
            self._conf._dir,
        )

    # noinspection PyProtectedMember, PyShadowingBuiltins
    def update(self, configs: Mapping[str, str]) -> None:
        for key in ["lib", "log", "tmp", "data"]:
            dir = configs.get(f"{key}_dir", None)
            if dir is not None:
                setattr(self, f"{key}", dir)
        conf_dir = configs.get("conf_dir", None)
        if conf_dir is not None:
            self.conf = conf_dir


[docs] class Directory(Path): _dir: Optional[Path] = None _base: Path default: str # noinspection PyShadowingBuiltins, PyTypeChecker def __new__(cls, *dirs: str, base: Optional[str | Directory] = None, default: Optional[str] = None): cls = WindowsDirectory if os.name == "nt" else PosixDirectory base = Directory.__parse_base(base) dir = Directory.__parse_dir(base, *dirs, default=default) return super().__new__(cls, Directory.__parse(base, dir, default), base=base, default=default) def __init__(self, *dirs: str, base: Optional[str | Directory] = None, default: Optional[str] = None): self.default = default self._base = Directory.__parse_base(base) self._dir = Directory.__parse_dir(self._base, *dirs, default=default) try: super().__init__(Directory.__parse(self._base, self._dir, default=default)) except TypeError: # FixMe: The mro appears to be called incorrectly for python Versions < 3.12. # ToDo: Remove this catch, as older versions proceed to be deprecated. super().__init__() @staticmethod def __parse_base(base: Optional[LiteralString | str | Path]) -> Path: if base is None or (isinstance(base, Directory) and base.is_default()): base = Path.cwd() elif isinstance(base, Directory) or not isinstance(base, Path): base = Path(base) # FIXME: Remove this once Python >= 3.9 is a requirement # if base.is_relative_to("~"): if str(base).startswith("~"): base = base.expanduser() if not base.is_absolute(): base = base.absolute() return base # noinspection PyShadowingBuiltins @staticmethod def __parse_dir(base: Path, *dirs: Optional[str], default: Optional[str] = None) -> Path: dir = Path(*dirs) if not any(d is None for d in dirs) else None if dir is not None: # FIXME: Remove this once Python >= 3.9 is a requirement # if dir.is_relative_to(base): if str(dir).startswith(str(base)): dir = dir.relative_to(base) # if dir.is_relative_to("~"): if str(dir).startswith("~"): dir = dir.expanduser() if str(dir) == default: dir = None return dir @staticmethod def __parse(base: Path, path: Optional[Path], default: Optional[str] = None) -> Path: if path is None: path = Path(default) if path is not None and not os.path.isabs(path): path = base.joinpath(path) return path def is_default(self) -> bool: if self.default is None: return False return self._dir is None or str(self) == os.path.join(self._base, self.default) # noinspection SpellCheckingInspection
[docs] def joinpath(self, *paths: LiteralString | str | Path) -> Directory: _dir = self.__parse(self._base, self._dir, self.default) return Directory(_dir.joinpath(*paths), default=self.default, base=self._base)
[docs] def relative_to(self, path: LiteralString | str | Path, *_, walk_up=False) -> Path: return self.__parse(self._base, self._dir, self.default).relative_to(path)
[docs] class PosixDirectory(Directory, PosixPath): pass
[docs] class WindowsDirectory(Directory, WindowsPath): pass