From d3fc01689f59786252c2a826ef2d89d30c77dfa5 Mon Sep 17 00:00:00 2001 From: l_samenv Date: Thu, 8 Apr 2021 10:21:44 +0200 Subject: [PATCH] improve more handling of errors when starting server + add shutdown method to server (and dispatcher) --- secop/protocol/dispatcher.py | 1 + secop/server.py | 48 ++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/secop/protocol/dispatcher.py b/secop/protocol/dispatcher.py index e9296e2..2836342 100644 --- a/secop/protocol/dispatcher.py +++ b/secop/protocol/dispatcher.py @@ -82,6 +82,7 @@ class Dispatcher: self._subscriptions = {} self._lock = threading.RLock() self.restart = srv.restart + self.shutdown = srv.shutdown def broadcast_event(self, msg, reallyall=False): """broadcasts a msg to all active connections diff --git a/secop/server.py b/secop/server.py index 50a674f..fbb4ae2 100644 --- a/secop/server.py +++ b/secop/server.py @@ -29,6 +29,7 @@ import os import sys import threading import time +import traceback from collections import OrderedDict from secop.errors import ConfigError, SECoPError @@ -219,6 +220,10 @@ class Server: self._restart = True self.interface.shutdown() + def shutdown(self): + self._restart = False + self.interface.shutdown() + def _processCfg(self): errors = [] opts = dict(self.node_cfg) @@ -227,31 +232,39 @@ class Server: if opts: errors.append(self.unknown_options(cls, opts)) self.modules = OrderedDict() - badclass = None + failure_traceback = None # traceback for the last error failed = set() # python modules failed to load self.lastError = None for modname, options in self.module_cfg.items(): opts = dict(options) + pymodule = None try: classname = opts.pop('class') pymodule = classname.rpartition('.')[0] if pymodule in failed: continue cls = get_class(classname) - modobj = cls(modname, self.log.getChild(modname), opts, self) - # all used args should be popped from opts! - if opts: - errors.append(self.unknown_options(cls, opts)) - self.modules[modname] = modobj - except ConfigError as e: - errors.append(str(e)) except Exception as e: - if str(e) == 'no such class': - errors.append('%s not found' % classname) + if pymodule is None: + if str(e) == 'no such class': + errors.append('%s not found' % classname) + else: + raise + errors.append(repr(e)) else: failed.add(pymodule) - badclass = classname - errors.append('error importing %s' % pymodule) + failure_traceback = traceback.format_exc() + errors.append('error importing %s' % classname) + else: + try: + modobj = cls(modname, self.log.getChild(modname), opts, self) + # all used args should be popped from opts! + if opts: + errors.append(self.unknown_options(cls, opts)) + self.modules[modname] = modobj + except Exception as e: + failure_traceback = traceback.format_exc() + errors.append('error creating %s: %r' % (modname, e)) poll_table = dict() # all objs created, now start them up and interconnect @@ -276,7 +289,11 @@ class Server: # call init on each module after registering all for modname, modobj in self.modules.items(): - modobj.initModule() + try: + modobj.initModule() + except Exception as e: + failure_traceback = traceback.format_exc() + errors.append('error initializing %s: %r' % (modname, e)) if errors: for errtxt in errors: @@ -285,9 +302,8 @@ class Server: # print a list of config errors to stderr sys.stderr.write('\n'.join(errors)) sys.stderr.write('\n') - if badclass: - # force stack trace for import of last erroneous module - get_class(badclass) + if failure_traceback: + sys.stderr.write(failure_traceback) sys.exit(1) if self._testonly: