From 9d817b54b4739378d0d8b0eba02145879ceca543 Mon Sep 17 00:00:00 2001 From: Alex Tavarez Date: Thu, 25 Dec 2025 09:43:13 -0500 Subject: [PATCH] added classes that allow instantiation of relevant paths tied to given software --- softman.py | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 softman.py diff --git a/softman.py b/softman.py new file mode 100644 index 0000000..b36ea78 --- /dev/null +++ b/softman.py @@ -0,0 +1,153 @@ +""" +Library of classes modeling software and software-related +data as represented in or used by Ansible. +""" + +from typing import TypeAlias as Neotype +from typing import TypedDict as Dict +from typing import Never, Union +from custtypes import ExecutedPath, IdlePath +from enum import Enum +from pathlib import Path, PurePath +from whereami import USER_PATH, PROJ_ROOT + +AppPath: Neotype = Union[ExecutedPath, IdlePath] + +class SoftScope(Enum): + PERSONAL = 0 + LOCAL = 1 + GLOBAL = 2 + +class SoftPathGroup(Enum): + CONFIG = 0 + DATA = 1 + MEM = 2 + +# @TODO continue adding magic methods to below class +# @NOTE https://rszalski.github.io/magicmethods/#sequence +class Software: + # @TODO 2 options: add additional requirements to type definition below, + # or ensure attribute which stores data of this type also stores other data + _AppParams = Dict("_AppParams", { + SoftPathGroup.CONFIG.name: { + SoftScope.PERSONAL.name: IdlePath | list[IdlePath], + SoftScope.LOCAL.name: IdlePath | list[IdlePath], + SoftScope.GLOBAL.name: IdlePath | list[IdlePath] + }, + SoftPathGroup.DATA.name: { + SoftScope.PERSONAL.name: IdlePath | list[IdlePath], + SoftScope.LOCAL.name: IdlePath | list[IdlePath], + SoftScope.GLOBAL.name: IdlePath | list[IdlePath] + }, + SoftPathGroup.MEM.name: { + SoftScope.PERSONAL.name: IdlePath | list[IdlePath], + SoftScope.LOCAL.name: IdlePath | list[IdlePath], + SoftScope.GLOBAL.name: IdlePath | list[IdlePath] + } + }, total=False) + _SubAppParams = Dict("_SubAppParams", { + SoftScope.PERSONAL.name: IdlePath | list[IdlePath], + SoftScope.LOCAL.name: IdlePath | list[IdlePath], + SoftScope.GLOBAL.name: IdlePath | list[IdlePath] + }, total=False) + + __app_input = { + SoftPathGroup.CONFIG.name: { + SoftScope.PERSONAL.name: [], + SoftScope.LOCAL.name: [], + SoftScope.GLOBAL.name: [] + }, + SoftPathGroup.DATA.name: { + SoftScope.PERSONAL.name: [], + SoftScope.LOCAL.name: [], + SoftScope.GLOBAL.name: [] + }, + SoftPathGroup.MEM.name: { + SoftScope.PERSONAL.name: [], + SoftScope.LOCAL.name: [], + SoftScope.GLOBAL.name: [] + } + } + _Apps = type("_Apps", (), **__app_input) + __user_path: ExecutedPath = USER_PATH + + def __init__(self): + self._fqdn: str | None = None + + def declare(self, name: str, **kwpaths: Software._SubAppParams) -> Software._AppParams: + keyword_args: Software._AppParams = kwpaths + + app = Software._Apps(**keyword_args) + setattr(self, name, app) + return app + + def __getitem__(self, key: str) -> Software._AppParams | Never: + if hasattr(self, key): + app: Software._Apps = getattr(self, key) + else: + raise KeyError + return app + + def __setitem__(self, key: tuple[str, SoftPathGroup], **value: IdlePath | list[IdlePath]) -> None | Never: + if len(value) < 1 or len(value) > 3: + raise ValueError + + app_params: Software._SubAppParams = value + + if hasattr(self, key[0]): + app: Software._Apps = getattr(self, key[0]) + + if hasattr(app, key[1]): + app_child: Software._SubAppParams = getattr(app, key[1]) + + for k, v in app_params.items(): + v = [v] if not isinstance(v, list) else v + app_child[k]: IdlePath | list[IdlePath] = v + setattr(app, key[1], app_child) + else: + raise KeyError + + setattr(self, key[0], app) + else: + raise KeyError + + def __delitem__(self, key: tuple[str | SoftPathGroup]) -> None | Never: + if len(key) < 1 or len(key) > 3: + raise KeyError + + if not hasattr(self, key[0]): + raise KeyError + + if len(key) == 1: + delattr(self, key[0]) + elif len(key) > 1: + app: Software._Apps = getattr(self, key[0]) + delattr(app, key[1]) + setattr(self, key[0], app) + + def list(self, contents: bool = False) -> tuple[str]: + apps: tuple[str] | tuple[Software._Apps] = tuple( + filter( + lambda a: isinstance(getattr(self, a), Software._Apps), + dir(self) + ) + ) + + if contents: + apps = tuple( + map( + lambda a: getattr(self, a), + apps + ) + ) + + return apps + + def __len__(self) -> int: + apps: tuple[str] = tuple( + filter( + lambda a: isinstance(getattr(self, a), Software._Apps), + dir(self) + ) + ) + return len(apps) \ No newline at end of file