From 80d3274238bdbde6d9eeab744be08cdd2219ec47 Mon Sep 17 00:00:00 2001 From: Burak Arslan Date: Wed, 14 Sep 2011 01:53:30 +0300 Subject: [PATCH] fix binary support and examples. --- examples/binary_http.py | 83 +++++++++++++++++++++++++ examples/{binary.py => binary_soap.py} | 18 +++--- src/rpclib/model/binary.py | 41 +++++++++--- src/rpclib/protocol/xml/_base.py | 5 ++ src/rpclib/protocol/xml/model/binary.py | 17 ++--- 5 files changed, 137 insertions(+), 27 deletions(-) create mode 100755 examples/binary_http.py rename examples/{binary.py => binary_soap.py} (87%) diff --git a/examples/binary_http.py b/examples/binary_http.py new file mode 100755 index 000000000..a11c12477 --- /dev/null +++ b/examples/binary_http.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# encoding: utf8 +# +# rpclib - Copyright (C) Rpclib contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# + +import os +import logging + +from tempfile import mkstemp + +from rpclib.application import Application +from rpclib.decorator import srpc +from rpclib.interface.wsdl import Wsdl11 +from rpclib.model.binary import Attachment +from rpclib.model.fault import Fault +from rpclib.model.primitive import String +from rpclib.protocol.http import HttpRpc +from rpclib.service import ServiceBase +from rpclib.server.wsgi import WsgiApplication + +class DocumentArchiver(ServiceBase): + @srpc(Attachment, _returns=String) + def put(document): + '''This method accepts an Attachment object, and returns + the filename of the archived file + ''' + fd, fname = mkstemp() + os.close(fd) + + document.file_name = fname + document.save_to_file() + + return fname + + @srpc(String, _returns=Attachment) + def get(file_path): + '''This method loads a document from the specified file path + and returns it. If the path isn't found, an exception is + raised. + ''' + + if not os.path.exists(file_path): + raise Fault("Client.FileName", "File '%s' not found" % file_path) + + document = Attachment(file_name=file_path) + # the service automatically loads the data from the file. + # alternatively, The data could be manually loaded into memory + # and loaded into the Attachment like: + # document = Attachment(data=data_from_file) + return document + +if __name__=='__main__': + try: + from wsgiref.simple_server import make_server + except ImportError: + print "Error: example server code requires Python >= 2.5" + + application = Application([DocumentArchiver], 'rpclib.examples.binary', + interface=Wsdl11(), in_protocol=HttpRpc(), out_protocol=HttpRpc()) + + logging.basicConfig(level=logging.DEBUG) + logging.getLogger('rpclib.protocol.soap.soap11').setLevel(logging.DEBUG) + + server = make_server('127.0.0.1', 7789, WsgiApplication(application)) + print "listening to http://127.0.0.1:7789" + print "wsdl is at: http://localhost:7789/?wsdl" + + server.serve_forever() diff --git a/examples/binary.py b/examples/binary_soap.py similarity index 87% rename from examples/binary.py rename to examples/binary_soap.py index 32a7cc78c..6cd4a2490 100755 --- a/examples/binary.py +++ b/examples/binary_soap.py @@ -19,21 +19,23 @@ # import os +import logging from tempfile import mkstemp from rpclib.application import Application from rpclib.decorator import srpc from rpclib.interface.wsdl import Wsdl11 +from rpclib.model.binary import Attachment +from rpclib.model.fault import Fault +from rpclib.model.primitive import String from rpclib.protocol.soap import Soap11 from rpclib.service import ServiceBase -from rpclib.model.primitive import String -from rpclib.model.binary import Attachment from rpclib.server.wsgi import WsgiApplication class DocumentArchiver(ServiceBase): @srpc(Attachment, _returns=String) - def archive_document(document): + def put(document): '''This method accepts an Attachment object, and returns the filename of the archived file ''' @@ -46,14 +48,14 @@ def archive_document(document): return fname @srpc(String, _returns=Attachment) - def get_archived_document(file_path): + def get(file_path): '''This method loads a document from the specified file path - and returns it. If the path isn't found, an exception is + and returns it. If the path isn't found, an exception is raised. ''' if not os.path.exists(file_path): - raise Exception("File [%s] not found"%file_path) + raise Fault("Client.FileName", "File '%s' not found" % file_path) document = Attachment(file_name=file_path) # the service automatically loads the data from the file. @@ -71,8 +73,10 @@ def get_archived_document(file_path): application = Application([DocumentArchiver], 'rpclib.examples.binary', interface=Wsdl11(), in_protocol=Soap11(), out_protocol=Soap11()) - server = make_server('127.0.0.1', 7789, WsgiApplication(application)) + logging.basicConfig(level=logging.DEBUG) + logging.getLogger('rpclib.protocol.soap.soap11').setLevel(logging.DEBUG) + server = make_server('127.0.0.1', 7789, WsgiApplication(application)) print "listening to http://127.0.0.1:7789" print "wsdl is at: http://localhost:7789/?wsdl" diff --git a/src/rpclib/model/binary.py b/src/rpclib/model/binary.py index 4e420c446..e30a3cde4 100644 --- a/src/rpclib/model/binary.py +++ b/src/rpclib/model/binary.py @@ -70,21 +70,44 @@ def to_string(cls, value): if not (value.data is None): # the data has already been loaded, just encode # and return the element - return value.data + data = value.data elif not (value.file_name is None): # the data hasn't been loaded, but a file has been # specified - data_string = StringIO() - file_name = value.file_name - file = open(file_name, 'rb') - base64.encode(file, data_string) - file.close() + data = open(value.file_name, 'rb').read() - # go back to the begining of the data - data_string.seek(0) - return data_string.read() + else: + raise Exception("Neither data nor a file_name has been specified") + + return data + + @classmethod + @nillable_string + def to_base64(cls, value): + ostream = StringIO() + if not (value.data is None): + istream = StringIO(value.data) + + elif not (value.file_name is None): + istream = open(value.file_name, 'rb') else: raise Exception("Neither data nor a file_name has been specified") + + base64.encode(istream, ostream) + ostream.seek(0) + + return ostream.read() + + @classmethod + @nillable_string + def from_base64(cls, value): + istream = StringIO(value) + ostream = StringIO() + + base64.decode(istream, ostream) + ostream.seek(0) + + return Attachment(data=ostream.read()) diff --git a/src/rpclib/protocol/xml/_base.py b/src/rpclib/protocol/xml/_base.py index 4ad540124..caf0a04a6 100644 --- a/src/rpclib/protocol/xml/_base.py +++ b/src/rpclib/protocol/xml/_base.py @@ -29,6 +29,7 @@ from rpclib.error import NotFoundError from rpclib.model import ModelBase +from rpclib.model.binary import Attachment from rpclib.model.complex import Array from rpclib.model.complex import Iterable from rpclib.model.complex import ComplexModelBase @@ -42,6 +43,7 @@ from rpclib.protocol import ProtocolBase from rpclib.protocol.xml.model import base_to_parent_element +from rpclib.protocol.xml.model.binary import binary_to_parent_element from rpclib.protocol.xml.model.enum import enum_to_parent_element from rpclib.protocol.xml.model.fault import fault_to_parent_element from rpclib.protocol.xml.model.complex import complex_to_parent_element @@ -51,6 +53,7 @@ from rpclib.protocol.xml.model.primitive import duration_to_parent_element from rpclib.protocol.xml.model import base_from_element +from rpclib.protocol.xml.model.binary import binary_from_element from rpclib.protocol.xml.model.complex import array_from_element from rpclib.protocol.xml.model.complex import iterable_from_element from rpclib.protocol.xml.model.complex import complex_from_element @@ -62,6 +65,7 @@ _serialization_handlers = cdict({ ModelBase: base_to_parent_element, + Attachment: binary_to_parent_element, ComplexModelBase: complex_to_parent_element, Fault: fault_to_parent_element, String: string_to_parent_element, @@ -73,6 +77,7 @@ _deserialization_handlers = cdict({ ModelBase: base_from_element, + Attachment: binary_from_element, ComplexModelBase: complex_from_element, Fault: fault_from_element, String: string_from_element, diff --git a/src/rpclib/protocol/xml/model/binary.py b/src/rpclib/protocol/xml/model/binary.py index 32c87dbb9..ea238ec33 100644 --- a/src/rpclib/protocol/xml/model/binary.py +++ b/src/rpclib/protocol/xml/model/binary.py @@ -24,28 +24,23 @@ from lxml import etree -from rpclib.model.binary import Attachment - from _base import nillable_value from _base import nillable_element + @nillable_value -def to_parent_element(cls, value, tns, parent_elt, name='retval'): +def binary_to_parent_element(prot, cls, value, tns, parent_elt, name='retval'): '''This class method takes the data from the attachment and base64 encodes it as the text of an Element. An attachment can specify a file_name and if no data is given, it will read the data from the file ''' - - element = etree.SubElement(parent_elt, '{%s}%s' % (tns,name)) - element.text = base64.encodestring(cls.to_string(value)) + element = etree.SubElement(parent_elt, "{%s}%s" % (tns,name)) + element.text = cls.to_base64(value) @nillable_element -def from_xml(cls, element): +def binary_from_element(prot, cls, element): '''This method returns an Attachment object that contains the base64 decoded string of the text of the given element ''' - data = base64.decodestring(element.text) - a = Attachment(data=data) - return a - + return cls.from_base64(element.text)