Commit 6487a7a8 authored by Tulir Asokan's avatar Tulir Asokan 🐈
Browse files

Let plugins read their own files directly

parent 8f806a25
Pipeline #2941 passed with stages
in 1 minute and 2 seconds
......@@ -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:
......
......@@ -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()
......
from .abc import PluginLoader, PluginClass, IDConflictError, PluginMeta
from .abc import BasePluginLoader, PluginLoader, PluginClass, IDConflictError, PluginMeta
from .zip import ZippedPluginLoader, MaubotZipImportError
# 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])
......
# 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:
......
# 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 <https://www.gnu.org/licenses/>.
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:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment