[+] plugins command

This commit is contained in:
2024-07-29 03:09:02 +03:00
parent 2b4c0bf4d0
commit 2bf1c07041
2 changed files with 168 additions and 95 deletions
+17 -4
View File
@@ -96,6 +96,21 @@ class EventsSystem:
self.log.debug("used builtins_hook") self.log.debug("used builtins_hook")
builtins.ev = self builtins.ev = self
def unregister(self, func):
self.log.debug(f"unregister {func}")
s = a = 0
for k, funcs in self.__events.items():
for f in funcs:
if f is func:
s += 1
self.__events[k].remove(func)
for k, funcs in self.__async_events.items():
for f in funcs:
if f is func:
a += 1
self.__async_events[k].remove(func)
self.log.debug(f"unregister in {s+a} events; S:{s}; A:{a};")
def is_event(self, event_name): def is_event(self, event_name):
return (event_name in self.__async_events.keys() or return (event_name in self.__async_events.keys() or
event_name in self.__events.keys() or event_name in self.__events.keys() or
@@ -117,14 +132,12 @@ class EventsSystem:
return return
if async_event or inspect.iscoroutinefunction(event_func): if async_event or inspect.iscoroutinefunction(event_func):
if event_name not in self.__async_events: if event_name not in self.__async_events:
self.__async_events.update({str(event_name): [event_func]}) self.__async_events[event_name] = []
else:
self.__async_events[event_name].append(event_func) self.__async_events[event_name].append(event_func)
self.log.debug("Register ok") self.log.debug("Register ok")
else: else:
if event_name not in self.__events: if event_name not in self.__events:
self.__events.update({str(event_name): [event_func]}) self.__events[event_name] = []
else:
self.__events[event_name].append(event_func) self.__events[event_name].append(event_func)
self.log.debug("Register ok") self.log.debug("Register ok")
+81 -21
View File
@@ -11,6 +11,7 @@ import inspect
import os import os
import subprocess import subprocess
import sys import sys
import time
import types import types
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
@@ -21,6 +22,7 @@ from core import get_logger
class KuiToi: class KuiToi:
_plugins_dir = "" _plugins_dir = ""
_file = ""
def __init__(self, name): def __init__(self, name):
if not name: if not name:
@@ -29,6 +31,8 @@ class KuiToi:
self.__name = name self.__name = name
self.__dir = Path(self._plugins_dir) / self.__name self.__dir = Path(self._plugins_dir) / self.__name
os.makedirs(self.__dir, exist_ok=True) os.makedirs(self.__dir, exist_ok=True)
self.__events_funcs = []
self.register_event = self.register
@property @property
def log(self): def log(self):
@@ -63,8 +67,13 @@ class KuiToi:
def register(self, event_name, event_func): def register(self, event_name, event_func):
self.log.debug(f"Registering event {event_name}") self.log.debug(f"Registering event {event_name}")
self.__events_funcs.append(event_func)
ev.register(event_name, event_func) ev.register(event_name, event_func)
def _unload(self):
for f in self.__events_funcs:
ev.unregister(f)
def call_event(self, event_name, *args, **kwargs): def call_event(self, event_name, *args, **kwargs):
self.log.debug(f"Called event {event_name}") self.log.debug(f"Called event {event_name}")
return ev.call_event(event_name, *args, **kwargs) return ev.call_event(event_name, *args, **kwargs)
@@ -109,16 +118,45 @@ class PluginsLoader:
self.plugins_tasks = [] self.plugins_tasks = []
self.plugins_dir = plugins_dir self.plugins_dir = plugins_dir
self.log = get_logger("PluginsLoader") self.log = get_logger("PluginsLoader")
self.loaded_str = "Plugins: " self.loaded = []
ev.register("_plugins_start", self.start) ev.register("_plugins_start", self.start)
ev.register("_plugins_unload", self.unload) ev.register("_plugins_unload", self.unload)
ev.register("_plugins_get", lambda x: list(self.plugins.keys())) ev.register("_plugins_get", lambda _: "Plugins: " + ", ".join(f"{i[0]}:{'on' if i[1] else 'off'}" for i in self.loaded))
console.add_command("plugins", lambda x: self.loaded_str[:-2]) console.add_command("plugins", self._parse_console, None, "Plugins manipulations", {"plugins": {"reload", "load", "unload", "list"}})
console.add_command("pl", lambda x: self.loaded_str[:-2]) console.add_command("pl", lambda _: ev.call_event("_plugins_get")[0])
sys.path.append(self._pip_dir) sys.path.append(self._pip_dir)
os.makedirs(self._pip_dir, exist_ok=True) os.makedirs(self._pip_dir, exist_ok=True)
console.add_command("install", self._pip_install) console.add_command("install", self._pip_install)
async def _parse_console(self, x):
usage = 'Usage: plugin [reload <name> | load <file.py> | unload <name> | list]'
if not x:
return usage
match x[0]:
case 'reload':
if len(x) == 2:
t1 = time.monotonic()
ok, _, file, _ = await self._unload_by_name(x[1], True)
if ok:
if await self._load_by_file(file):
return f"Plugin reloaded ({time.monotonic() - t1:.1f}sec)"
return "Plugin not found"
return usage
case 'load':
if len(x) == 2:
if await self._load_by_file(x[1]):
return "Plugin loaded"
return usage
case 'unload':
if len(x) == 2:
ok, _, _, _ = await self._unload_by_name(x[1], True)
if ok:
return "Plugin unloaded"
return usage
case 'list':
return ev.call_event("_plugins_get")[0]
return usage
def _pip_install(self, x): def _pip_install(self, x):
self.log.debug(f"_pip_install {x}") self.log.debug(f"_pip_install {x}")
if len(x) > 0: if len(x) > 0:
@@ -131,9 +169,7 @@ class PluginsLoader:
else: else:
return "Invalid syntax" return "Invalid syntax"
async def load(self): async def _load_by_file(self, file):
self.log.debug("Loading plugins...")
for file in os.listdir(self.plugins_dir):
file_path = os.path.join(self.plugins_dir, file) file_path = os.path.join(self.plugins_dir, file)
if os.path.isfile(file_path) and file.endswith(".py"): if os.path.isfile(file_path) and file.endswith(".py"):
try: try:
@@ -141,6 +177,7 @@ class PluginsLoader:
plugin = types.ModuleType(file[:-3]) plugin = types.ModuleType(file[:-3])
plugin.KuiToi = KuiToi plugin.KuiToi = KuiToi
plugin.KuiToi._plugins_dir = self.plugins_dir plugin.KuiToi._plugins_dir = self.plugins_dir
plugin.KuiToi._file = file
plugin.print = print plugin.print = print
plugin.__file__ = file_path plugin.__file__ = file_path
with open(f'{file_path}', 'r', encoding=config.enc) as f: with open(f'{file_path}', 'r', encoding=config.enc) as f:
@@ -202,12 +239,41 @@ class PluginsLoader:
th = Thread(target=plugin.load, name=f"{pl_name}.load()") th = Thread(target=plugin.load, name=f"{pl_name}.load()")
th.start() th.start()
th.join() th.join()
self.loaded_str += f"{pl_name}:ok, " self.loaded.append((pl_name, True))
self.log.debug(f"Plugin loaded: {file}. Settings: {self.plugins[pl_name]}") self.log.debug(f"Plugin loaded: {file}. Settings: {self.plugins[pl_name]}")
return True
except Exception as e: except Exception as e:
self.loaded_str += f"{file}:no, " self.loaded.append((file, False))
self.log.error(i18n.plugins_error_loading.format(file, f"{e}")) self.log.error(i18n.plugins_error_loading.format(file, f"{e}"))
self.log.exception(e) self.log.exception(e)
return False
async def load(self):
self.log.debug("Loading plugins...")
for file in os.listdir(self.plugins_dir):
await self._load_by_file(file)
async def _unload_by_name(self, name, reload=False):
t1 = time.monotonic()
data = self.plugins.get(name)
if not data:
return False, name, None, None
try:
if reload:
data['plugin'].kt._unload()
self.loaded.remove((name, True))
self.plugins.pop(name)
if data['unload']['async']:
self.log.debug(f"Unload async plugin: {name}")
await data['unload']['func']()
else:
self.log.debug(f"Unload sync plugin: {name}")
th = Thread(target=data['unload']['func'], name=f"Thread {name}")
th.start()
th.join()
except Exception as e:
self.log.exception(e)
return True, name, data['plugin'].kt._file, time.monotonic() - t1
async def start(self, _): async def start(self, _):
for pl_name, pl_data in self.plugins.items(): for pl_name, pl_data in self.plugins.items():
@@ -225,15 +291,9 @@ class PluginsLoader:
self.log.exception(e) self.log.exception(e)
async def unload(self, _): async def unload(self, _):
for pl_name, pl_data in self.plugins.items(): t = []
try: for n in self.plugins.keys():
if pl_data['unload']['async']: await asyncio.sleep(0.01)
self.log.debug(f"Unload async plugin: {pl_name}") t.append(self._unload_by_name(n))
await pl_data['unload']['func']() self.log.debug(await asyncio.gather(*t))
else: self.log.debug("Plugins unloaded")
self.log.debug(f"Unload sync plugin: {pl_name}")
th = Thread(target=pl_data['unload']['func'], name=f"Thread {pl_name}")
th.start()
th.join()
except Exception as e:
self.log.exception(e)