diff options
| -rw-r--r-- | tools/cru-py/cru/app.py | 81 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_base.py | 214 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_config.py | 15 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_data.py | 7 | ||||
| -rw-r--r-- | tools/cru-py/cru/service/_template.py | 13 | 
5 files changed, 199 insertions, 131 deletions
| diff --git a/tools/cru-py/cru/app.py b/tools/cru-py/cru/app.py deleted file mode 100644 index d45fd30..0000000 --- a/tools/cru-py/cru/app.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -from pathlib import Path - -from ._error import CruException -from ._path import CruPath - - -class CruApplication: -    def __init__(self, name: str) -> None: -        self._name = name - - -class ApplicationPathError(CruException): -    def __init__(self, message, _path: Path, *args, **kwargs): -        super().__init__(message, *args, **kwargs) -        self._path = _path - -    @property -    def path(self) -> Path: -        return self._path - - -class ApplicationPath: -    def __init__(self, app_dir: str, subpath: str, is_dir: bool) -> None: -        self._app_dir = app_dir -        self._subpath = subpath -        self._full_path = CruPath(app_dir, subpath) -        self._is_dir = is_dir - -    @property -    def app_dir(self) -> str: -        return self._app_dir - -    @property -    def subpath(self) -> str: -        return self._subpath - -    @property -    def full_path(self) -> CruPath: -        return self._full_path - -    @property -    def full_path_str(self) -> str: -        return str(self._full_path) - -    @property -    def is_dir(self) -> bool: -        return self._is_dir - -    def check_parents(self, must_exist: bool = False) -> bool: -        return self.full_path.check_parents_dir(must_exist) - -    def check_self(self, must_exist: bool = False) -> bool: -        if not self.check_parents(must_exist): -            return False -        if not self.full_path.exists(): -            if not must_exist: -                return False -            raise ApplicationPathError("Not exist.", self.full_path) -        if self.is_dir: -            if not self.full_path.is_dir(): -                raise ApplicationPathError( -                    "Should be a directory, but not.", self.full_path -                ) -            else: -                return False -        else: -            if not self.full_path.is_file(): -                raise ApplicationPathError("Should be a file, but not.", self.full_path) -            else: -                return False - -    def ensure(self, create_file: bool = False) -> None: -        e = self.check_self(False) -        if not e: -            os.makedirs(self.full_path.parent, exist_ok=True) -            if self.is_dir: -                os.mkdir(self.full_path) -            elif create_file: -                with open(self.full_path, "w") as f: -                    f.write("") diff --git a/tools/cru-py/cru/service/_base.py b/tools/cru-py/cru/service/_base.py index c84ab05..f91eadd 100644 --- a/tools/cru-py/cru/service/_base.py +++ b/tools/cru-py/cru/service/_base.py @@ -2,11 +2,11 @@ from __future__ import annotations  from argparse import ArgumentParser, Namespace  from abc import ABC, abstractmethod -from collections.abc import Iterable +import os +from pathlib import Path  from typing import TypeVar, overload -from cru import CruIterator, CruInternalError -from cru.app import ApplicationPath, CruApplication +from cru import CruException, CruInternalError, CruPath  _F = TypeVar("_F") @@ -15,13 +15,149 @@ class InternalAppException(CruInternalError):      pass +class AppPathError(CruException): +    def __init__(self, message, _path: str | Path, *args, **kwargs): +        super().__init__(message, *args, **kwargs) +        self._path = str(_path) + +    @property +    def path(self) -> str: +        return self._path + + +class AppPath(ABC): +    def __init__( +        self, +        name: str, +        is_dir: bool, +        /, +        id: str | None = None, +        description: str = "", +    ) -> None: +        self._name = name +        self._is_dir = is_dir +        self._id = id or name +        self._description = description + +    @property +    @abstractmethod +    def parent(self) -> AppPath | None: ... + +    @property +    def name(self) -> str: +        return self._name + +    @property +    @abstractmethod +    def app(self) -> AppBase: ... + +    @property +    def id(self) -> str: +        return self._id + +    @property +    def description(self) -> str: +        return self._description + +    @property +    def is_dir(self) -> bool: +        return self._is_dir + +    @property +    def full_path(self) -> CruPath: +        if self.parent is None: +            return CruPath(self.name) +        else: +            return CruPath(self.parent.full_path, self.name) + +    @property +    def full_path_str(self) -> str: +        return str(self.full_path) + +    def check_parents(self, must_exist: bool = False) -> bool: +        return self.full_path.check_parents_dir(must_exist) + +    def check_self(self, must_exist: bool = False) -> bool: +        if not self.check_parents(must_exist): +            return False +        if not self.full_path.exists(): +            if not must_exist: +                return False +            raise AppPathError("Not exist.", self.full_path) +        if self.is_dir: +            if not self.full_path.is_dir(): +                raise AppPathError("Should be a directory, but not.", self.full_path) +            else: +                return False +        else: +            if not self.full_path.is_file(): +                raise AppPathError("Should be a file, but not.", self.full_path) +            else: +                return False + +    def ensure(self, create_file: bool = False) -> None: +        e = self.check_self(False) +        if not e: +            os.makedirs(self.full_path.parent, exist_ok=True) +            if self.is_dir: +                os.mkdir(self.full_path) +            elif create_file: +                with open(self.full_path, "w") as f: +                    f.write("") + +    def add_subpath( +        self, +        name: str, +        is_dir: bool, +        /, +        id: str | None = None, +        description: str = "", +    ) -> AppFeaturePath: +        return self.app.add_path(name, is_dir, self, id, description) + + +class AppFeaturePath(AppPath): +    def __init__( +        self, +        parent: AppPath, +        name: str, +        is_dir: bool, +        /, +        id: str | None = None, +        description: str = "", +    ) -> None: +        super().__init__(name, is_dir, id, description) +        self._parent = parent + +    @property +    def parent(self) -> AppPath: +        return self._parent + +    @property +    def app(self) -> AppBase: +        return self.parent.app + + +class AppRootPath(AppPath): +    def __init__(self, app: AppBase, path: str): +        super().__init__(path, True, "root", "Application root path.") +        self._app = app + +    @property +    def parent(self) -> None: +        return None + +    @property +    def app(self) -> AppBase: +        return self._app + +  class AppFeatureProvider:      def __init__(self, name: str, /, app: AppBase | None = None):          super().__init__()          self._name = name          self._app = app if app else AppBase.get_instance() -        self._app_paths: list[ApplicationPath] = [] -        self.app.add_app_feature(self) +        self.app.add_feature(self)      @property      def app(self) -> AppBase: @@ -31,15 +167,6 @@ class AppFeatureProvider:      def name(self) -> str:          return self._name -    @property -    def app_paths(self) -> list[ApplicationPath]: -        return self._app_paths - -    def add_app_path(self, subpath: str, is_dir: bool) -> ApplicationPath: -        p = ApplicationPath(self.app.app_dir, subpath, is_dir) -        self._app_paths.append(p) -        return p -  class AppCommandFeatureProvider(AppFeatureProvider, ABC):      @abstractmethod @@ -49,7 +176,7 @@ class AppCommandFeatureProvider(AppFeatureProvider, ABC):      def run_command(self, args: Namespace) -> None: ... -class AppBase(CruApplication): +class AppBase:      _instance: AppBase | None = None      @staticmethod @@ -58,28 +185,47 @@ class AppBase(CruApplication):              raise CruInternalError("App instance not initialized")          return AppBase._instance -    def __init__(self, name: str, app_dir: str): -        super().__init__(name) +    def __init__(self, name: str, root: str):          AppBase._instance = self -        self._app_dir = app_dir -        self._app_features: list[AppFeatureProvider] = [] +        self._name = name +        self._root = AppRootPath(self, root) +        self._paths: list[AppFeaturePath] = [] +        self._features: list[AppFeatureProvider] = []      @property -    def app_dir(self) -> str: -        return self._app_dir +    def name(self) -> str: +        return self._name      @property -    def app_features(self) -> list[AppFeatureProvider]: -        return self._app_features +    def root(self) -> AppRootPath: +        return self._root      @property -    def app_paths(self) -> Iterable[ApplicationPath]: -        return ( -            CruIterator(self._app_features).transform(lambda x: x.app_paths).flatten(1) -        ) +    def features(self) -> list[AppFeatureProvider]: +        return self._features + +    @property +    def paths(self) -> list[AppFeaturePath]: +        return self._paths -    def add_app_feature(self, feature: AppFeatureProvider) -> None: -        self._app_features.append(feature) +    def add_feature(self, feature: AppFeatureProvider) -> AppFeatureProvider: +        self._features.append(feature) +        return feature + +    def add_path( +        self, +        name: str, +        is_dir: bool, +        /, +        parent: AppPath | None = None, +        id: str | None = None, +        description: str = "", +    ) -> AppFeaturePath: +        p = AppFeaturePath( +            parent or self.root, name, is_dir, id=id, description=description +        ) +        self._paths.append(p) +        return p      @overload      def get_feature(self, feature: str) -> AppFeatureProvider: ... @@ -89,11 +235,11 @@ class AppBase(CruApplication):      def get_feature(self, feature: str | type[_F]) -> AppFeatureProvider | _F:          if isinstance(feature, str): -            for f in self._app_features: +            for f in self._features:                  if f.name == feature:                      return f          elif isinstance(feature, type): -            for f in self._app_features: +            for f in self._features:                  if isinstance(f, feature):                      return f          else: @@ -102,3 +248,9 @@ class AppBase(CruApplication):              )          raise InternalAppException(f"Feature {feature} not found.") + +    def get_path(self, name: str) -> AppFeaturePath: +        for p in self._paths: +            if p.id == name or p.name == name: +                return p +        raise InternalAppException(f"Application path {name} not found.") diff --git a/tools/cru-py/cru/service/_config.py b/tools/cru-py/cru/service/_config.py index 1838015..a387ef7 100644 --- a/tools/cru-py/cru/service/_config.py +++ b/tools/cru-py/cru/service/_config.py @@ -1,15 +1,14 @@ -import os.path -from ._base import AppFeatureProvider +from ._base import AppFeaturePath, AppFeatureProvider  from ._data import DataManager  class ConfigManager(AppFeatureProvider): -    def __init__(self, config_file_name="config") -> None: +    def __init__(self) -> None:          super().__init__("config-manager") -        self._file_name = config_file_name +        self._config_path = self.app.get_feature(DataManager).data_dir.add_subpath( +            "config", False, description="Configuration file path." +        )      @property -    def config_file_path(self) -> str: -        return os.path.join( -            self.app.get_feature(DataManager).data_dir.full_path, self._file_name -        ) +    def config_path(self) -> AppFeaturePath: +        return self._config_path diff --git a/tools/cru-py/cru/service/_data.py b/tools/cru-py/cru/service/_data.py index f38dc23..3cddfc8 100644 --- a/tools/cru-py/cru/service/_data.py +++ b/tools/cru-py/cru/service/_data.py @@ -1,12 +1,11 @@ -from cru.app import ApplicationPath -from ._base import AppFeatureProvider +from ._base import AppFeaturePath, AppFeatureProvider  class DataManager(AppFeatureProvider):      def __init__(self) -> None:          super().__init__("data-manager") -        self._dir = self.add_app_path("data", True) +        self._dir = self.app.add_path("data", True)      @property -    def data_dir(self) -> ApplicationPath: +    def data_dir(self) -> AppFeaturePath:          return self._dir diff --git a/tools/cru-py/cru/service/_template.py b/tools/cru-py/cru/service/_template.py index bf13212..fcc5658 100644 --- a/tools/cru-py/cru/service/_template.py +++ b/tools/cru-py/cru/service/_template.py @@ -1,26 +1,25 @@  from argparse import ArgumentParser, Namespace - -from ._base import AppCommandFeatureProvider -from cru.app import ApplicationPath  from cru.template import TemplateTree +from ._base import AppCommandFeatureProvider, AppFeaturePath +  class TemplateManager(AppCommandFeatureProvider):      def __init__(self, prefix: str = "CRUPEST"):          super().__init__("template-manager") -        self._templates_dir = self.add_app_path("templates", True) -        self._generated_dir = self.add_app_path("generated", True) +        self._templates_dir = self.app.add_path("templates", True) +        self._generated_dir = self.app.add_path("generated", True)          self._template_tree = TemplateTree(              prefix, self._templates_dir.full_path_str, self._generated_dir.full_path_str          )      @property -    def templates_dir(self) -> ApplicationPath: +    def templates_dir(self) -> AppFeaturePath:          return self._templates_dir      @property -    def generated_dir(self) -> ApplicationPath: +    def generated_dir(self) -> AppFeaturePath:          return self._generated_dir      def add_arg_parser(self, arg_parser: ArgumentParser) -> None: | 
