10 Commits
v0.3 ... v0.6

Author SHA1 Message Date
François Dausseur
5a60e47c12 rev 0.6 2025-10-28 11:48:17 +01:00
François Dausseur
355915e9c1 Merge branch 'main' of https://git.beafrancois.fr/Foue-opensource/pyappengine 2025-10-28 11:37:14 +01:00
François Dausseur
0da36439a6 management of help functions improved. 2025-10-28 11:37:10 +01:00
François Dausseur
3d402db2c2 free not limited to threaded modules. modified the modules dependencies management. 2025-09-20 16:33:21 +02:00
François Dausseur
de04b2b3b9 robustness. 2025-04-30 17:39:35 +02:00
François Dausseur
fffba77497 Merge branch 'main' of https://git.beafrancois.fr/Foue-opensource/pyappengine 2025-04-17 14:40:38 +02:00
François Dausseur
28057dddd6 Removed systemd dependency 2025-04-17 14:40:34 +02:00
François Dausseur
d46e3b9859 release 0.4 2025-02-25 12:41:28 +01:00
François Dausseur
c67c5e3e28 allow to stop the application from a module 2025-02-25 12:39:34 +01:00
François Dausseur
a45d975617 new release 2025-02-25 11:48:24 +01:00
3 changed files with 62 additions and 22 deletions

View File

@@ -1 +1 @@
0.2
0.6

View File

@@ -15,9 +15,7 @@ classifiers = [
"License :: OSI Approved :: CeCILL-C",
"Operating System :: OS Independent",
]
dependencies = [
"systemd-python",
]
dependencies = [ ]
dynamic = ["version"]
[project.urls]

View File

@@ -5,7 +5,6 @@ import sys
import traceback
from configparser import ConfigParser
import logging
from systemd import journal
import inspect
from enum import Enum, auto
import signal
@@ -13,7 +12,7 @@ from importlib import import_module
from threading import Thread, Lock
import inspect
from threading import Thread
from threading import Thread, Event
def is_number(s):
@@ -49,6 +48,8 @@ class AppEngineException(Exception):
def __init__(self, error: AEErrs, mesg=None) -> None:
if mesg is None:
self.mesg = str(error)
else:
self.mesg = mesg
super().__init__(self.mesg)
self.value = error.value
@@ -63,6 +64,7 @@ class Commands(Thread):
self.cmods = {}
self.config = config
self.log = log
self.stop_all_event = None
self.stopped = False
self.nickname = None
if not (self.config is None):
@@ -105,8 +107,8 @@ class Commands(Thread):
self.stopped = True
def free(self):
""" Virtual method used to clean resources for threaded Commands
when they are stopped.
""" Virtual method used to clean resources for all Commands
when the application is exited.
"""
pass
@@ -174,7 +176,28 @@ class Commands(Thread):
if module == "":
if method == 'help':
return self.list_modules()
# No argument was given to the help function
if len(args) == 0 and len(kwargs) == 0:
msg = "Type 'help <module>' to have the list of module's functions.\n"
msg += "Type 'help <module>.<function>' to documentation of a specific function.\n"
success, ret = self.list_modules()
return success, msg + ret
else:
# 1 argument has been provided
if len(args) > 0:
arg = args[0]
else:
arg = list(kwargs.keys())[0]
spl = arg.split(".", 1)
# help <module>
if len(spl) == 1:
module = arg
# help <module>.<method>
else:
module = spl[0]
args = [spl[1]]
kwargs = {}
else:
module = self.defmod
@@ -187,6 +210,10 @@ class Commands(Thread):
return self.cmods[module]._execute_command(method, *args, **kwargs)
def stop_all(self):
if self.stop_all_event is not None:
self.stop_all_event.set()
def cmd_help(self, *args, **kwargs):
"""Help of module commands.
Params:
@@ -218,7 +245,7 @@ class Commands(Thread):
class CommandsLoader:
def __init__(self, config: ConfigParser, log: logging.Handler, modpath: str):
def __init__(self, stop_event: Event, config: ConfigParser, log: logging.Handler, modpath: str):
self.config = config
self.modpath = modpath
sys.path.append(os.path.join(modpath))
@@ -232,6 +259,7 @@ class CommandsLoader:
self.prefcmds = prefcmds
self.log = log
self.lock = Lock()
self.stop_event = stop_event
self._load_commands()
self._set_cmods()
@@ -273,9 +301,14 @@ class CommandsLoader:
obj = None
for n, c in members:
if issubclass(c, Commands) and (n != "Commands"):
obj = c(conf, self.log)
try:
obj = c(conf, self.log)
except:
self.log.error(f"The object '{c.__name__}' could not be instantiated.")
continue
obj.log = self.log
obj.lock = self.lock
obj.stop_all_event = self.stop_event
break
return obj
@@ -287,9 +320,15 @@ class CommandsLoader:
def _load_dependencies(self):
for k, v in self.cmods.items():
if hasattr(v, "dependencies"):
for p in v.dependencies:
if p in self.cmods.keys():
setattr(v, p, self.cmods[p])
deps = v.dependencies
# dependencies can be a list or dictionary
if isinstance(v.dependencies, list):
deps = {}
for d in v.dependencies:
deps[d] = d
for p, pv in deps.items():
if pv in self.cmods.keys():
setattr(v, p, self.cmods[pv])
else:
self.log.error(
'Dependency "{}" of module "{}" could not be satisfied'.format(
@@ -315,8 +354,7 @@ class CommandsLoader:
def free(self):
for k, v in self.cmods.items():
if v.threaded:
v.free()
v.free()
class AppEngine:
@@ -338,6 +376,10 @@ class AppEngine:
self.log.setLevel(logging.DEBUG)
else:
self.log.setLevel(logging.WARNING)
# Mechanism to stop the application
self.stop_event = Event()
self.stop_thread = Thread(target=self.wait_stop, args=(self.stop_event,))
self.stop_thread.start()
def signal_handler(self, sig, frame):
print("\nExiting.")
@@ -364,17 +406,17 @@ class AppEngine:
if is_writeable:
self.log.addHandler(logging.FileHandler(fname))
else:
self.log.addHandler(journal.JournalHandler())
self.log.error('No write permissions: "{}"'.format(fname))
else:
self.log.addHandler(journal.JournalHandler())
def exec(self, modpath: str = ""):
self.cl = CommandsLoader(self.conf, self.log, modpath)
self.cl = CommandsLoader(self.stop_event, self.conf, self.log, modpath)
self.cl.start()
self.cl.join()
self.cl.free()
def wait_stop(self, evnt):
evnt.wait()
self.cl.stop()
def stop(self):
self.cl.stop()
self.stop_event.set()