From d76815f27ff40a89aa03f4d7f1f37ba06ec78fe9 Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Fri, 12 Jan 2024 15:49:22 -0600 Subject: [PATCH 1/3] Brought in plugin system from Sparrow as starting point --- poetry.lock | 33 +++- pyproject.toml | 1 + .../macrostrat/subsystem_manager/__init__.py | 144 ++++++++++++++++++ .../macrostrat/subsystem_manager/defs.py | 30 ++++ subsystem-manager/pyproject.toml | 20 +++ 5 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 subsystem-manager/macrostrat/subsystem_manager/__init__.py create mode 100644 subsystem-manager/macrostrat/subsystem_manager/defs.py create mode 100644 subsystem-manager/pyproject.toml diff --git a/poetry.lock b/poetry.lock index ee8a637..50ab035 100644 --- a/poetry.lock +++ b/poetry.lock @@ -344,7 +344,7 @@ url = "app-frame" [[package]] name = "macrostrat-database" -version = "3.0.0-beta1" +version = "3.0.0" description = "A SQLAlchemy-based database toolkit." optional = false python-versions = "^3.8" @@ -409,6 +409,24 @@ typer = "^0.9.0" type = "directory" url = "package-tools" +[[package]] +name = "macrostrat-subsystem-manager" +version = "1.1.0" +description = "A control script framework for containerized applications." +optional = false +python-versions = "^3.10" +files = [] +develop = true + +[package.dependencies] +"macrostrat.utils" = "^1.1.0" +python-dotenv = "^1.0.0" +toposort = "^1.5" + +[package.source] +type = "directory" +url = "subsystem-manager" + [[package]] name = "macrostrat-utils" version = "1.2.0" @@ -977,6 +995,17 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "toposort" +version = "1.10" +description = "Implements a topological sort algorithm." +optional = false +python-versions = "*" +files = [ + {file = "toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87"}, + {file = "toposort-1.10.tar.gz", hash = "sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd"}, +] + [[package]] name = "typer" version = "0.9.0" @@ -1045,4 +1074,4 @@ test = ["websockets"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "17f3fcb2377005f1e8314292b149f6fe18b37fbc5510457b5c6a4efd2cc4ed59" +content-hash = "777b2726df30c262f27c0f5b6da8413326c272417e8647b2dc63ca25e919a301" diff --git a/pyproject.toml b/pyproject.toml index b40a17c..584c70b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ black = "^22.10.0" "macrostrat.database" = {path = "./database", develop = true} "macrostrat.dinosaur" = {path = "./dinosaur", develop = true} "macrostrat.package_tools" = {path = "./package-tools", develop = true} +"macrostrat.subsystem_manager" = {path = "./subsystem-manager", develop = true} "macrostrat.utils" = {path = "./utils", develop = true} pytest = "^7.2.2" python-dotenv = "^1.0.0" diff --git a/subsystem-manager/macrostrat/subsystem_manager/__init__.py b/subsystem-manager/macrostrat/subsystem_manager/__init__.py new file mode 100644 index 0000000..afc6365 --- /dev/null +++ b/subsystem-manager/macrostrat/subsystem_manager/__init__.py @@ -0,0 +1,144 @@ +import sparrow +from toposort import toposort_flatten +from packaging.specifiers import InvalidSpecifier, SpecifierSet +from packaging.version import Version +from sparrow.defs import SparrowPlugin, SparrowCorePlugin, SparrowPluginError + +from ..logs import get_logger + +log = get_logger(__name__) + + +def handle_compat_error(plugin): + _error = ( + f"Plugin '{plugin.name}' is incompatible with Sparrow core " + f"version {sparrow.core.__version__} (expected {plugin.sparrow_version})" + ) + log.error(_error) + if issubclass(plugin, SparrowCorePlugin): + raise SparrowPluginError(_error) + + +def handle_load_error(plugin, err): + _error = f"Could not load plugin '{plugin.name}': {err}" + log.error(_error) + if issubclass(plugin, SparrowCorePlugin): + raise SparrowPluginError(_error) + + +class SparrowPluginManager(object): + """ + Storage class for plugins. Currently, we enforce a single + phase of plugin loading, ended by a call to `finished_loading_plugins`. + Hooks can be run afterwards. This removes the risk of some parts of the + application performing actions before all plugins are initialized. + """ + + _hooks_fired = [] + + def __init__(self): + self.__init_store = [] + self.__store = None + + def __iter__(self): + try: + yield from self.__store + except TypeError: + raise SparrowPluginError("Cannot list plugins until loading is finished.") + + @property + def is_ready(self): + return self.__store is not None + + def _is_compatible(self, plugin): + """Assess package compatibility: https://packaging.pypa.io/en/latest/specifiers.html""" + if plugin.sparrow_version is None: + return True + try: + spec = SpecifierSet(plugin.sparrow_version, prereleases=True) + except InvalidSpecifier: + raise SparrowPluginError( + f"Plugin '{plugin.name}' specifies an invalid Sparrow compatibility range '{plugin.sparrow_version}'" + ) + return Version(sparrow.core.__version__) in spec + + def add(self, plugin): + if not plugin.should_enable(self): + return + if not self._is_compatible(plugin): + handle_compat_error(plugin) + return + + try: + self.__init_store.append(plugin) + except AttributeError: + raise SparrowPluginError( + "Cannot add plugins after Sparrow is finished loading." + ) + except Exception as err: + handle_load_error(plugin, err) + + def add_module(self, module): + for _, obj in module.__dict__.items(): + try: + assert issubclass(obj, SparrowPlugin) + except (TypeError, AssertionError): + continue + + if obj in [SparrowPlugin, SparrowCorePlugin]: + continue + + self.add(obj) + + def add_all(self, *plugins): + for plugin in plugins: + self.add(plugin) + + def order_plugins(self, store=None): + store = store or self.__store + for p in store: + if getattr(p, "name") is None: + raise SparrowPluginError( + f"Sparrow plugin '{p}' must have a name attribute." + ) + struct = {p.name: set(p.dependencies) for p in store} + map_ = {p.name: p for p in store} + res = toposort_flatten(struct, sort=True) + return {map_[k] for k in res} + + def __load_plugin(self, plugin_class, app): + if not issubclass(plugin_class, SparrowPlugin): + raise SparrowPluginError( + "Sparrow plugins must be a subclass of SparrowPlugin" + ) + return plugin_class(app) + + def finalize(self, app): + candidate_store = self.order_plugins(self.__init_store) + + self.__store = [] + for plugin in candidate_store: + self.__store.append(self.__load_plugin(plugin, app)) + + self.__init_store = None + + def get(self, name: str) -> SparrowPlugin: + """Get a plugin object, given its name.""" + for plugin in self.__store: + if plugin.name == name: + return plugin + raise AttributeError(f"Plugin {name} not found") + + def _iter_hooks(self, hook_name): + method_name = "on_" + hook_name.replace("-", "_") + for plugin in self.__store: + method = getattr(plugin, method_name, None) + if method is None: + continue + log.info(" plugin: " + plugin.name) + yield plugin, method + + def run_hook(self, hook_name, *args, **kwargs): + self._hooks_fired.append(hook_name) + for _, method in self._iter_hooks(hook_name): + method(*args, **kwargs) diff --git a/subsystem-manager/macrostrat/subsystem_manager/defs.py b/subsystem-manager/macrostrat/subsystem_manager/defs.py new file mode 100644 index 0000000..d56f6f4 --- /dev/null +++ b/subsystem-manager/macrostrat/subsystem_manager/defs.py @@ -0,0 +1,30 @@ +class SparrowError(Exception): + """Base class for all sparrow-related errors""" + + +class SparrowPluginError(SparrowError): + pass + + +class SparrowPlugin(object): + """A base plugin for Sparrow + + sparrow_version can be set to a specifier of valid versions of Sparrow. This may + become mandatory in a future release of Sparrow. + """ + + dependencies = [] + sparrow_version = None + name = None + + def __init__(self, app): + self.app = app + self.db = self.app.db + # self._register_tasks() + + def should_enable(self): + return True + + +class SparrowCorePlugin(SparrowPlugin): + pass diff --git a/subsystem-manager/pyproject.toml b/subsystem-manager/pyproject.toml new file mode 100644 index 0000000..f1f65e0 --- /dev/null +++ b/subsystem-manager/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +authors = ["Daven Quinn "] +description = "A control script framework for containerized applications." +name = "macrostrat.subsystem_manager" +packages = [ + {include = "macrostrat"}, +] +version = "1.0.0" + +[tool.poetry.dependencies] +"macrostrat.utils" = "^1.1.0" +python = "^3.10" +python-dotenv = "^1.0.0" +toposort = "^1.5" + +[tool.poetry.dev-dependencies] + +[build-system] +build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.0.0"] From 066fb16164e8f6914e03ce8f94c1688eccc96361 Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Fri, 12 Jan 2024 18:00:20 -0600 Subject: [PATCH 2/3] Merge into app_frame module --- Makefile | 6 +- app-frame/macrostrat/app_frame/exc.py | 4 + .../app_frame/subsystems}/__init__.py | 90 +++++----- .../macrostrat/app_frame/subsystems/defs.py | 23 +++ app-frame/pyproject.toml | 2 +- dinosaur/poetry.lock | 163 ++++++++++-------- poetry.lock | 33 +--- pyproject.toml | 1 - .../macrostrat/subsystem_manager/defs.py | 30 ---- 9 files changed, 170 insertions(+), 182 deletions(-) create mode 100644 app-frame/macrostrat/app_frame/exc.py rename {subsystem-manager/macrostrat/subsystem_manager => app-frame/macrostrat/app_frame/subsystems}/__init__.py (59%) create mode 100644 app-frame/macrostrat/app_frame/subsystems/defs.py delete mode 100644 subsystem-manager/macrostrat/subsystem_manager/defs.py diff --git a/Makefile b/Makefile index 109686b..3a32bd3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,8 @@ -all: +.PHONY: install publish test + +all: install + +install: poetry install poetry run mono install diff --git a/app-frame/macrostrat/app_frame/exc.py b/app-frame/macrostrat/app_frame/exc.py new file mode 100644 index 0000000..c1841ad --- /dev/null +++ b/app-frame/macrostrat/app_frame/exc.py @@ -0,0 +1,4 @@ +class ApplicationError(Exception): + """Base class for all errors that should be caught and handled by the application.""" + + pass diff --git a/subsystem-manager/macrostrat/subsystem_manager/__init__.py b/app-frame/macrostrat/app_frame/subsystems/__init__.py similarity index 59% rename from subsystem-manager/macrostrat/subsystem_manager/__init__.py rename to app-frame/macrostrat/app_frame/subsystems/__init__.py index afc6365..9a3fb12 100644 --- a/subsystem-manager/macrostrat/subsystem_manager/__init__.py +++ b/app-frame/macrostrat/app_frame/subsystems/__init__.py @@ -1,32 +1,17 @@ -import sparrow from toposort import toposort_flatten +from ..core import ApplicationBase from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version -from sparrow.defs import SparrowPlugin, SparrowCorePlugin, SparrowPluginError -from ..logs import get_logger +from macrostrat.utils.logs import get_logger -log = get_logger(__name__) - - -def handle_compat_error(plugin): - _error = ( - f"Plugin '{plugin.name}' is incompatible with Sparrow core " - f"version {sparrow.core.__version__} (expected {plugin.sparrow_version})" - ) - log.error(_error) - if issubclass(plugin, SparrowCorePlugin): - raise SparrowPluginError(_error) +from .defs import Subsystem, SubsystemError -def handle_load_error(plugin, err): - _error = f"Could not load plugin '{plugin.name}': {err}" - log.error(_error) - if issubclass(plugin, SparrowCorePlugin): - raise SparrowPluginError(_error) +log = get_logger(__name__) -class SparrowPluginManager(object): +class SubsystemManager: """ Storage class for plugins. Currently, we enforce a single phase of plugin loading, ended by a call to `finished_loading_plugins`. @@ -35,8 +20,10 @@ class SparrowPluginManager(object): """ _hooks_fired = [] + _app: ApplicationBase - def __init__(self): + def __init__(self, app: ApplicationBase): + self._app = app self.__init_store = [] self.__store = None @@ -44,48 +31,48 @@ def __iter__(self): try: yield from self.__store except TypeError: - raise SparrowPluginError("Cannot list plugins until loading is finished.") + raise SubsystemError("Cannot list subsystems until loading is finished.") @property def is_ready(self): return self.__store is not None - def _is_compatible(self, plugin): + def _is_compatible(self, sub: Subsystem): """Assess package compatibility: https://packaging.pypa.io/en/latest/specifiers.html""" - if plugin.sparrow_version is None: + if sub.app_version is None: return True try: - spec = SpecifierSet(plugin.sparrow_version, prereleases=True) + spec = SpecifierSet(sub.app_version, prereleases=True) except InvalidSpecifier: - raise SparrowPluginError( - f"Plugin '{plugin.name}' specifies an invalid Sparrow compatibility range '{plugin.sparrow_version}'" + raise SubsystemError( + f"Subsystem '{sub.name}' specifies an invalid {self.app.name} compatibility range '{sub.app_version}'" ) - return Version(sparrow.core.__version__) in spec + return Version() in spec def add(self, plugin): if not plugin.should_enable(self): return if not self._is_compatible(plugin): - handle_compat_error(plugin) + _raise_compat_error(plugin) return try: self.__init_store.append(plugin) except AttributeError: - raise SparrowPluginError( - "Cannot add plugins after Sparrow is finished loading." + raise SubsystemError( + f"Cannot add subsystems after {self.app.name} is finished loading." ) except Exception as err: - handle_load_error(plugin, err) + _raise_load_error(plugin, err) def add_module(self, module): for _, obj in module.__dict__.items(): try: - assert issubclass(obj, SparrowPlugin) + assert issubclass(obj, Subsystem) except (TypeError, AssertionError): continue - if obj in [SparrowPlugin, SparrowCorePlugin]: + if obj is Subsystem: continue self.add(obj) @@ -98,22 +85,22 @@ def order_plugins(self, store=None): store = store or self.__store for p in store: if getattr(p, "name") is None: - raise SparrowPluginError( - f"Sparrow plugin '{p}' must have a name attribute." + raise SubsystemError( + f"{self.app.name} subsystem '{p}' must have a name attribute." ) struct = {p.name: set(p.dependencies) for p in store} map_ = {p.name: p for p in store} res = toposort_flatten(struct, sort=True) return {map_[k] for k in res} - def __load_plugin(self, plugin_class, app): - if not issubclass(plugin_class, SparrowPlugin): - raise SparrowPluginError( - "Sparrow plugins must be a subclass of SparrowPlugin" + def __load_plugin(self, plugin_class, app: ApplicationBase): + if not issubclass(plugin_class, Subsystem): + raise SubsystemError( + f"{app.name} subsystems must be a subclass of Subsystem" ) return plugin_class(app) - def finalize(self, app): + def finalize(self, app: ApplicationBase): candidate_store = self.order_plugins(self.__init_store) self.__store = [] @@ -122,12 +109,12 @@ def finalize(self, app): self.__init_store = None - def get(self, name: str) -> SparrowPlugin: + def get(self, name: str) -> Subsystem: """Get a plugin object, given its name.""" for plugin in self.__store: if plugin.name == name: return plugin - raise AttributeError(f"Plugin {name} not found") + raise AttributeError(f"Subsystem {name} not found") def _iter_hooks(self, hook_name): method_name = "on_" + hook_name.replace("-", "_") @@ -135,10 +122,25 @@ def _iter_hooks(self, hook_name): method = getattr(plugin, method_name, None) if method is None: continue - log.info(" plugin: " + plugin.name) + log.info(" subsystem: " + plugin.name) yield plugin, method def run_hook(self, hook_name, *args, **kwargs): self._hooks_fired.append(hook_name) for _, method in self._iter_hooks(hook_name): method(*args, **kwargs) + + +def _raise_compat_error(sub: Subsystem, app: ApplicationBase): + _error = ( + f"Subsystem '{sub.name}' is incompatible with {app.name} " + f"version {app.version} (expected {sub.app_version})" + ) + log.error(_error) + raise SubsystemError(_error) + + +def _raise_load_error(sub, err): + _error = f"Could not load subsystem '{sub.name}': {err}" + log.error(_error) + raise SubsystemError(_error) diff --git a/app-frame/macrostrat/app_frame/subsystems/defs.py b/app-frame/macrostrat/app_frame/subsystems/defs.py new file mode 100644 index 0000000..861f8b3 --- /dev/null +++ b/app-frame/macrostrat/app_frame/subsystems/defs.py @@ -0,0 +1,23 @@ +from macrostrat.app_frame import ApplicationError + + +class SubsystemError(ApplicationError): + pass + + +class Subsystem: + """A base subsystem + + app_version can be set to a specifier of valid versions of the hosting application. + """ + + dependencies = [] + app_version = None + name = None + + def __init__(self, app): + self.app = app + self.db = self.app.db + + def should_enable(self): + return True diff --git a/app-frame/pyproject.toml b/app-frame/pyproject.toml index 87f2869..c41dbc3 100644 --- a/app-frame/pyproject.toml +++ b/app-frame/pyproject.toml @@ -6,7 +6,7 @@ packages = [ {include = "macrostrat"}, {include = "test_app"}, ] -version = "1.1.0" +version = "1.2.0" [tool.poetry.dependencies] "macrostrat.utils" = "^1.1.0" diff --git a/dinosaur/poetry.lock b/dinosaur/poetry.lock index f9a301b..2a2ce1a 100644 --- a/dinosaur/poetry.lock +++ b/dinosaur/poetry.lock @@ -160,18 +160,21 @@ ssh = ["paramiko (>=2.4.3)"] [[package]] name = "geoalchemy2" -version = "0.9.4" +version = "0.14.3" description = "Using SQLAlchemy with Spatial Databases" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "GeoAlchemy2-0.9.4-py2.py3-none-any.whl", hash = "sha256:2e3323a8442d4b1f8de69106315c17d8ce729fc127a38a5d7e2b53e1a19b9dd5"}, - {file = "GeoAlchemy2-0.9.4.tar.gz", hash = "sha256:b0e56d4a945bdc0f8fa9edd50ecc912889ea68e0e3558a19160dcb0d5b1b65fc"}, + {file = "GeoAlchemy2-0.14.3-py3-none-any.whl", hash = "sha256:a727198394fcc4760a27c4c5bff8b9f4f79324ec2dd98c4c1b8a7026b8918d81"}, + {file = "GeoAlchemy2-0.14.3.tar.gz", hash = "sha256:79c432b10dd8c48422f794eaf9a1200929de14f41d2396923bfe92f4c6abaf89"}, ] [package.dependencies] packaging = "*" -SQLAlchemy = ">=1.1" +SQLAlchemy = ">=1.4" + +[package.extras] +shapely = ["Shapely (>=1.7)"] [[package]] name = "greenlet" @@ -263,7 +266,7 @@ files = [ [[package]] name = "macrostrat-database" -version = "2.1.3" +version = "3.0.0" description = "A SQLAlchemy-based database toolkit." optional = false python-versions = "^3.8" @@ -272,12 +275,12 @@ develop = true [package.dependencies] click = "^8.1.3" -GeoAlchemy2 = "^0.9.4" +GeoAlchemy2 = "^0.14.0" "macrostrat.utils" = "^1.0.0" -psycopg2-binary = "^2.9.1" -SQLAlchemy = "^1.4.26" -SQLAlchemy-Utils = "^0.37.0" -sqlparse = "^0.4.0" +psycopg2-binary = "^2.9.6" +SQLAlchemy = "^2.0.18" +SQLAlchemy-Utils = "^0.41.1" +sqlparse = "^0.4.4" [package.source] type = "directory" @@ -524,92 +527,104 @@ files = [ [[package]] name = "sqlalchemy" -version = "1.4.48" +version = "2.0.24" description = "Database Abstraction Library" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-1.4.48-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:4bac3aa3c3d8bc7408097e6fe8bf983caa6e9491c5d2e2488cfcfd8106f13b6a"}, - {file = "SQLAlchemy-1.4.48-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dbcae0e528d755f4522cad5842f0942e54b578d79f21a692c44d91352ea6d64e"}, - {file = "SQLAlchemy-1.4.48-cp27-cp27m-win32.whl", hash = "sha256:cbbe8b8bffb199b225d2fe3804421b7b43a0d49983f81dc654d0431d2f855543"}, - {file = "SQLAlchemy-1.4.48-cp27-cp27m-win_amd64.whl", hash = "sha256:627e04a5d54bd50628fc8734d5fc6df2a1aa5962f219c44aad50b00a6cdcf965"}, - {file = "SQLAlchemy-1.4.48-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9af1db7a287ef86e0f5cd990b38da6bd9328de739d17e8864f1817710da2d217"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:ce7915eecc9c14a93b73f4e1c9d779ca43e955b43ddf1e21df154184f39748e5"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5381ddd09a99638f429f4cbe1b71b025bed318f6a7b23e11d65f3eed5e181c33"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:87609f6d4e81a941a17e61a4c19fee57f795e96f834c4f0a30cee725fc3f81d9"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb0808ad34167f394fea21bd4587fc62f3bd81bba232a1e7fbdfa17e6cfa7cd7"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-win32.whl", hash = "sha256:d53cd8bc582da5c1c8c86b6acc4ef42e20985c57d0ebc906445989df566c5603"}, - {file = "SQLAlchemy-1.4.48-cp310-cp310-win_amd64.whl", hash = "sha256:4355e5915844afdc5cf22ec29fba1010166e35dd94a21305f49020022167556b"}, - {file = "SQLAlchemy-1.4.48-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:066c2b0413e8cb980e6d46bf9d35ca83be81c20af688fedaef01450b06e4aa5e"}, - {file = "SQLAlchemy-1.4.48-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99bf13e07140601d111a7c6f1fc1519914dd4e5228315bbda255e08412f61a4"}, - {file = "SQLAlchemy-1.4.48-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee26276f12614d47cc07bc85490a70f559cba965fb178b1c45d46ffa8d73fda"}, - {file = "SQLAlchemy-1.4.48-cp311-cp311-win32.whl", hash = "sha256:49c312bcff4728bffc6fb5e5318b8020ed5c8b958a06800f91859fe9633ca20e"}, - {file = "SQLAlchemy-1.4.48-cp311-cp311-win_amd64.whl", hash = "sha256:cef2e2abc06eab187a533ec3e1067a71d7bbec69e582401afdf6d8cad4ba3515"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:3509159e050bd6d24189ec7af373359f07aed690db91909c131e5068176c5a5d"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc2ab4d9f6d9218a5caa4121bdcf1125303482a1cdcfcdbd8567be8518969c0"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1ddbbcef9bcedaa370c03771ebec7e39e3944782bef49e69430383c376a250b"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f82d8efea1ca92b24f51d3aea1a82897ed2409868a0af04247c8c1e4fef5890"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-win32.whl", hash = "sha256:e3e98d4907805b07743b583a99ecc58bf8807ecb6985576d82d5e8ae103b5272"}, - {file = "SQLAlchemy-1.4.48-cp36-cp36m-win_amd64.whl", hash = "sha256:25887b4f716e085a1c5162f130b852f84e18d2633942c8ca40dfb8519367c14f"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:0817c181271b0ce5df1aa20949f0a9e2426830fed5ecdcc8db449618f12c2730"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1dd2562313dd9fe1778ed56739ad5d9aae10f9f43d9f4cf81d65b0c85168bb"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:68413aead943883b341b2b77acd7a7fe2377c34d82e64d1840860247cec7ff7c"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbde5642104ac6e95f96e8ad6d18d9382aa20672008cf26068fe36f3004491df"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-win32.whl", hash = "sha256:11c6b1de720f816c22d6ad3bbfa2f026f89c7b78a5c4ffafb220e0183956a92a"}, - {file = "SQLAlchemy-1.4.48-cp37-cp37m-win_amd64.whl", hash = "sha256:eb5464ee8d4bb6549d368b578e9529d3c43265007193597ddca71c1bae6174e6"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:92e6133cf337c42bfee03ca08c62ba0f2d9695618c8abc14a564f47503157be9"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d29a3fc6d9c45962476b470a81983dd8add6ad26fdbfae6d463b509d5adcda"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:005e942b451cad5285015481ae4e557ff4154dde327840ba91b9ac379be3b6ce"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c8cfe951ed074ba5e708ed29c45397a95c4143255b0d022c7c8331a75ae61f3"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-win32.whl", hash = "sha256:2b9af65cc58726129d8414fc1a1a650dcdd594ba12e9c97909f1f57d48e393d3"}, - {file = "SQLAlchemy-1.4.48-cp38-cp38-win_amd64.whl", hash = "sha256:2b562e9d1e59be7833edf28b0968f156683d57cabd2137d8121806f38a9d58f4"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a1fc046756cf2a37d7277c93278566ddf8be135c6a58397b4c940abf837011f4"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d9b55252d2ca42a09bcd10a697fa041e696def9dfab0b78c0aaea1485551a08"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6dab89874e72a9ab5462997846d4c760cdb957958be27b03b49cf0de5e5c327c"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd8b5ee5a3acc4371f820934b36f8109ce604ee73cc668c724abb054cebcb6e"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-win32.whl", hash = "sha256:eee09350fd538e29cfe3a496ec6f148504d2da40dbf52adefb0d2f8e4d38ccc4"}, - {file = "SQLAlchemy-1.4.48-cp39-cp39-win_amd64.whl", hash = "sha256:7ad2b0f6520ed5038e795cc2852eb5c1f20fa6831d73301ced4aafbe3a10e1f6"}, - {file = "SQLAlchemy-1.4.48.tar.gz", hash = "sha256:b47bc287096d989a0838ce96f7d8e966914a24da877ed41a7531d44b55cdb8df"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f801d85ba4753d4ed97181d003e5d3fa330ac7c4587d131f61d7f968f416862"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b35c35e3923ade1e7ac44e150dec29f5863513246c8bf85e2d7d313e3832bcfb"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9b3fd5eca3c0b137a5e0e468e24ca544ed8ca4783e0e55341b7ed2807518ee"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6209e689d0ff206c40032b6418e3cfcfc5af044b3f66e381d7f1ae301544b4"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:37e89d965b52e8b20571b5d44f26e2124b26ab63758bf1b7598a0e38fb2c4005"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6910eb4ea90c0889f363965cd3c8c45a620ad27b526a7899f0054f6c1b9219e"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-win32.whl", hash = "sha256:d8e7e8a150e7b548e7ecd6ebb9211c37265991bf2504297d9454e01b58530fc6"}, + {file = "SQLAlchemy-2.0.24-cp310-cp310-win_amd64.whl", hash = "sha256:396f05c552f7fa30a129497c41bef5b4d1423f9af8fe4df0c3dcd38f3e3b9a14"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:adbd67dac4ebf54587198b63cd30c29fd7eafa8c0cab58893d9419414f8efe4b"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a0f611b431b84f55779cbb7157257d87b4a2876b067c77c4f36b15e44ced65e2"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56a0e90a959e18ac5f18c80d0cad9e90cb09322764f536e8a637426afb1cae2f"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6db686a1d9f183c639f7e06a2656af25d4ed438eda581de135d15569f16ace33"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0cc0b486a56dff72dddae6b6bfa7ff201b0eeac29d4bc6f0e9725dc3c360d71"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4a1d4856861ba9e73bac05030cec5852eabfa9ef4af8e56c19d92de80d46fc34"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-win32.whl", hash = "sha256:a3c2753bf4f48b7a6024e5e8a394af49b1b12c817d75d06942cae03d14ff87b3"}, + {file = "SQLAlchemy-2.0.24-cp311-cp311-win_amd64.whl", hash = "sha256:38732884eabc64982a09a846bacf085596ff2371e4e41d20c0734f7e50525d01"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9f992e0f916201731993eab8502912878f02287d9f765ef843677ff118d0e0b1"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2587e108463cc2e5b45a896b2e7cc8659a517038026922a758bde009271aed11"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bb7cedcddffca98c40bb0becd3423e293d1fef442b869da40843d751785beb3"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83fa6df0e035689df89ff77a46bf8738696785d3156c2c61494acdcddc75c69d"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:cc889fda484d54d0b31feec409406267616536d048a450fc46943e152700bb79"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57ef6f2cb8b09a042d0dbeaa46a30f2df5dd1e1eb889ba258b0d5d7d6011b81c"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-win32.whl", hash = "sha256:ea490564435b5b204d8154f0e18387b499ea3cedc1e6af3b3a2ab18291d85aa7"}, + {file = "SQLAlchemy-2.0.24-cp312-cp312-win_amd64.whl", hash = "sha256:ccfd336f96d4c9bbab0309f2a565bf15c468c2d8b2d277a32f89c5940f71fcf9"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9aaaaa846b10dfbe1bda71079d0e31a7e2cebedda9409fa7dba3dfed1ae803e8"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95bae3d38f8808d79072da25d5e5a6095f36fe1f9d6c614dd72c59ca8397c7c0"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a04191a7c8d77e63f6fc1e8336d6c6e93176c0c010833e74410e647f0284f5a1"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:acc58b7c2e40235712d857fdfc8f2bda9608f4a850d8d9ac0dd1fc80939ca6ac"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:00d76fe5d7cdb5d84d625ce002ce29fefba0bfd98e212ae66793fed30af73931"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-win32.whl", hash = "sha256:29e51f848f843bbd75d74ae64ab1ab06302cb1dccd4549d1f5afe6b4a946edb2"}, + {file = "SQLAlchemy-2.0.24-cp37-cp37m-win_amd64.whl", hash = "sha256:e9d036e343a604db3f5a6c33354018a84a1d3f6dcae3673358b404286204798c"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9bafaa05b19dc07fa191c1966c5e852af516840b0d7b46b7c3303faf1a349bc9"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e69290b921b7833c04206f233d6814c60bee1d135b09f5ae5d39229de9b46cd4"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8398593ccc4440ce6dffcc4f47d9b2d72b9fe7112ac12ea4a44e7d4de364db1"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f073321a79c81e1a009218a21089f61d87ee5fa3c9563f6be94f8b41ff181812"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9036ebfd934813990c5b9f71f297e77ed4963720db7d7ceec5a3fdb7cd2ef6ce"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcf84fe93397a0f67733aa2a38ed4eab9fc6348189fc950e656e1ea198f45668"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-win32.whl", hash = "sha256:6f5e75de91c754365c098ac08c13fdb267577ce954fa239dd49228b573ca88d7"}, + {file = "SQLAlchemy-2.0.24-cp38-cp38-win_amd64.whl", hash = "sha256:9f29c7f0f4b42337ec5a779e166946a9f86d7d56d827e771b69ecbdf426124ac"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07cc423892f2ceda9ae1daa28c0355757f362ecc7505b1ab1a3d5d8dc1c44ac6"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a479aa1ab199178ff1956b09ca8a0693e70f9c762875d69292d37049ffd0d8f"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b8d0e8578e7f853f45f4512b5c920f6a546cd4bed44137460b2a56534644205"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17e7e27af178d31b436dda6a596703b02a89ba74a15e2980c35ecd9909eea3a"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1ca7903d5e7db791a355b579c690684fac6304478b68efdc7f2ebdcfe770d8d7"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db09e424d7bb89b6215a184ca93b4f29d7f00ea261b787918a1af74143b98c06"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-win32.whl", hash = "sha256:a5cd7d30e47f87b21362beeb3e86f1b5886e7d9b0294b230dde3d3f4a1591375"}, + {file = "SQLAlchemy-2.0.24-cp39-cp39-win_amd64.whl", hash = "sha256:7ae5d44517fe81079ce75cf10f96978284a6db2642c5932a69c82dbae09f009a"}, + {file = "SQLAlchemy-2.0.24-py3-none-any.whl", hash = "sha256:8f358f5cfce04417b6ff738748ca4806fe3d3ae8040fb4e6a0c9a6973ccf9b6e"}, + {file = "SQLAlchemy-2.0.24.tar.gz", hash = "sha256:6db97656fd3fe3f7e5b077f12fa6adb5feb6e0b567a3e99f47ecf5f7ea0a09e3"}, ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} +typing-extensions = ">=4.2.0" [package.extras] -aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] -mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] mysql-connector = ["mysql-connector-python"] -oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] postgresql = ["psycopg2 (>=2.7)"] postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] postgresql-psycopg2binary = ["psycopg2-binary"] postgresql-psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql", "pymysql (<1)"] -sqlcipher = ["sqlcipher3-binary"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlalchemy-utils" -version = "0.37.9" +version = "0.41.1" description = "Various utility functions for SQLAlchemy." optional = false -python-versions = "~=3.4" +python-versions = ">=3.6" files = [ - {file = "SQLAlchemy-Utils-0.37.9.tar.gz", hash = "sha256:4667edbdcb1ece011076b69772ef524bfbb17cc97e03f11ee6b85d98e7741d61"}, - {file = "SQLAlchemy_Utils-0.37.9-py3-none-any.whl", hash = "sha256:bb6f4da8ac044cb0dd4d0278b1fb434141a5ee9d1881c757a076830ddbb04160"}, + {file = "SQLAlchemy-Utils-0.41.1.tar.gz", hash = "sha256:a2181bff01eeb84479e38571d2c0718eb52042f9afd8c194d0d02877e84b7d74"}, + {file = "SQLAlchemy_Utils-0.41.1-py3-none-any.whl", hash = "sha256:6c96b0768ea3f15c0dc56b363d386138c562752b84f647fb8d31a2223aaab801"}, ] [package.dependencies] -six = "*" -SQLAlchemy = ">=1.0" +SQLAlchemy = ">=1.3" [package.extras] arrow = ["arrow (>=0.3.4)"] @@ -620,8 +635,8 @@ intervals = ["intervals (>=0.7.1)"] password = ["passlib (>=1.6,<2.0)"] pendulum = ["pendulum (>=2.0.5)"] phone = ["phonenumbers (>=5.9.2)"] -test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "mock (==2.0.0)", "pg8000 (>=1.12.4)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] -test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "mock (==2.0.0)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] +test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg (>=3.1.8)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"] timezone = ["python-dateutil"] url = ["furl (>=0.4.1)"] @@ -709,4 +724,4 @@ test = ["websockets"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "a9abbf814357dc8649a55919a85004cd8960ab38e7182d5f69c0e23e5a5f7702" +content-hash = "435c776dc59d620a65e6a10a39dae1e696f82a484457ae81fec8ebe6c15664db" diff --git a/poetry.lock b/poetry.lock index 50ab035..e0958f7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -325,7 +325,7 @@ files = [ [[package]] name = "macrostrat-app-frame" -version = "1.1.0" +version = "1.2.0" description = "A control script framework for containerized applications." optional = false python-versions = "^3.10" @@ -409,24 +409,6 @@ typer = "^0.9.0" type = "directory" url = "package-tools" -[[package]] -name = "macrostrat-subsystem-manager" -version = "1.1.0" -description = "A control script framework for containerized applications." -optional = false -python-versions = "^3.10" -files = [] -develop = true - -[package.dependencies] -"macrostrat.utils" = "^1.1.0" -python-dotenv = "^1.0.0" -toposort = "^1.5" - -[package.source] -type = "directory" -url = "subsystem-manager" - [[package]] name = "macrostrat-utils" version = "1.2.0" @@ -995,17 +977,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "toposort" -version = "1.10" -description = "Implements a topological sort algorithm." -optional = false -python-versions = "*" -files = [ - {file = "toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87"}, - {file = "toposort-1.10.tar.gz", hash = "sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd"}, -] - [[package]] name = "typer" version = "0.9.0" @@ -1074,4 +1045,4 @@ test = ["websockets"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "777b2726df30c262f27c0f5b6da8413326c272417e8647b2dc63ca25e919a301" +content-hash = "17f3fcb2377005f1e8314292b149f6fe18b37fbc5510457b5c6a4efd2cc4ed59" diff --git a/pyproject.toml b/pyproject.toml index 584c70b..b40a17c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,6 @@ black = "^22.10.0" "macrostrat.database" = {path = "./database", develop = true} "macrostrat.dinosaur" = {path = "./dinosaur", develop = true} "macrostrat.package_tools" = {path = "./package-tools", develop = true} -"macrostrat.subsystem_manager" = {path = "./subsystem-manager", develop = true} "macrostrat.utils" = {path = "./utils", develop = true} pytest = "^7.2.2" python-dotenv = "^1.0.0" diff --git a/subsystem-manager/macrostrat/subsystem_manager/defs.py b/subsystem-manager/macrostrat/subsystem_manager/defs.py deleted file mode 100644 index d56f6f4..0000000 --- a/subsystem-manager/macrostrat/subsystem_manager/defs.py +++ /dev/null @@ -1,30 +0,0 @@ -class SparrowError(Exception): - """Base class for all sparrow-related errors""" - - -class SparrowPluginError(SparrowError): - pass - - -class SparrowPlugin(object): - """A base plugin for Sparrow - - sparrow_version can be set to a specifier of valid versions of Sparrow. This may - become mandatory in a future release of Sparrow. - """ - - dependencies = [] - sparrow_version = None - name = None - - def __init__(self, app): - self.app = app - self.db = self.app.db - # self._register_tasks() - - def should_enable(self): - return True - - -class SparrowCorePlugin(SparrowPlugin): - pass From d9097218e2d515a88ac9b6039c09fab5859c2f4b Mon Sep 17 00:00:00 2001 From: Daven Quinn Date: Fri, 12 Jan 2024 22:49:14 -0600 Subject: [PATCH 3/3] Update structure of subsystems manager --- app-frame/macrostrat/app_frame/__init__.py | 2 ++ .../macrostrat/app_frame/subsystems/__init__.py | 12 +++++++++--- app-frame/macrostrat/app_frame/subsystems/defs.py | 5 ++--- app-frame/poetry.lock | 13 ++++++++++++- app-frame/pyproject.toml | 1 + 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app-frame/macrostrat/app_frame/__init__.py b/app-frame/macrostrat/app_frame/__init__.py index 9b3886a..a471258 100644 --- a/app-frame/macrostrat/app_frame/__init__.py +++ b/app-frame/macrostrat/app_frame/__init__.py @@ -1,2 +1,4 @@ from .core import Application from .compose import compose +from .subsystems import SubsystemManager, Subsystem, SubsystemError +from .exc import ApplicationError diff --git a/app-frame/macrostrat/app_frame/subsystems/__init__.py b/app-frame/macrostrat/app_frame/subsystems/__init__.py index 9a3fb12..8cafcd0 100644 --- a/app-frame/macrostrat/app_frame/subsystems/__init__.py +++ b/app-frame/macrostrat/app_frame/subsystems/__init__.py @@ -2,6 +2,7 @@ from ..core import ApplicationBase from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version +from typing import Optional from macrostrat.utils.logs import get_logger @@ -20,13 +21,18 @@ class SubsystemManager: """ _hooks_fired = [] - _app: ApplicationBase + _app: Optional[ApplicationBase] = None + _subsystem_cls: Subsystem = Subsystem - def __init__(self, app: ApplicationBase): - self._app = app + def __init__(self, subsystem_cls: Subsystem = Subsystem): + self._app = None self.__init_store = [] self.__store = None + # Ensure that the plugin class is a subclass of Subsystem + assert issubclass(subsystem_cls, Subsystem) or subsystem_cls is Subsystem + self._subsystem_cls = subsystem_cls + def __iter__(self): try: yield from self.__store diff --git a/app-frame/macrostrat/app_frame/subsystems/defs.py b/app-frame/macrostrat/app_frame/subsystems/defs.py index 861f8b3..e3f9ffb 100644 --- a/app-frame/macrostrat/app_frame/subsystems/defs.py +++ b/app-frame/macrostrat/app_frame/subsystems/defs.py @@ -1,4 +1,4 @@ -from macrostrat.app_frame import ApplicationError +from ..exc import ApplicationError class SubsystemError(ApplicationError): @@ -17,7 +17,6 @@ class Subsystem: def __init__(self, app): self.app = app - self.db = self.app.db - def should_enable(self): + def should_enable(self, mgr: "SubsystemManager"): return True diff --git a/app-frame/poetry.lock b/app-frame/poetry.lock index 662b78c..8816256 100644 --- a/app-frame/poetry.lock +++ b/app-frame/poetry.lock @@ -190,6 +190,17 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "toposort" +version = "1.10" +description = "Implements a topological sort algorithm." +optional = false +python-versions = "*" +files = [ + {file = "toposort-1.10-py3-none-any.whl", hash = "sha256:cbdbc0d0bee4d2695ab2ceec97fe0679e9c10eab4b2a87a9372b929e70563a87"}, + {file = "toposort-1.10.tar.gz", hash = "sha256:bfbb479c53d0a696ea7402601f4e693c97b0367837c8898bc6471adfca37a6bd"}, +] + [[package]] name = "typer" version = "0.9.0" @@ -225,4 +236,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "a2a524ce463937d0831bef398bea837c509f6b3d964db5b91a4b9ce5fb3bcee0" +content-hash = "48ab9ab958c58f9851147b92ae4465052bc1638d306576e2e566509bcf87340c" diff --git a/app-frame/pyproject.toml b/app-frame/pyproject.toml index c41dbc3..0c901a5 100644 --- a/app-frame/pyproject.toml +++ b/app-frame/pyproject.toml @@ -12,6 +12,7 @@ version = "1.2.0" "macrostrat.utils" = "^1.1.0" python = "^3.10" python-dotenv = "^1.0.0" +toposort = "^1.5" rich = "^13" typer = "^0.9.0"