Modo Socket comms wrapper¶
ModoSock.py¶
A python wrapper for modo’s telnet comms. Uses ‘raw’ socket mode and implements several convenience methods based on the existing modo lx module methods (eval(), eval1() & evaN(). If you import this module like this:
1 | import modosock as lx
|
then you can write code in your external script which should, with very little modification, transfer directly into a modo script and run. For example, if you started a regular python shell and imported the modosock module as lx yopu should be able to prototype code live from the shell and copy/paste it into a file to run inside modo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | # python
################################################################################
#
# modosock.py
#
# Version: 1.002
#
# Author: Gwynne Reddick
#
# Description: Wrapper for modo socket connection. uses modo's raw mode
#
#
# Usage: Instantiate ModoSock class with hostname and port number. Use one of
# the three eval commands to send command strings to modo. The eval
# commands operate like their lx.eval counterparts
#
# Last Update 19:02 06/06/10
#
################################################################################
import socket
# defines the end of a transmit from modo, it's actually the prompt character
# sent after the last result
_END = '> \0'
# status codes
_ERROR = -1
_OK = 1
_INFO = 2
class ModoSockError(Exception):
pass
class ModoError(ModoSockError):
"""Raised when an error message is received from modo
Attributes:
message - the error message sent from modo
command - the command that was executed
"""
def __init__(self, command, value):
self.value = value
self.command = command
def __str__(self):
return '%s\ncommand string: %s' % (self.value, self.command)
def get_error(self):
return '%s\ncommand string: %s' % (self.value, self.command)
class UnrecognisedLineError(ModoSockError):
"""Raised when an incoming line is found that doesn't start with one of the
known line start characters. This probably means that the current line is a
continuation of the previous one.
Attributes:
command - command that was sent to modo
prevline - text of line that was received before the error line
currline - text of line that threw the error
"""
def __init__(self, command, prevline, currline):
self.command = command
self.prevline = prevline
self.currline = currline
def __str__(self):
return 'command: %s\nresult: %s\n%s' % (self.command, self.prevline, self.currline)
def get_error(self):
return 'command: %s\nresult: %s\n%s' % (self.command, self.prevline, self.currline)
class ModoSock(object):
"""Raw socket communication class.
Wraps a socket connection for cummunicating with modo in raw mode. Implements
three methods that work/behave like their lx module counterparts.
"""
def __init__(self, host, port):
try:
self._con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._con.connect((host, port))
except:
raise
self.status = _OK
self.message = ''
self._con.recv(1024) # eat the first prompt character
def close(self):
"""Close the connection with modo.
Be sure to call this at the end of any script
"""
self._con.close()
def eval(self, command):
"""Send a command to modo.
Unlike the regular lx.eval command in modo, this implementation does not
return a value. It should therefore be used for executing in 'command'
mode, ie for executing commands in modo that do not return a value - so don't
use for queries!!!
"""
result = self._get_result(command)
def eval1(self, command):
"""Send a command to modo. Behaves like lx.eval1
Return value is always either a singleton or None. If modo returns more
than one result only the first will be returned by this function
"""
result = self._get_result(command)
if self.status == _OK:
return result[0]
def evalN(self, command):
"""Send a command to modo. Behaves like lx.evalN
Return value is always either a list or None.
"""
result = self._get_result(command)
if self.status == _OK:
return result
def _get_result(self, command):
result = []
# send command
self._con.sendall('%s\0' % command)
alldata = ''
# collect data
while 1:
data = self._con.recv(1024)
if not data: break
if _END in data:
alldata += data[:data.find(_END)]
break
alldata += data
# process data
alldata = alldata.split('\0')
alldata.remove('') # remove trailing blank line from alldata
for item in alldata:
if item.startswith('- error'):
# modo has returned an error, set self.status to error and
# self.message to the result value so they can be retrieved by
# by calling scripts and then raise an error
self.status = _ERROR
self.message = item[2:]
raise ModoError(command, item[2:])
elif item[0] in ['#','!','@']:
self.status = _INFO
self.message = item
break
elif item.startswith('+ ok'):
self.status = _OK
elif item.startswith(':'):
result.append(item[2:])
else:
raise UnrecognisedLineError(command, alldata[alldata.index(item)-1], item)
return result
|