From 6487a7a8a5eb0f72960aaaeba954ef09a7b6daf2 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Fri, 11 Jun 2021 16:03:52 +0300 Subject: [PATCH] Let plugins read their own files directly --- maubot/cli/commands/build.py | 6 ++++-- maubot/instance.py | 2 +- maubot/loader/__init__.py | 2 +- maubot/loader/abc.py | 36 +++++++++++++++++++++++++----------- maubot/loader/zip.py | 15 +++++++++++++-- maubot/plugin_base.py | 13 ++++++++----- 6 files changed, 52 insertions(+), 22 deletions(-) diff --git a/maubot/cli/commands/build.py b/maubot/cli/commands/build.py index 9e3724c..a7b2fe1 100644 --- a/maubot/cli/commands/build.py +++ b/maubot/cli/commands/build.py @@ -16,6 +16,7 @@ from typing import Optional, Union, IO from io import BytesIO import zipfile +import glob import os from ruamel.yaml import YAML, YAMLError @@ -94,8 +95,9 @@ def write_plugin(meta: PluginMeta, output: Union[str, IO]) -> None: else: print(Fore.YELLOW + f"Module {module} not found, skipping" + Fore.RESET) - for file in meta.extra_files: - zip.write(file) + for pattern in meta.extra_files: + for file in glob.iglob(pattern): + zip.write(file) def upload_plugin(output: Union[str, IO], server: str) -> None: diff --git a/maubot/instance.py b/maubot/instance.py index b2312b2..8d1dea2 100644 --- a/maubot/instance.py +++ b/maubot/instance.py @@ -199,7 +199,7 @@ class PluginInstance: self.config = config_class(self.load_config, base_cfg_func, self.save_config) self.plugin = cls(client=self.client.client, loop=self.loop, http=self.client.http_client, instance_id=self.id, log=self.log, config=self.config, - database=self.inst_db, webapp=self.inst_webapp, + database=self.inst_db, loader=self.loader, webapp=self.inst_webapp, webapp_url=self.inst_webapp_url) try: await self.plugin.internal_start() diff --git a/maubot/loader/__init__.py b/maubot/loader/__init__.py index ec298ad..b783152 100644 --- a/maubot/loader/__init__.py +++ b/maubot/loader/__init__.py @@ -1,2 +1,2 @@ -from .abc import PluginLoader, PluginClass, IDConflictError, PluginMeta +from .abc import BasePluginLoader, PluginLoader, PluginClass, IDConflictError, PluginMeta from .zip import ZippedPluginLoader, MaubotZipImportError diff --git a/maubot/loader/abc.py b/maubot/loader/abc.py index 01713f4..f148451 100644 --- a/maubot/loader/abc.py +++ b/maubot/loader/abc.py @@ -1,5 +1,5 @@ # maubot - A plugin-based Matrix bot system. -# Copyright (C) 2019 Tulir Asokan +# Copyright (C) 2021 Tulir Asokan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -65,7 +65,30 @@ class PluginMeta(SerializableAttrs['PluginMeta']): soft_dependencies: List[str] = [] -class PluginLoader(ABC): +class BasePluginLoader(ABC): + meta: PluginMeta + + @property + @abstractmethod + def source(self) -> str: + pass + + def sync_read_file(self, path: str) -> bytes: + raise NotImplementedError("This loader doesn't support synchronous operations") + + @abstractmethod + async def read_file(self, path: str) -> bytes: + pass + + def sync_list_files(self, directory: str) -> List[str]: + raise NotImplementedError("This loader doesn't support synchronous operations") + + @abstractmethod + async def list_files(self, directory: str) -> List[str]: + pass + + +class PluginLoader(BasePluginLoader, ABC): id_cache: Dict[str, 'PluginLoader'] = {} meta: PluginMeta @@ -85,15 +108,6 @@ class PluginLoader(ABC): "instances": [instance.to_dict() for instance in self.references], } - @property - @abstractmethod - def source(self) -> str: - pass - - @abstractmethod - async def read_file(self, path: str) -> bytes: - pass - async def stop_instances(self) -> None: await asyncio.gather(*[instance.stop() for instance in self.references if instance.started]) diff --git a/maubot/loader/zip.py b/maubot/loader/zip.py index 72daf22..6d8a8ce 100644 --- a/maubot/loader/zip.py +++ b/maubot/loader/zip.py @@ -1,5 +1,5 @@ # maubot - A plugin-based Matrix bot system. -# Copyright (C) 2019 Tulir Asokan +# Copyright (C) 2021 Tulir Asokan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -106,9 +106,20 @@ class ZippedPluginLoader(PluginLoader): f"meta={self.meta} " f"loaded={self._loaded is not None}>") - async def read_file(self, path: str) -> bytes: + def sync_read_file(self, path: str) -> bytes: return self._file.read(path) + async def read_file(self, path: str) -> bytes: + return self.sync_read_file(path) + + def sync_list_files(self, directory: str) -> List[str]: + directory = directory.rstrip("/") + return [file.filename for file in self._file.filelist + if os.path.dirname(file.filename) == directory] + + async def list_files(self, directory: str) -> List[str]: + return self.sync_list_files(directory) + @staticmethod def _read_meta(source) -> Tuple[ZipFile, PluginMeta]: try: diff --git a/maubot/plugin_base.py b/maubot/plugin_base.py index 65e2039..fc3dae1 100644 --- a/maubot/plugin_base.py +++ b/maubot/plugin_base.py @@ -1,5 +1,5 @@ # maubot - A plugin-based Matrix bot system. -# Copyright (C) 2019 Tulir Asokan +# Copyright (C) 2021 Tulir Asokan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -15,7 +15,6 @@ # along with this program. If not, see . from typing import Type, Optional, TYPE_CHECKING from abc import ABC -from logging import Logger from asyncio import AbstractEventLoop from sqlalchemy.engine.base import Engine @@ -23,26 +22,29 @@ from aiohttp import ClientSession from yarl import URL if TYPE_CHECKING: + from mautrix.util.logging import TraceLogger from mautrix.util.config import BaseProxyConfig from .client import MaubotMatrixClient from .plugin_server import PluginWebApp + from .loader import BasePluginLoader class Plugin(ABC): client: 'MaubotMatrixClient' http: ClientSession id: str - log: Logger + log: 'TraceLogger' loop: AbstractEventLoop + loader: 'BasePluginLoader' config: Optional['BaseProxyConfig'] database: Optional[Engine] webapp: Optional['PluginWebApp'] webapp_url: Optional[URL] def __init__(self, client: 'MaubotMatrixClient', loop: AbstractEventLoop, http: ClientSession, - instance_id: str, log: Logger, config: Optional['BaseProxyConfig'], + instance_id: str, log: 'TraceLogger', config: Optional['BaseProxyConfig'], database: Optional[Engine], webapp: Optional['PluginWebApp'], - webapp_url: Optional[str]) -> None: + webapp_url: Optional[str], loader: 'BasePluginLoader') -> None: self.client = client self.loop = loop self.http = http @@ -52,6 +54,7 @@ class Plugin(ABC): self.database = database self.webapp = webapp self.webapp_url = URL(webapp_url) if webapp_url else None + self.loader = loader self._handlers_at_startup = [] def register_handler_class(self, obj) -> None: