mirror of
https://gitflic.ru/project/maks1ms/ocab.git
synced 2024-12-24 16:44:44 +03:00
добавлена проверка зависимостей
This commit is contained in:
parent
89fe6a3520
commit
3b849417c3
@ -17,10 +17,11 @@
|
|||||||
"name": "Info",
|
"name": "Info",
|
||||||
"description": "Модуль с информацией",
|
"description": "Модуль с информацией",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
"version": "1.0",
|
"version": "1.0.0",
|
||||||
"privileged": false,
|
"privileged": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"standard.roles": "^1.0.0"
|
"standard.roles": "^1.0.0",
|
||||||
|
"standard.database": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -54,7 +55,6 @@
|
|||||||
|
|
||||||
## Взаимодействие между модулями
|
## Взаимодействие между модулями
|
||||||
|
|
||||||
Модули могут взаимодействовать друг с другом через API, предоставляемое системой управления модулями.
|
Модули могут взаимодействовать друг с другом через [API](../src/ocab_core/modules_system/public_api/__init__.py), предоставляемое системой управления модулями.
|
||||||
|
|
||||||
Например, есть `ocab_core.modules_system.public_api.get_module`.
|
Например, есть функция `get_module`. Она позволяет получить модуль или предоставляемые им объекты по его идентификатору.
|
||||||
Функция, которая получает модуль или предоставляемые им объекты по его идентификатору.
|
|
||||||
|
13
poetry.lock
generated
13
poetry.lock
generated
@ -1124,6 +1124,17 @@ pygments = ">=2.13.0,<3.0.0"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
jupyter = ["ipywidgets (>=7.5.1,<9)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "3.0.2"
|
||||||
|
description = "Python helper for Semantic Versioning (https://semver.org)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "semver-3.0.2-py3-none-any.whl", hash = "sha256:b1ea4686fe70b981f85359eda33199d60c53964284e0cfb4977d243e37cf4bf4"},
|
||||||
|
{file = "semver-3.0.2.tar.gz", hash = "sha256:6253adb39c70f6e51afed2fa7152bcd414c411286088fb4b9effb133885ab4cc"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stevedore"
|
name = "stevedore"
|
||||||
version = "5.2.0"
|
version = "5.2.0"
|
||||||
@ -1307,4 +1318,4 @@ multidict = ">=4.0"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.11.6,<3.13"
|
python-versions = ">=3.11.6,<3.13"
|
||||||
content-hash = "ea35c6a1b64b487fba434fe79cdbf6b78e21941e28d7cda433fd4093c3a0923d"
|
content-hash = "5df07f0efc29d67f3ef2952b7d26a58098c16d5a391f469258f91ba47ebb972f"
|
||||||
|
@ -30,6 +30,7 @@ pyyaml = "^6.0.1"
|
|||||||
requests = "^2.32.3"
|
requests = "^2.32.3"
|
||||||
restrictedpython = "^7.1"
|
restrictedpython = "^7.1"
|
||||||
dataclasses-json = "^0.6.7"
|
dataclasses-json = "^0.6.7"
|
||||||
|
semver = "^3.0.2"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
flake8 = "^7.1.0"
|
flake8 = "^7.1.0"
|
||||||
@ -37,6 +38,7 @@ black = "^24.4.2"
|
|||||||
isort = "^5.13.2"
|
isort = "^5.13.2"
|
||||||
bandit = "^1.7.9"
|
bandit = "^1.7.9"
|
||||||
pre-commit = "^3.7.1"
|
pre-commit = "^3.7.1"
|
||||||
|
semver = "^3.0.2"
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
@ -12,6 +12,8 @@ class ModuleInfo:
|
|||||||
description: str
|
description: str
|
||||||
version: str
|
version: str
|
||||||
author: str
|
author: str
|
||||||
|
privileged: bool
|
||||||
|
dependencies: dict
|
||||||
|
|
||||||
|
|
||||||
class AbstractLoader:
|
class AbstractLoader:
|
||||||
|
@ -18,13 +18,9 @@ class FSLoader(UnsafeFSLoader):
|
|||||||
self.builtins["__import__"] = self._hook_import
|
self.builtins["__import__"] = self._hook_import
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
# TODO handle dependencies from info
|
info = self.info()
|
||||||
|
if info.privileged:
|
||||||
self.info()
|
raise Exception("Only non privileged modules are allowed to be imported")
|
||||||
|
|
||||||
# with open(os.path.join(self.path, "__init__.py"), "r") as f:
|
|
||||||
# source = f.read()
|
|
||||||
|
|
||||||
return self._hook_import(".")
|
return self._hook_import(".")
|
||||||
|
|
||||||
def _resolve_module_from_path(self, module_name: str):
|
def _resolve_module_from_path(self, module_name: str):
|
||||||
@ -52,8 +48,7 @@ class FSLoader(UnsafeFSLoader):
|
|||||||
if name == allowed or name.startswith(f"{allowed}."):
|
if name == allowed or name.startswith(f"{allowed}."):
|
||||||
return __import__(name, *args, **kwargs)
|
return __import__(name, *args, **kwargs)
|
||||||
|
|
||||||
# TODO: allow only public api for modules
|
if name == "ocab_core.modules_system.public_api":
|
||||||
if name.startswith("ocab_core."):
|
|
||||||
return __import__(name, *args, **kwargs)
|
return __import__(name, *args, **kwargs)
|
||||||
|
|
||||||
module_file_path = self._resolve_module_from_path(name)
|
module_file_path = self._resolve_module_from_path(name)
|
||||||
|
@ -1,12 +1,48 @@
|
|||||||
|
import semver
|
||||||
|
|
||||||
from ocab_core.modules_system.loaders.base import AbstractLoader
|
from ocab_core.modules_system.loaders.base import AbstractLoader
|
||||||
|
|
||||||
|
|
||||||
|
def is_version_compatible(version, requirement):
|
||||||
|
def parse_requirement(req):
|
||||||
|
if req.startswith("^"):
|
||||||
|
base_version = req[1:]
|
||||||
|
base_version_info = semver.VersionInfo.parse(base_version)
|
||||||
|
range_start = base_version_info
|
||||||
|
range_end = base_version_info.bump_major()
|
||||||
|
return [f">={range_start}", f"<{range_end}"]
|
||||||
|
else:
|
||||||
|
return [req]
|
||||||
|
|
||||||
|
for r in parse_requirement(requirement):
|
||||||
|
if not semver.Version.parse(version).match(r):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ModulesManager:
|
class ModulesManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.modules = {}
|
self.modules = {}
|
||||||
|
|
||||||
def load(self, loader: AbstractLoader):
|
def load(self, loader: AbstractLoader):
|
||||||
info = loader.info()
|
info = loader.info()
|
||||||
|
|
||||||
|
if info.id in self.modules:
|
||||||
|
return
|
||||||
|
|
||||||
|
for dependency, version in info.dependencies.items():
|
||||||
|
if dependency not in self.modules:
|
||||||
|
raise Exception(
|
||||||
|
f"Module {info.id} depends on {dependency}, but it is not loaded"
|
||||||
|
)
|
||||||
|
loaded_dependency_info = self.modules[dependency]["info"]
|
||||||
|
if not is_version_compatible(loaded_dependency_info.version, version):
|
||||||
|
raise Exception(
|
||||||
|
f"Module {info.id} depends on {dependency}, "
|
||||||
|
f"but version {version} is not compatible"
|
||||||
|
)
|
||||||
|
|
||||||
module = loader.load()
|
module = loader.load()
|
||||||
|
|
||||||
self.modules[info.id] = {
|
self.modules[info.id] = {
|
||||||
|
@ -1 +1,3 @@
|
|||||||
|
from ocab_core.logger import log # noqa
|
||||||
|
|
||||||
from .public_api import Storage, get_module, register_router
|
from .public_api import Storage, get_module, register_router
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Config YAML",
|
"name": "Config YAML",
|
||||||
"description": "Модуль для работы с конфигурационным файлом бота (YAML)",
|
"description": "Модуль для работы с конфигурационным файлом бота (YAML)",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
"version": "1.0",
|
"version": "1.0.0",
|
||||||
"privileged": true,
|
"privileged": true,
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Database",
|
"name": "Database",
|
||||||
"description": "Модуль для работы с БД",
|
"description": "Модуль для работы с БД",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
"version": "1.0",
|
"version": "1.0.0",
|
||||||
"privileged": true,
|
"privileged": true,
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,18 @@
|
|||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
from typing import Any, Type
|
from typing import Type
|
||||||
|
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message
|
||||||
|
|
||||||
from ocab_core.logger import log
|
from ocab_core.modules_system.public_api import get_module, log
|
||||||
from ocab_core.modules_system.public_api import get_module
|
|
||||||
|
|
||||||
from .interfaces import IDbApi, IRoles
|
from .interfaces import IDbApi, IRoles
|
||||||
|
|
||||||
# from src.modules.standard.database import db_api
|
|
||||||
|
|
||||||
|
|
||||||
db_api: Type[IDbApi] = get_module(
|
db_api: Type[IDbApi] = get_module(
|
||||||
"standard.database",
|
"standard.database",
|
||||||
"db_api",
|
"db_api",
|
||||||
)
|
)
|
||||||
|
|
||||||
# db_api.init_db_connection()
|
|
||||||
|
|
||||||
Roles: Type[IRoles] = get_module("standard.roles", "Roles")
|
Roles: Type[IRoles] = get_module("standard.roles", "Roles")
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,9 +3,10 @@
|
|||||||
"name": "Info",
|
"name": "Info",
|
||||||
"description": "Модуль с информацией",
|
"description": "Модуль с информацией",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
"version": "1.0",
|
"version": "1.0.0",
|
||||||
"privileged": false,
|
"privileged": false,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"standard.roles": "^1.0.0"
|
"standard.roles": "^1.0.0",
|
||||||
|
"standard.database": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Roles",
|
"name": "Roles",
|
||||||
"description": "Модуль для работы с ролями",
|
"description": "Модуль для работы с ролями",
|
||||||
"author": "OCAB Team",
|
"author": "OCAB Team",
|
||||||
"version": "1.0",
|
"version": "1.0.0",
|
||||||
"privileged": true,
|
"privileged": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"standard.config": "^1.0.0",
|
"standard.config": "^1.0.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user