/usr/share/pyshared/hl7/client.py is in python-hl7 0.2.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | from optparse import OptionParser
import os.path
import socket
import sys
SB = '\x0b' #<SB>, vertical tab
EB = '\x1c' #<EB>, file separator
CR = '\x0d' #<CR>, \r
FF = '\x0c' # <FF>, new page form feed
RECV_BUFFER = 4096
class MLLPException(Exception): pass
class MLLPClient(object):
"""
A basic, blocking, HL7 MLLP client based upon :py:mod:`socket`.
MLLPClient implements two methods for sending data to the server.
* :py:meth:`MLLPClient.send` for raw data that already is wrapped in the
appropriate MLLP container (e.g. *<SB>message<EB><CR>*).
* :py:meth:`MLLPClient.send_message` will wrap the message in the MLLP
container
Can be used by the ``with`` statement to ensure :py:meth:`MLLPClient.close`
is called::
with MLLPClient(host, port) as client:
client.send_message('MSH|...')
"""
def __init__(self, host, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((host, port))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, trackeback):
self.close()
def close(self):
"""Release the socket connection"""
self.socket.close()
def send_message(self, message):
"""Wraps a str, unicode, or :py:cls:`hl7.Message` in a MLLP container
and send the message to the server
"""
# wrap in MLLP message container
data = SB + unicode(message) + EB + CR
# TODO consider encoding (e.g. UTF-8)
return self.send(data)
def send(self, data):
"""Low-level, direct access to the socket.send (data must be already
wrapped in an MLLP container). Blocks until the server returns.
"""
# upload the data
self.socket.send(data)
# wait for the ACK/NACK
return self.socket.recv(RECV_BUFFER)
# wrappers to make testing easier
def stdout(content):
sys.stdout.write(content + '\n')
def stdin():
return sys.stdin
def stderr():
return sys.stderr
def read_stream(stream):
"""Buffer the stream and yield individual, stripped messages"""
_buffer = ''
while True:
data = stream.read(RECV_BUFFER)
if data == '':
break
# usually should be broken up by EB, but I have seen FF separating
# messages
messages = (_buffer + data).split(EB if FF not in data else FF)
# whatever is in the last chunk is an uncompleted message, so put back
# into the buffer
_buffer = messages.pop(-1)
for m in messages:
yield m.strip(SB+CR)
if len(_buffer.strip()) > 0:
raise MLLPException('buffer not terminated: %s' % _buffer)
def read_loose(stream):
"""Turn a single HL7-like blob of text into a real HL7 message"""
# load all the data (should only be 1 message)
data = stream.read()
# Windows new lines to segment separators
data = data.replace('\r\n', '\r')
# take out all the the typical MLLP separators and trailing
# whitespace
data = data.strip(EB+FF+SB+CR+'\n ')
yield data
def mllp_send():
"""Command line tool to send messages to an MLLP server"""
# set up the command line options
script_name = os.path.basename(sys.argv[0])
parser = OptionParser(usage=script_name + ' [options] <server>')
parser.add_option('-p', '--port',
action='store', type='int', dest='port', default=6661,
help='port to connect to')
parser.add_option('-f', '--file', dest='filename',
help='read from FILE instead of stdin', metavar='FILE')
parser.add_option('-q', '--quiet',
action='store_true', dest='verbose', default=True,
help='do not print status messages to stdout')
parser.add_option('--loose',
action='store_true', dest='loose', default=False,
help='allow file to be a HL7-like object (\\r\\n ' \
+ 'instead of \\r). Can ONLY send 1 message. Requires ' \
+ '--file option (no stdin)')
(options, args) = parser.parse_args()
if len(args) == 1:
host = args[0]
else:
# server not present
parser.print_usage()
stderr().write('server required\n')
return
if options.filename is not None:
stream = open(options.filename, 'rb') #FIXME with_statement
else:
if options.loose:
stderr().write('--loose requires --file with a single message\n')
return
stream = stdin()
with MLLPClient(host, options.port) as client:
message_stream = read_stream(stream) \
if not options.loose \
else read_loose(stream)
for message in message_stream:
result = client.send_message(message)
if options.verbose:
stdout(result)
if __name__ == '__main__':
mllp_send()
|