diff --git a/.hgignore b/.hgignore index 81656dc..59b4c42 100644 --- a/.hgignore +++ b/.hgignore @@ -4,8 +4,10 @@ syntax: glob *.pyo *.egg-info *.swp +*.orig ~* .coverage +.noseids syntax: regexp diff --git a/setup.py b/setup.py index 63dca4c..81ce88c 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,6 @@ setup( 'wsme.protocols': [ 'restjson = wsme.protocols.restjson:RestJsonProtocol', 'restxml = wsme.protocols.restxml:RestXmlProtocol', - 'soap = wsme.protocols.soap:SoapProtocol', ] }, ) diff --git a/wsme/protocols/soap.py b/wsme/protocols/soap.py deleted file mode 100644 index 282e25d..0000000 --- a/wsme/protocols/soap.py +++ /dev/null @@ -1,335 +0,0 @@ -""" -A SOAP implementation for wsme. -Parts of the code were taken from the tgwebservices soap implmentation. -""" - -import pkg_resources -import datetime -import decimal -import base64 -import logging - -from simplegeneric import generic - -try: - from xml.etree import cElementTree as et -except ImportError: - import cElementTree as et - -from genshi.builder import tag, Element, Namespace -from genshi.template import MarkupTemplate -from wsme.controller import pexpose -import wsme.types -from wsme import exc -from wsme.utils import * - -log = logging.getLogger(__name__) - -xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance' -type_qn = '{%s}type' % xsi_ns -nil_qn = '{%s}nil' % xsi_ns - - -type_registry = { - basestring: 'xsd:string', - str: 'xsd:string', - unicode: 'xsd:string', - int: 'xsd:int', - long: "xsd:long", - float: "xsd:float", - bool: "xsd:boolean", - #unsigned: "xsd:unsignedInt", - datetime.datetime: "xsd:dateTime", - datetime.date: "xsd:date", - datetime.time: "xsd:time", - decimal.Decimal: "xsd:decimal", - wsme.types.binary: "xsd:base64Binary", -} - -array_registry = { - basestring: "String_Array", - str: "String_Array", - unicode: "String_Array", - int: "Int_Array", - long: "Long_Array", - float: "Float_Array", - bool: "Boolean_Array", -} - - -def soap_array(datatype): - if datatype in array_registry: - return array_registry[datatype] - return 'types:' + datatype.__name__ + '_Array' - - -def soap_type(datatype): - if type(datatype) == list: - return soap_array(datatype[0]) - if datatype in type_registry: - return type_registry[datatype] - if wsme.types.iscomplex(datatype): - return "types:%s" % datatype.__name__ - - -def soap_fname(funcdef): - return "%s%s" % ( - "".join((i.capitalize() for i in funcdef.path)), - funcdef.name.capitalize()) - - -def make_soap_element(datatype, tag, value): - el = Element(tag) - if value is None: - el(**{'xsi:nil': 'true'}) - elif wsme.types.iscomplex(datatype): - el(**{'xsi:type': datatype.__name__}) - for name, attrdef in wsme.types.list_attributes(datatype): - el.append( - tosoap(attrdef.datatype, name, getattr(value, name))) - else: - el(value, **{'xsi:type': type_registry.get(datatype)}) - return el - - -@generic -def tosoap(datatype, tag, value): - """Converts a value into xml Element objects for inclusion in the SOAP - response output (after adding the type to the type_registry). - - If a non-complex user specific type is to be used in the api, - a specific toxml should be added:: - - from wsme.protocol.soap import tosoap, make_soap_element, type_registry - - class MySpecialType(object): - pass - - type_registry[MySpecialType] = 'xsd:MySpecialType' - - @tosoap.when_object(MySpecialType) - def myspecialtype_tosoap(datatype, tag, value): - return make_soap_element(datatype, tag, str(value)) - """ - return make_soap_element(datatype, tag, value) - - -@tosoap.when_type(list) -def array_tosoap(datatype, tag, value): - el = Element(tag) - el(**{'xsi:type': soap_array(datatype[0])}) - if value is None: - el(**{'xsi:nil': 'true'}) - for item in value: - el.append(tosoap(datatype[0], 'item', item)) - return el - - -@tosoap.when_object(datetime.datetime) -def datetime_tosoap(datatype, tag, value): - return make_soap_element(datatype, tag, - value is not None and value.isoformat() or None) - - -@tosoap.when_object(wsme.types.binary) -def binary_tosoap(datatype, tag, value): - return make_soap_element(datatype, tag, - value is not None and base64.encodestring(value) - or None) - - -@tosoap.when_object(None) -def None_tosoap(datatype, tag, value): - return make_soap_element(datatype, tag, None) - - -@generic -def fromsoap(datatype, el, ns): - """ - A generic converter from soap elements to python datatype. - - If a non-complex user specific type is to be used in the api, - a specific fromsoap should be added. - """ - if el.get(nil_qn) == 'true': - return None - soaptype = el.get(type_qn) - if datatype in type_registry: - if soaptype != type_registry[datatype]: - raise exc.InvalidInput(el.tag, et.tostring(el)) - value = datatype(el.text) - else: - if soaptype != datatype.__name__: - raise exc.InvalidInput(el.tag, et.tostring(el)) - value = datatype() - for name, attr in wsme.types.list_attributes(datatype): - child = el.find('{%s}%s' % (ns['type'], name)) - setattr(value, name, fromsoap(attr.datatype, child, ns)) - return value - - -@fromsoap.when_type(list) -def array_fromsoap(datatype, el, ns): - return [fromsoap(datatype[0], child, ns) for child in el] - -@fromsoap.when_object(datetime.date) -def date_fromsoap(datatype, el, ns): - if el.get(nil_qn) == 'true': - return None - if el.get(type_qn) != 'xsd:date': - raise exc.InvalidInput(el.tag, et.tostring(el)) - return parse_isodate(el.text) - - -@fromsoap.when_object(datetime.time) -def time_fromsoap(datatype, el, ns): - if el.get(nil_qn) == 'true': - return None - if el.get(type_qn) != 'xsd:time': - raise exc.InvalidInput(el.tag, et.tostring(el)) - return parse_isotime(el.text) - - -@fromsoap.when_object(datetime.datetime) -def datetime_fromsoap(datatype, el, ns): - if el.get(nil_qn) == 'true': - return None - if el.get(type_qn) != 'xsd:dateTime': - raise exc.InvalidInput(el.tag, et.tostring(el)) - return parse_isodatetime(el.text) - - -@fromsoap.when_object(wsme.types.binary) -def binary_fromsoap(datatype, el, ns): - if el.get(nil_qn) == 'true': - return None - if el.get(type_qn) != 'xsd:base64Binary': - raise exc.InvalidInput(el.tag, et.tostring(el)) - return base64.decodestring(el.text) - - -class SoapProtocol(object): - """ - SOAP protocol. - - .. autoattribute:: name - .. autoattribute:: content_types - """ - name = 'soap' - content_types = ['application/soap+xml'] - - ns = { - "soap": "http://www.w3.org/2001/12/soap-envelope", - "soapenv": "http://schemas.xmlsoap.org/soap/envelope/", - "soapenc": "http://schemas.xmlsoap.org/soap/encoding/", - } - - def __init__(self, tns=None, - typenamespace=None, - baseURL=None): - self.tns = tns - self.typenamespace = typenamespace - self.servicename = 'MyApp' - self.baseURL = baseURL - self._name_mapping = {} - - def get_name_mapping(self, service=None): - if service not in self._name_mapping: - self._name_mapping[service] = dict( - (soap_fname(f), f.path + [f.name]) - for f in self.root.getapi() - if service is None or (f.path and f.path[0] == service)) - return self._name_mapping[service] - - def accept(self, req): - if req.path.endswith('.wsdl'): - return True - for ct in self.content_types: - if req.headers['Content-Type'].startswith(ct): - return True - if req.headers.get("Soapaction"): - return True - return False - - def extract_path(self, request): - if request.path.endswith('.wsdl'): - return ['_protocol', self.name, 'api_wsdl'] - el = et.fromstring(request.body) - body = el.find('{%(soapenv)s}Body' % self.ns) - # Extract the service name from the tns - message = list(body)[0] - fname = message.tag - if fname.startswith('{%s}' % self.typenamespace): - fname = fname[len(self.typenamespace) + 2:] - mapping = self.get_name_mapping() - if fname not in mapping: - raise exc.UnknownFunction(fname) - path = mapping[fname] - request._wsme_soap_message = message - return path - return None - - def read_arguments(self, funcdef, request): - kw = {} - if not hasattr(request, '_wsme_soap_message'): - return kw - msg = request._wsme_soap_message - parameters = msg.find('{%s}parameters' % self.typenamespace) - if parameters: - for param in parameters: - name = param.tag[len(self.typenamespace) + 2:] - arg = funcdef.get_arg(name) - value = fromsoap(arg.datatype, param, { - 'type': self.typenamespace, - }) - kw[name] = value - - return kw - - def soap_response(self, funcdef, result): - r = Element(soap_fname(funcdef) + 'Response') - r.append(tosoap(funcdef.return_type, 'result', result)) - return r - - def encode_result(self, funcdef, result): - envelope = self.render_template('soap', - typenamespace=self.typenamespace, - result=result, - funcdef=funcdef, - soap_response=self.soap_response) - return envelope - - def get_template(self, name): - return pkg_resources.resource_string( - __name__, 'templates/%s.html' % name) - - def render_template(self, name, **kw): - tmpl = MarkupTemplate(self.get_template(name)) - stream = tmpl.generate(**kw) - return stream.render('xml') - - def encode_error(self, infos): - return self.render_template('fault', - typenamespace=self.typenamespace, - **infos) - - @pexpose(contenttype="text/xml") - def api_wsdl(self, service=None): - if service is None: - servicename = self.servicename - else: - servicename = self.servicename + service.capitalize() - return self.render_template('wsdl', - tns=self.tns, - typenamespace=self.typenamespace, - soapenc=self.ns['soapenc'], - service_name=servicename, - complex_types=(t() for t in wsme.types.complex_types), - funclist=self.root.getapi(), - arrays=wsme.types.array_types, - list_attributes=wsme.types.list_attributes, - baseURL=self.baseURL, - soap_array=soap_array, - soap_type=soap_type, - soap_fname=soap_fname, - ) diff --git a/wsme/protocols/templates/__init__.py b/wsme/protocols/templates/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/wsme/protocols/templates/fault.html b/wsme/protocols/templates/fault.html deleted file mode 100644 index 4e36ec5..0000000 --- a/wsme/protocols/templates/fault.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - ${faultcode} - ${faultstring} - ${debuginfo} - - - diff --git a/wsme/protocols/templates/soap.html b/wsme/protocols/templates/soap.html deleted file mode 100644 index 6e2c470..0000000 --- a/wsme/protocols/templates/soap.html +++ /dev/null @@ -1,10 +0,0 @@ - - - ${soap_response(funcdef, result)} - - diff --git a/wsme/protocols/templates/wsdl.html b/wsme/protocols/templates/wsdl.html deleted file mode 100644 index 9c9bf11..0000000 --- a/wsme/protocols/templates/wsdl.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${funcdef.doc} - - - - - - - - - - - - - - - - - - - - - - WSDL File for ${service_name} - - - - - diff --git a/wsme/tests/test_soap.py b/wsme/tests/test_soap.py deleted file mode 100644 index 8d86c57..0000000 --- a/wsme/tests/test_soap.py +++ /dev/null @@ -1,211 +0,0 @@ -import decimal -import datetime -import base64 - -import wsme.tests.protocol - -try: - import xml.etree.ElementTree as et -except: - import cElementTree as et - -from wsme.protocols.soap import SoapProtocol -import wsme.utils - -tns = "http://foo.bar.baz/soap/" -typenamespace = "http://foo.bar.baz/types/" - -soapenv_ns = 'http://schemas.xmlsoap.org/soap/envelope/' -xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance' -body_qn = '{%s}Body' % soapenv_ns -fault_qn = '{%s}Fault' % soapenv_ns -faultcode_qn = '{%s}faultcode' % soapenv_ns -faultstring_qn = '{%s}faultstring' % soapenv_ns -faultdetail_qn = '{%s}detail' % soapenv_ns -type_qn = '{%s}type' % xsi_ns -nil_qn = '{%s}nil' % xsi_ns - - -def build_soap_message(method, params=""): - message = """ - - - - <%(method)s> - %(params)s - - - - -""" % dict(method=method, - params=params, - typenamespace=typenamespace) - return message - - -python_types = { - int: ('xsd:int', str), - float: ('xsd:float', str), - str: ('xsd:string', str), - unicode: ('xsd:string', unicode), - wsme.types.binary: ('xsd:base64Binary', base64.encodestring), - decimal.Decimal: ('xsd:decimal', str), - datetime.date: ('xsd:date', datetime.date.isoformat), - datetime.time: ('xsd:time', datetime.time.isoformat), - datetime.datetime: ('xsd:dateTime', datetime.datetime.isoformat), -} - -array_types = { - basestring: "String_Array", - str: "String_Array", - unicode: "String_Array", - int: "Int_Array", - long: "Long_Array", - float: "Float_Array", - bool: "Boolean_Array", -} - - -def tosoap(tag, value): - if isinstance(value, tuple): - value, datatype = value - else: - datatype = type(value) - el = et.Element(tag) - if value is None: - el.set('xsi:nil', True) - elif isinstance(datatype, list): - if datatype[0] in array_types: - el.set('xsi:type', array_types[datatype[0]]) - else: - el.set('xsi:type', 'types:' + datatype[0].__name__) - for item in value: - el.append(tosoap('item', (item, datatype[0]))) - elif datatype in python_types: - stype, conv = python_types[datatype] - el.text = conv(value) - el.set('xsi:type', stype) - else: - el.set('xsi:type', datatype.__name__) - for name, attr in datatype._wsme_attributes: - if name in value: - el.append(tosoap(name, (value[name], attr.datatype))) - - return el - - -def read_bool(value): - return value == 'true' - -soap_types = { - 'xsd:string': unicode, - 'xsd:int': int, - 'xsd:long': long, - 'xsd:float': float, - 'xsd:decimal': decimal.Decimal, - 'xsd:boolean': read_bool, - 'xsd:date': wsme.utils.parse_isodate, - 'xsd:time': wsme.utils.parse_isotime, - 'xsd:dateTime': wsme.utils.parse_isodatetime, - 'xsd:base64Binary': base64.decodestring, -} - - -def fromsoap(el): - if el.get(nil_qn) == 'true': - return None - t = el.get(type_qn) - if t in soap_types: - return soap_types[t](el.text) - elif t and t.endswith('_Array'): - return [fromsoap(i) for i in el] - else: - d = {} - for child in el: - name = child.tag - assert name.startswith('{%s}' % typenamespace) - name = name[len(typenamespace) + 2:] - d[name] = fromsoap(child) - return d - - -class TestSOAP(wsme.tests.protocol.ProtocolTestCase): - protocol = SoapProtocol( - tns=tns, typenamespace=typenamespace) - - def test_simple_call(self): - message = build_soap_message('Touch') - print message - res = self.app.post('/', message, - headers={"Content-Type": "application/soap+xml; charset=utf-8"}, - expect_errors=True) - print res.body - assert res.status.startswith('200') - - def call(self, fpath, _rt=None, _accept=None, - _no_result_decode=False, **kw): - path = fpath.strip('/').split('/') - # get the actual definition so we can build the adequate request - if kw: - el = et.Element('parameters') - for key, value in kw.items(): - el.append(tosoap(key, value)) - - params = et.tostring(el) - else: - params = "" - methodname = ''.join((i.capitalize() for i in path)) - message = build_soap_message(methodname, params) - print message - headers = {"Content-Type": "application/soap+xml; charset=utf-8"} - if _accept is not None: - headers['Accept'] = _accept - res = self.app.post('/', message, - headers=headers, - expect_errors=True) - print "Status: ", res.status, "Received:", res.body - - if _no_result_decode: - return res - - el = et.fromstring(res.body) - body = el.find(body_qn) - print body - - if res.status_int == 200: - r = body.find('{%s}%sResponse' % (typenamespace, methodname)) - result = r.find('{%s}result' % typenamespace) - print "Result element: ", result - return fromsoap(result) - elif res.status_int == 400: - fault = body.find(fault_qn) - raise wsme.tests.protocol.CallException( - fault.find(faultcode_qn).text, - fault.find(faultstring_qn).text, - "") - - elif res.status_int == 500: - fault = body.find(fault_qn) - raise wsme.tests.protocol.CallException( - fault.find(faultcode_qn).text, - fault.find(faultstring_qn).text, - fault.find(faultdetail_qn).text) - - if el.tag == 'error': - raise wsme.tests.protocol.CallException( - el.find('faultcode').text, - el.find('faultstring').text, - el.find('debuginfo') is not None and - el.find('debuginfo').text or None) - - else: - return loadxml(et.fromstring(res.body)) - - def test_wsdl(self): - res = self.app.get('/api.wsdl') - print res.body - assert res.body.find('NestedOuter_Array') != -1 - assert 'ReturntypesGetunicode' in res.body