Add Process and Signals support

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2019-10-29 11:25:03 +01:00
commit c96ffe52c0
6 changed files with 139 additions and 0 deletions

View 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

View 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)

View file

@ -0,0 +1,4 @@
TOPDIR = ../../../..
include $(TOPDIR)/make/proj.mk
include $(JWBDIR)/make/py-mod.mk

View 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()

View 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

View file

@ -0,0 +1,3 @@
# >> -------------------------- generated by python-tools.sh >>
from jwutils.asyncio.Process import Process
# << -------------------------- generated by python-tools.sh <<