class _ProxyModule(Module): ''' A proxy to create dependencies on configurable module ''' service = True def __init__(self, server): ''' Constructor ''' Module.__init__(self, server) self.proxyhandler = EventHandler(self.scheduler) def load(self, container): self._targetname = self._proxytarget._instance.getServiceName() self.proxyhandler.registerHandler( ModuleAPICall.createMatcher(None, self.getServiceName()), self._proxyhandler) for m in Module.load(self, container): yield m def unload(self, container, force=False): self.proxyhandler.close() for m in Module.unload(self, container, force=force): yield m def _proxyhandler(self, event, scheduler): event.canignore = True scheduler.emergesend( ModuleAPICall(event.handle, self._targetname, event.name, params=event.params))
class _ProxyModule(Module): ''' A proxy to create dependencies on configurable module ''' service = True def __init__(self, server): ''' Constructor ''' Module.__init__(self, server) self.proxyhandler = EventHandler(self.scheduler) def load(self, container): self._targetname = self._proxytarget._instance.getServiceName() self.proxyhandler.registerHandler(ModuleAPICall.createMatcher(None, self.getServiceName()), self._proxyhandler) for m in Module.load(self, container): yield m def unload(self, container, force=False): self.proxyhandler.close() for m in Module.unload(self, container, force=force): yield m def _proxyhandler(self, event, scheduler): event.canignore = True scheduler.emergesend(ModuleAPICall(event.handle, self._targetname, event.name, params = event.params))
class ModuleAPIHandler(RoutineContainer): """ API Handler for modules """ def __init__(self, moduleinst, apidefs = None, allowdiscover = True, rejectunknown = True): RoutineContainer.__init__(self, scheduler=moduleinst.scheduler, daemon=False) self.handler = EventHandler(self.scheduler) self.servicename = moduleinst.getServiceName() self.apidefs = apidefs self.registeredAPIs = {} self.discoverinfo = {} self.allowdiscover = allowdiscover self.rejectunknown = rejectunknown @staticmethod def createReply(handle, result): return ModuleAPIReply(handle, result=result) @staticmethod def createExceptionReply(handle, exception): return ModuleAPIReply(handle, exception = exception) def _createHandler(self, name, handler, container = None, discoverinfo = None, criteria = None): extra_params = {} if criteria: extra_params['_ismatch'] = lambda e: not e.canignore and criteria(**e.params) if name is None: matcher = ModuleAPICall.createMatcher(target = self.servicename, **extra_params) elif name.startswith('public/'): matcher = ModuleAPICall.createMatcher(target = 'public', name = name[len('public/'):], **extra_params) else: matcher = ModuleAPICall.createMatcher(target = self.servicename, name = name, **extra_params) if container is not None: async def wrapper(event): try: r = await handler(event.name, event.params) await container.wait_for_send(self.createReply(event.handle, r)) except Exception as val: await container.wait_for_send(self.createExceptionReply(event.handle, val)) def event_handler(event, scheduler): event.canignore = True container.subroutine(wrapper(event), False) else: async def wrapper(event): try: result = handler(event.name, event.params) await self.wait_for_send(self.createReply(event.handle, result)) except Exception as val: await self.wait_for_send(self.createExceptionReply(event.handle, val)) def event_handler(event, scheduler): event.canignore = True self.subroutine(wrapper(event), False) return (matcher, event_handler) def registerAPIs(self, apidefs): ''' API definition is in format: `(name, handler, container, discoverinfo)` if the handler is a generator, container should be specified handler should accept two arguments:: def handler(name, params): ... `name` is the method name, `params` is a dictionary contains the parameters. the handler can either return the result directly, or be a generator (async-api), and write the result to container.retvalue on exit. e.g:: ('method1', self.method1), # method1 directly returns the result ('method2', self.method2, self) # method2 is an async-api Use api() to automatically generate API definitions. ''' handlers = [self._createHandler(*apidef) for apidef in apidefs] self.handler.registerAllHandlers(handlers) self.discoverinfo.update((apidef[0], apidef[3] if len(apidef) > 3 else {'description':cleandoc(apidef[1].__doc__)}) for apidef in apidefs) def registerAPI(self, name, handler, container = None, discoverinfo = None, criteria = None): """ Append new API to this handler """ self.handler.registerHandler(*self._createHandler(name, handler, container, criteria)) if discoverinfo is None: self.discoverinfo[name] = {'description': cleandoc(handler.__doc__)} else: self.discoverinfo[name] = discoverinfo def unregisterAPI(self, name): """ Remove an API from this handler """ if name.startswith('public/'): target = 'public' name = name[len('public/'):] else: target = self.servicename name = name removes = [m for m in self.handler.handlers.keys() if m.target == target and m.name == name] for m in removes: self.handler.unregisterHandler(m) def discover(self, details = False): 'Discover API definitions. Set details=true to show details' if details and not (isinstance(details, str) and details.lower() == 'false'): return copy.deepcopy(self.discoverinfo) else: return dict((k,v.get('description', '')) for k,v in self.discoverinfo.items()) def reject(self, name, args): raise APIRejectedException('%r is not defined in module %r' % (name, self.servicename)) def start(self, asyncStart=False): if self.apidefs: self.registerAPIs(self.apidefs) if self.allowdiscover: self.registerAPI(*api(self.discover)) if self.rejectunknown: self.handler.registerHandler(*self._createHandler(None, self.reject, None)) def close(self): self.handler.close()
class ModuleAPIHandler(RoutineContainer): """ API Handler for modules """ def __init__(self, moduleinst, apidefs = None, allowdiscover = True, rejectunknown = True): RoutineContainer.__init__(self, scheduler=moduleinst.scheduler, daemon=False) self.handler = EventHandler(self.scheduler) self.servicename = moduleinst.getServiceName() self.apidefs = apidefs self.registeredAPIs = {} self.discoverinfo = {} self.allowdiscover = allowdiscover self.rejectunknown = True @staticmethod def createReply(handle, result): return ModuleAPIReply(handle, result=result) @staticmethod def createExceptionReply(handle, exception): return ModuleAPIReply(handle, exception = exception) def _createHandler(self, name, handler, container = None, discoverinfo = None, criteria = None): extra_params = {} if criteria: extra_params['_ismatch'] = lambda e: not e.canignore and criteria(**e.params) if name is None: matcher = ModuleAPICall.createMatcher(target = self.servicename, **extra_params) matcher = ModuleAPICall.createMatcher(target = self.servicename, **extra_params) elif name.startswith('public/'): matcher = ModuleAPICall.createMatcher(target = 'public', name = name[len('public/'):], **extra_params) else: matcher = ModuleAPICall.createMatcher(target = self.servicename, name = name, **extra_params) if container is not None: def wrapper(event): try: for m in handler(event.name, event.params): yield m for m in container.waitForSend(self.createReply(event.handle, container.retvalue)): yield m except Exception as val: for m in container.waitForSend(self.createExceptionReply(event.handle, val)): yield m def event_handler(event, scheduler): event.canignore = True container.subroutine(wrapper(event), False) else: def wrapper(event): try: result = handler(event.name, event.params) for m in self.waitForSend(self.createReply(event.handle, result)): yield m except Exception as val: for m in self.waitForSend(self.createExceptionReply(event.handle, val)): yield m def event_handler(event, scheduler): event.canignore = True self.subroutine(wrapper(event), False) return (matcher, event_handler) def registerAPIs(self, apidefs): ''' API definition is in format: `(name, handler, container, discoverinfo)` if the handler is a generator, container should be specified handler should accept two arguments:: def handler(name, params): ... `name` is the method name, `params` is a dictionary contains the parameters. the handler can either return the result directly, or be a generator (async-api), and write the result to container.retvalue on exit. e.g:: ('method1', self.method1), # method1 directly returns the result ('method2', self.method2, self) # method2 is an async-api Use api() to automatically generate API definitions. ''' handlers = [self._createHandler(*apidef) for apidef in apidefs] self.handler.registerAllHandlers(handlers) self.discoverinfo.update((apidef[0], apidef[3] if len(apidef) > 3 else {'description':cleandoc(apidef[1].__doc__)}) for apidef in apidefs) def registerAPI(self, name, handler, container = None, discoverinfo = None, criteria = None): """ Append new API to this handler """ self.handler.registerHandler(*self._createHandler(name, handler, container, criteria)) if discoverinfo is None: self.discoverinfo[name] = {'description': cleandoc(handler.__doc__)} else: self.discoverinfo[name] = discoverinfo def unregisterAPI(self, name): """ Remove an API from this handler """ if name.startswith('public/'): target = 'public' name = name[len('public/'):] else: target = self.servicename name = name removes = [m for m in self.handler.handlers.keys() if m.target == target and m.name == name] for m in removes: self.handler.unregisterHandler(m) def discover(self, details = False): 'Discover API definitions. Set details=true to show details' if details and not (isinstance(details, str) and details.lower() == 'false'): return copy.deepcopy(self.discoverinfo) else: return dict((k,v.get('description', '')) for k,v in self.discoverinfo.items()) def reject(self, name, args): raise ValueError('%r is not defined in module %r' % (name, self.servicename)) def start(self, asyncStart=False): if self.apidefs: self.registerAPIs(self.apidefs) if self.allowdiscover: self.registerAPI(*api(self.discover)) if self.rejectunknown: self.handler.registerHandler(*self._createHandler(None, self.reject, None)) def close(self): self.handler.close()