Source code for pychron.tx.protocols.service

# ===============================================================================
# Copyright 2015 Jake Ross
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================

# ============= enthought library imports =======================
# ============= standard library imports ========================
from __future__ import absolute_import
from __future__ import print_function
import re
import traceback
import json

from twisted.internet import defer
from twisted.internet.protocol import Protocol
from twisted.protocols.basic import LineReceiver

from pychron.tx.errors import InvalidArgumentsErrorCode
from pychron.tx.exceptions import ServiceNameError, ResponseError


def default_err(failure):
    print(failure.getTraceback())
    failure.trap(BaseException)
    return failure


def service_err(failure):
    failure.trap(ServiceNameError)
    return failure


def response_err(failure):
    failure.trap(ResponseError)
    return failure


def nargs_err(failure):
    failure.trap(ValueError)
    return InvalidArgumentsErrorCode("Foo", str(failure.value))


# logger = Logger()

regex = re.compile(r"^(?P<command>\w+) {0,1}(?P<args>.*)")


class MockLogger(object):
    def __getattr__(self, item):
        def mockfunc(*args, **kw):
            pass

        return mockfunc


[docs]class ServiceProtocol(LineReceiver): def __init__(self, logger=None, *args, **kw): # super(ServiceProtocol, self).__init__(*args, **kw) self._services = {} self._cmd_delim = " " self._arg_delim = "," if logger is None: logger = MockLogger() self.debug = logger.debug self.warning = logger.warn self.info = logger.info self.error = logger.error self.critical = logger.critical def dataReceived(self, data): self.debug("Received n={n}: {data!r}", n=len(data), data=data) data = data.decode("utf-8") data = data.strip() args = self._get_service(data) if args: service, data = args self._get_response(service, data) def register_service(self, service_name, success, err=None): """ """ if err is None: err = default_err d = defer.Deferred() if not isinstance(success, (list, tuple)): success = (success,) for si in success: d.addCallback(si) d.addCallback(self._prepare_response) d.addCallback(self._send_response) d.addErrback(nargs_err) d.addErrback(service_err) d.addErrback(err) self._services[service_name] = d def _register_services(self, services): for name, cb in services: if isinstance(cb, str): cb = getattr(self, cb) self.register_service(name, cb) def _prepare_response(self, data): if isinstance(data, bool) and data: return "OK" elif data is None: return "No Response" else: return data def _send_response(self, resp): resp = str(resp) self.debug("Response {data!r}", data=resp) self.transport.write(resp.encode("utf-8")) self.transport.loseConnection() def _get_service(self, data): m = regex.match(data) if m: name = m.group("command") jd = data else: jd = json.loads(data) name = jd["command"] try: service = self._services[name] return service, jd except KeyError as e: traceback.print_exc() raise ServiceNameError(name, data) def _prepare_data(self, data): if isinstance(data, dict): cdata = data else: delim = self._cmd_delim data = delim.join(data.split(delim)[1:]) data = data.split(self._arg_delim) if len(data) == 1: data = data[0] else: data = tuple(data) cdata = data self.debug("Data {cdata!r}", cdata=cdata) return cdata def _get_response(self, service, data): cdata = self._prepare_data(data) service.callback(cdata)
# ============= EOF ============================================= # def sleep(secs): # d = defer.Deferred() # reactor.callLater(secs, d.callback, None) # return d