mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-python
synced 2026-01-15 01:52:56 +01:00
Add Process and Signals support
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
6123a68195
commit
c96ffe52c0
6 changed files with 139 additions and 0 deletions
68
tools/python/jwutils/Process.py
Normal file
68
tools/python/jwutils/Process.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from enum import Enum, Flag, auto
|
||||
|
||||
def _sigchld_handler(signum, process):
|
||||
if not signum == signal.SIGCHLD:
|
||||
return
|
||||
Process.propagate_signal(signum)
|
||||
|
||||
class Process(ABC): # export
|
||||
|
||||
__processes = []
|
||||
|
||||
class State(Enum):
|
||||
Running = auto()
|
||||
Shutdown = auto()
|
||||
Done = auto()
|
||||
|
||||
class Flags(Flag):
|
||||
FailOnExitWithoutShutdown = auto()
|
||||
|
||||
def __init__(self):
|
||||
self.__state = Running
|
||||
self.__flags = Flags.FailOnExitWithoutShutdown
|
||||
if len(self.__processes) == 0:
|
||||
self._signals().add_handler(signals.SIGCHLD, _sigchld_handler)
|
||||
self.__processes.add(self)
|
||||
|
||||
@classmethod
|
||||
def propagate_signal(cls, signum):
|
||||
for p in cls.__processes:
|
||||
p.__signal(signum)
|
||||
|
||||
def signal(self, signum):
|
||||
if signum == signals.SIGCHLD:
|
||||
self.exited()
|
||||
|
||||
@abstractmethod
|
||||
def _pid(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def signals(cls):
|
||||
pass
|
||||
|
||||
# to be reimplemented
|
||||
def _request_shutdown(self):
|
||||
pass
|
||||
|
||||
# to be reimplemented
|
||||
def name(self):
|
||||
return str(self._pid())
|
||||
|
||||
def request_shutdown(self):
|
||||
if not self.__state == Shutdown:
|
||||
self.__state = Shutdown
|
||||
self._request_shutdown()
|
||||
|
||||
def exited(self):
|
||||
if self.__state == Process.State.Running:
|
||||
slog(ERR, 'process "{}" exited unexpectedly'.format(process.name()))
|
||||
if __flags & Process.Flags.FailOnExitWithoutShutdown:
|
||||
slog(ERR, 'exiting')
|
||||
exit(1)
|
||||
self.__state = Process.State.Done
|
||||
self.__processes.erase(self)
|
||||
if len(self.__processes) == 0:
|
||||
self._signals().remove_handler(signals.SIGCHLD) # FIXME: broken logic
|
||||
34
tools/python/jwutils/Signals.py
Normal file
34
tools/python/jwutils/Signals.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
_handled_signals = {}
|
||||
|
||||
def _signal_handler(signal, frame):
|
||||
if not signal in _handled_signals.keys():
|
||||
return
|
||||
for h in _handled_signals[signal]:
|
||||
h.func(signal, *h.args)
|
||||
|
||||
class Signals:
|
||||
|
||||
class Handler:
|
||||
def __init__(self, func, args):
|
||||
self.func = func
|
||||
self.args = args
|
||||
|
||||
def __init(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def _add_handler(self, signal, handler):
|
||||
raise Exception("_add_handler() is not reimplemented")
|
||||
|
||||
@classmethod
|
||||
def add_handler(cls, signals, handler, *args):
|
||||
for signal in signals:
|
||||
h = Signals.Handler(handler, args)
|
||||
if not signal in _handled_signals.keys():
|
||||
_handled_signals[signal] = [h]
|
||||
cls._add_signal_handler(signal, _signal_handler)
|
||||
else:
|
||||
_handled_signals[signal].add(h)
|
||||
4
tools/python/jwutils/asyncio/Makefile
Normal file
4
tools/python/jwutils/asyncio/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
TOPDIR = ../../../..
|
||||
|
||||
include $(TOPDIR)/make/proj.mk
|
||||
include $(JWBDIR)/make/py-mod.mk
|
||||
20
tools/python/jwutils/asyncio/Process.py
Normal file
20
tools/python/jwutils/asyncio/Process.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from abc import abstractmethod
|
||||
|
||||
from ..Process import Process as ProcessBase
|
||||
from .Signals import Signals
|
||||
|
||||
class Process(ProcessBase): # export
|
||||
|
||||
__signals = Signals()
|
||||
|
||||
def __init__(self, aio):
|
||||
super().__init()
|
||||
self.aio = aio
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def signals(cls):
|
||||
return cls.__signals
|
||||
|
||||
def wait(self):
|
||||
return self.aio.wait()
|
||||
10
tools/python/jwutils/asyncio/Signals.py
Normal file
10
tools/python/jwutils/asyncio/Signals.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import asyncio
|
||||
from ..Signals import Signals as SignalsBase
|
||||
|
||||
class Signals(SignalsBase):
|
||||
|
||||
# reimplemented from Signals
|
||||
@classmethod
|
||||
def _add_handler(cls, signal, handler):
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.add_signal_handler(signal, handler, None) # None = *args
|
||||
3
tools/python/jwutils/asyncio/__init__.py
Normal file
3
tools/python/jwutils/asyncio/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# >> -------------------------- generated by python-tools.sh >>
|
||||
from jwutils.asyncio.Process import Process
|
||||
# << -------------------------- generated by python-tools.sh <<
|
||||
Loading…
Add table
Add a link
Reference in a new issue