summaryrefslogtreecommitdiff
path: root/plugin_loader/loader.py
diff options
context:
space:
mode:
authormarios <marios8543@gmail.com>2022-04-18 15:57:51 +0300
committerGitHub <noreply@github.com>2022-04-18 15:57:51 +0300
commitfa776f0d0b7b4ca7071f0aa912be84e81caf6b61 (patch)
tree535f6f0bb71ab97f17a0ef21eb8f0491ae4ca87d /plugin_loader/loader.py
parent4576fed01baac066a8550018a0922739cc2f8985 (diff)
downloaddecky-loader-1.2.0.tar.gz
decky-loader-1.2.0.zip
Callsigns (#37)v1.2.0
* Plugin callsigns, filechangehandler thread bug fix, plugin file perms - Plugins are now assigned a callsign (a random string), which they use for all internal identification, like resource fetching and method calls. This is to ensure that plugins only access their own resources and methods. - Made FileChangeHandler send off events to a queue, that is then consumed by the Loader, instead of calling import_plugin on its own, since that caused weird issues with the event loop and the thread watchdog is using. - Plugins are now owned by root and have read-only permissions. This is handled automatically. * Improved general look and feel of plugin tab * Make all plugin entries have the same padding between them * Make "No plugins installed" text look the same as "No new notifications" Co-authored-by: WerWolv <werwolv98@gmail.com>
Diffstat (limited to 'plugin_loader/loader.py')
-rw-r--r--plugin_loader/loader.py52
1 files changed, 31 insertions, 21 deletions
diff --git a/plugin_loader/loader.py b/plugin_loader/loader.py
index ae609301..f060b5b6 100644
--- a/plugin_loader/loader.py
+++ b/plugin_loader/loader.py
@@ -2,19 +2,21 @@ from aiohttp import web
from aiohttp_jinja2 import template
from watchdog.observers.polling import PollingObserver as Observer
from watchdog.events import FileSystemEventHandler
-
+from asyncio import Queue
from os import path, listdir
from logging import getLogger
+from time import time
from injector import get_tabs, get_tab
from plugin import PluginWrapper
+from traceback import print_exc
class FileChangeHandler(FileSystemEventHandler):
- def __init__(self, loader, plugin_path) -> None:
+ def __init__(self, queue, plugin_path) -> None:
super().__init__()
self.logger = getLogger("file-watcher")
- self.loader : Loader = loader
self.plugin_path = plugin_path
+ self.queue = queue
def on_created(self, event):
src_path = event.src_path
@@ -31,7 +33,7 @@ class FileChangeHandler(FileSystemEventHandler):
rel_path = path.relpath(src_path, path.commonprefix([self.plugin_path, src_path]))
plugin_dir = path.split(rel_path)[0]
main_file_path = path.join(self.plugin_path, plugin_dir, "main.py")
- self.loader.import_plugin(main_file_path, plugin_dir, refresh=True)
+ self.queue.put_nowait((main_file_path, plugin_dir, True))
def on_modified(self, event):
src_path = event.src_path
@@ -46,7 +48,7 @@ class FileChangeHandler(FileSystemEventHandler):
# file that changed is not necessarily the one that needs to be reloaded
self.logger.debug(f"file modified: {src_path}")
plugin_dir = path.split(path.relpath(src_path, path.commonprefix([self.plugin_path, src_path])))[0]
- self.loader.import_plugin(path.join(self.plugin_path, plugin_dir, "main.py"), plugin_dir, refresh=True)
+ self.queue.put_nowait((path.join(self.plugin_path, plugin_dir, "main.py"), plugin_dir, True))
class Loader:
def __init__(self, server_instance, plugin_path, loop, live_reload=False) -> None:
@@ -55,16 +57,18 @@ class Loader:
self.plugin_path = plugin_path
self.logger.info(f"plugin_path: {self.plugin_path}")
self.plugins = {}
+ self.callsigns = {}
self.import_plugins()
if live_reload:
+ self.reload_queue = Queue()
self.observer = Observer()
- self.observer.schedule(FileChangeHandler(self, plugin_path), self.plugin_path, recursive=True)
+ self.observer.schedule(FileChangeHandler(self.reload_queue, plugin_path), self.plugin_path, recursive=True)
self.observer.start()
+ self.loop.create_task(self.handle_reloads())
server_instance.add_routes([
web.get("/plugins/iframe", self.plugin_iframe_route),
- web.get("/plugins/reload", self.reload_plugins),
web.get("/plugins/load_main/{name}", self.load_plugin_main_view),
web.get("/plugins/plugin_resource/{name}/{path:.+}", self.handle_sub_route),
web.get("/plugins/load_tile/{name}", self.load_plugin_tile_view),
@@ -75,18 +79,23 @@ class Loader:
try:
plugin = PluginWrapper(file, plugin_directory, self.plugin_path)
if plugin.name in self.plugins:
- if not "hot_reload" in plugin.flags and refresh:
+ if not "debug" in plugin.flags and refresh:
self.logger.info(f"Plugin {plugin.name} is already loaded and has requested to not be re-loaded")
return
else:
- self.plugins[plugin.name].stop(self.loop)
+ self.plugins[plugin.name].stop()
self.plugins.pop(plugin.name, None)
+ self.callsigns.pop(plugin.callsign, None)
if plugin.passive:
self.logger.info(f"Plugin {plugin.name} is passive")
- self.plugins[plugin.name] = plugin.start(self.loop)
+ callsign = str(time())
+ plugin.callsign = callsign
+ self.plugins[plugin.name] = plugin.start()
+ self.callsigns[callsign] = plugin
self.logger.info(f"Loaded {plugin.name}")
except Exception as e:
self.logger.error(f"Could not load {file}. {e}")
+ print_exc()
finally:
if refresh:
self.loop.create_task(self.refresh_iframe())
@@ -99,14 +108,15 @@ class Loader:
self.logger.info(f"found plugin: {directory}")
self.import_plugin(path.join(self.plugin_path, directory, "main.py"), directory)
- async def reload_plugins(self, request=None):
- self.logger.info("Re-importing plugins.")
- self.import_plugins()
+ async def handle_reloads(self):
+ while True:
+ args = await self.reload_queue.get()
+ self.import_plugin(*args)
- async def handle_plugin_method_call(self, plugin_name, method_name, **kwargs):
+ async def handle_plugin_method_call(self, callsign, method_name, **kwargs):
if method_name.startswith("_"):
raise RuntimeError("Tried to call private method")
- return await self.plugins[plugin_name].execute_method(method_name, kwargs)
+ return await self.callsigns[callsign].execute_method(method_name, kwargs)
async def get_steam_resource(self, request):
tab = (await get_tabs())[0]
@@ -116,7 +126,7 @@ class Loader:
return web.Response(text=str(e), status=400)
async def load_plugin_main_view(self, request):
- plugin = self.plugins[request.match_info["name"]]
+ plugin = self.callsigns[request.match_info["name"]]
# open up the main template
with open(path.join(self.plugin_path, plugin.plugin_directory, plugin.main_view_html), 'r') as template:
@@ -124,14 +134,14 @@ class Loader:
# setup the main script, plugin, and pull in the template
ret = f"""
<script src="/static/library.js"></script>
- <script>const plugin_name = '{plugin.name}' </script>
- <base href="http://127.0.0.1:1337/plugins/plugin_resource/{plugin.name}/">
+ <script>const plugin_name = '{plugin.callsign}' </script>
+ <base href="http://127.0.0.1:1337/plugins/plugin_resource/{plugin.callsign}/">
{template_data}
"""
return web.Response(text=ret, content_type="text/html")
async def handle_sub_route(self, request):
- plugin = self.plugins[request.match_info["name"]]
+ plugin = self.callsigns[request.match_info["name"]]
route_path = request.match_info["path"]
self.logger.info(path)
@@ -144,7 +154,7 @@ class Loader:
return web.Response(text=ret)
async def load_plugin_tile_view(self, request):
- plugin = self.plugins[request.match_info["name"]]
+ plugin = self.callsigns[request.match_info["name"]]
inner_content = ""
@@ -160,7 +170,7 @@ class Loader:
<head>
<link rel="stylesheet" href="/static/styles.css">
<script src="/static/library.js"></script>
- <script>const plugin_name = '{plugin.name}';</script>
+ <script>const plugin_name = '{plugin.callsign}';</script>
</head>
<body style="height: fit-content; display: block;">
{inner_content}