From c2b0d5d7be557163d0a3501c456285ddcf8d6f7d Mon Sep 17 00:00:00 2001 From: Stefano Miccoli Date: Sun, 6 Apr 2014 20:55:53 +0200 Subject: [PATCH] still working on error handling pyownet.protocol.Error hierarchy reorganized better code for fetching errcodes from owserver --- pyownet/__init__.py | 2 +- pyownet/protocol.py | 51 ++++++++++++++++++++++++++++++--------------- tests.py | 13 ++++++------ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/pyownet/__init__.py b/pyownet/__init__.py index 77e2d9d..fcc9b17 100644 --- a/pyownet/__init__.py +++ b/pyownet/__init__.py @@ -17,4 +17,4 @@ # along with this program. If not, see . # -__version__ = '0.7.1.dev0' +__version__ = '0.7.1.dev1' diff --git a/pyownet/protocol.py b/pyownet/protocol.py index 535f5fa..3d6dc27 100644 --- a/pyownet/protocol.py +++ b/pyownet/protocol.py @@ -45,7 +45,7 @@ import struct import socket -# see msg_classification from ow_message.h +# see 'enum msg_classification' from ow_message.h MSG_ERROR = 0 MSG_NOP = 1 MSG_READ = 2 @@ -93,6 +93,10 @@ FLG_FORMAT_FIC = 0x05000000 # /1067C6697351FF8D MSK_DEVFORMAT = 0xFF000000 +# useful paths +PTH_ERRCODES = '/settings/return_codes/text.ALL' + + # internal constants # socket timeout (s) @@ -100,6 +104,7 @@ # do not attempt to read messages bigger than this (bytes) _MAX_PAYLOAD = 65536 + def str2bytez(s): "transform string to zero-terminated bytes" if not isinstance(s, basestring): @@ -123,23 +128,33 @@ class Error(Exception): class ConnError(Error, IOError): - """Connection failed""" + """raised if no valid connection can be established with owserver""" pass -class ShortRead(Error): +class ProtocolError(Error): + """raised if no valid server response was received""" pass -class ShortWrite(Error): +class MalformedHeader(ProtocolError): + def __init__(self, msg, header): + self.msg = msg + self.header = header + def __str__(self): + return "{0.msg}: {0.header!s} == {0.header!r}".format(self) + + +class ShortRead(ProtocolError): pass -class ProtocolError(Error): +class ShortWrite(ProtocolError): pass class OwnetError(Error, EnvironmentError): + """raised if owserver returns error code""" pass @@ -292,11 +307,9 @@ def _read_msg(self): if self.verbose: print('<-', repr(header)) if header.version != 0: - raise ProtocolError('got malformed header: %s "%s"' % - (repr(header), header)) + raise MalformedHeader('bad version magic', header) if header.payload > _MAX_PAYLOAD: - raise ProtocolError('huge data, unwilling to read: %s "%s"' % - (repr(header), header)) + raise MalformedHeader('huge data, unwilling to read', header) if header.payload > 0: payload = self.socket.recv(header.payload) if len(payload) < header.payload: @@ -349,22 +362,26 @@ def __init__(self, host='localhost', port=4304, flags=0, # here we have an open connection, close for now conn.shutdown() + # setup self attributes self._sockaddr, self._family = sockaddr, family self._hostport = (host, port) # for display only self.verbose = verbose self.flags = flags | FLG_OWNET - # check if owserver on the line - self.ping() + self.errmess = _errtuple() - #self.errmess = _dummy() + # final sanity checks + # does the remote end speak the ownet protocol? + self.ping() # fetch errcodes array from owserver - errcodes = '/settings/return_codes/text.ALL' - assert self.present(errcodes) - self.errmess = _errtuple( - m for m in bytes2str(self.read(errcodes)).split(',')) + try: + self.errmess = _errtuple( + m for m in bytes2str(self.read(PTH_ERRCODES)).split(',')) + except OwnetError: + # failed, leave the default defined above + pass def __str__(self): return "ownet server at %s" % (self._hostport, ) @@ -455,7 +472,7 @@ def _main(): except ConnError: print("No owserver on localhost") return 1 - print("directory on %s:" % proxy) + print("directory on {0}:".format(proxy)) print("id".center(17), "type".center(7)) for sensor in proxy.dir(slash=False, bus=False): stype = bytes2str(proxy.read(sensor + '/type')) diff --git a/tests.py b/tests.py index 2780e31..328fd95 100644 --- a/tests.py +++ b/tests.py @@ -5,7 +5,10 @@ class TestProtocolModule(unittest.TestCase): @classmethod def setUpClass(cls): - cls.proxy = protocol.OwnetProxy() + try: + cls.proxy = protocol.OwnetProxy() + except protocol.ConnError as exc: + raise RuntimeError('no owserver on localhost, got:%s' % exc) def test_ping(self): self.assertIsNone(self.proxy.ping()) @@ -14,15 +17,13 @@ def test_present(self): self.assertIs(self.proxy.present('/'), True) self.assertIs(self.proxy.present('/nonexistent'), False) - def test_dir(self): + def test_dir_read(self): for i in self.proxy.dir(bus=False): self.assertTrue(self.proxy.present(i)) self.assertTrue(self.proxy.present(i + 'type')) - stype = self.proxy.read(i + 'type') + self.proxy.read(i + 'type') if self.proxy.present(i + 'temperature'): - temp = self.proxy.read(i + 'temperature') - else: - temp = '' + self.proxy.read(i + 'temperature') def test_exceptions(self): self.assertRaises(protocol.OwnetError, self.proxy.dir, '/nonexistent')