297 lines
8.8 KiB
Python
297 lines
8.8 KiB
Python
import six
|
|
import wsme.types
|
|
|
|
try:
|
|
from lxml import etree as ET
|
|
use_lxml = True
|
|
except ImportError:
|
|
from xml.etree import cElementTree as ET # noqa
|
|
use_lxml = False
|
|
|
|
|
|
def xml_tostring(el, pretty_print=False):
|
|
if use_lxml:
|
|
return ET.tostring(el, pretty_print=pretty_print)
|
|
return ET.tostring(el)
|
|
|
|
|
|
class NS(object):
|
|
def __init__(self, url):
|
|
self.url = url
|
|
|
|
def __call__(self, name):
|
|
return self.qn(name)
|
|
|
|
def __str__(self):
|
|
return self.url
|
|
|
|
def qn(self, name):
|
|
return '{%s}%s' % (self.url, name)
|
|
|
|
wsdl_ns = NS("http://schemas.xmlsoap.org/wsdl/")
|
|
soap_ns = NS("http://schemas.xmlsoap.org/wsdl/soap/")
|
|
xs_ns = NS("http://www.w3.org/2001/XMLSchema")
|
|
soapenc_ns = NS("http://schemas.xmlsoap.org/soap/encoding/")
|
|
|
|
|
|
class WSDLGenerator(object):
|
|
def __init__(
|
|
self,
|
|
tns,
|
|
types_ns,
|
|
soapenc,
|
|
service_name,
|
|
complex_types,
|
|
funclist,
|
|
arrays,
|
|
baseURL,
|
|
soap_array,
|
|
soap_type,
|
|
soap_fname):
|
|
|
|
self.tns = NS(tns)
|
|
self.types_ns = NS(types_ns)
|
|
self.soapenc = soapenc
|
|
self.service_name = service_name
|
|
self.complex_types = complex_types
|
|
self.funclist = funclist
|
|
self.arrays = arrays
|
|
self.baseURL = baseURL or ''
|
|
self.soap_array = soap_array
|
|
self.soap_fname = soap_fname
|
|
self.soap_type = soap_type
|
|
|
|
def gen_complex_type(self, cls):
|
|
complexType = ET.Element(xs_ns('complexType'))
|
|
complexType.set('name', cls.__name__)
|
|
sequence = ET.SubElement(complexType, xs_ns('sequence'))
|
|
for attrdef in wsme.types.list_attributes(cls):
|
|
soap_type = self.soap_type(attrdef.datatype, str(self.types_ns))
|
|
if soap_type is None:
|
|
continue
|
|
element = ET.SubElement(sequence, xs_ns('element'))
|
|
element.set('name', attrdef.name)
|
|
element.set('type', soap_type)
|
|
element.set('minOccurs', '1' if attrdef.mandatory else '0')
|
|
element.set('maxOccurs', '1')
|
|
return complexType
|
|
|
|
def gen_array(self, array):
|
|
complexType = ET.Element(xs_ns('complexType'))
|
|
complexType.set('name', self.soap_array(array, False))
|
|
ET.SubElement(
|
|
ET.SubElement(complexType, xs_ns('sequence')),
|
|
xs_ns('element'),
|
|
name='item',
|
|
maxOccurs='unbounded',
|
|
nillable='true',
|
|
type=self.soap_type(array.item_type, self.types_ns)
|
|
)
|
|
return complexType
|
|
|
|
def gen_function_types(self, path, funcdef):
|
|
args_el = ET.Element(
|
|
xs_ns('element'),
|
|
name=self.soap_fname(path, funcdef)
|
|
)
|
|
|
|
sequence = ET.SubElement(
|
|
ET.SubElement(args_el, xs_ns('complexType')),
|
|
xs_ns('sequence')
|
|
)
|
|
|
|
for farg in funcdef.arguments:
|
|
t = self.soap_type(farg.datatype, True)
|
|
if t is None:
|
|
continue
|
|
element = ET.SubElement(
|
|
sequence, xs_ns('element'),
|
|
name=farg.name,
|
|
type=self.soap_type(farg.datatype, True)
|
|
)
|
|
if not farg.mandatory:
|
|
element.set('minOccurs', 0)
|
|
|
|
response_el = ET.Element(
|
|
xs_ns('element'),
|
|
name=self.soap_fname(path, funcdef) + 'Response'
|
|
)
|
|
element = ET.SubElement(
|
|
ET.SubElement(
|
|
ET.SubElement(
|
|
response_el,
|
|
xs_ns('complexType')
|
|
),
|
|
xs_ns('sequence')
|
|
),
|
|
xs_ns('element'),
|
|
name='result'
|
|
)
|
|
return_soap_type = self.soap_type(funcdef.return_type, True)
|
|
if return_soap_type is not None:
|
|
element.set('type', return_soap_type)
|
|
|
|
return args_el, response_el
|
|
|
|
def gen_types(self):
|
|
types = ET.Element(wsdl_ns('types'))
|
|
schema = ET.SubElement(types, xs_ns('schema'))
|
|
schema.set('elementFormDefault', 'qualified')
|
|
schema.set('targetNamespace', str(self.types_ns))
|
|
for cls in self.complex_types:
|
|
schema.append(self.gen_complex_type(cls))
|
|
for array in self.arrays:
|
|
schema.append(self.gen_array(array))
|
|
for path, funcdef in self.funclist:
|
|
schema.extend(self.gen_function_types(path, funcdef))
|
|
return types
|
|
|
|
def gen_functions(self):
|
|
messages = []
|
|
|
|
binding = ET.Element(
|
|
wsdl_ns('binding'),
|
|
name='%s_Binding' % self.service_name,
|
|
type='%s_PortType' % self.service_name
|
|
)
|
|
ET.SubElement(
|
|
binding,
|
|
soap_ns('binding'),
|
|
style='document',
|
|
transport='http://schemas.xmlsoap.org/soap/http'
|
|
)
|
|
|
|
portType = ET.Element(
|
|
wsdl_ns('portType'),
|
|
name='%s_PortType' % self.service_name
|
|
)
|
|
|
|
for path, funcdef in self.funclist:
|
|
soap_fname = self.soap_fname(path, funcdef)
|
|
|
|
# message
|
|
req_message = ET.Element(
|
|
wsdl_ns('message'),
|
|
name=soap_fname + 'Request',
|
|
xmlns=str(self.types_ns)
|
|
)
|
|
ET.SubElement(
|
|
req_message,
|
|
wsdl_ns('part'),
|
|
name='parameters',
|
|
element='types:%s' % soap_fname
|
|
)
|
|
messages.append(req_message)
|
|
|
|
res_message = ET.Element(
|
|
wsdl_ns('message'),
|
|
name=soap_fname + 'Response',
|
|
xmlns=str(self.types_ns)
|
|
)
|
|
ET.SubElement(
|
|
res_message,
|
|
wsdl_ns('part'),
|
|
name='parameters',
|
|
element='types:%sResponse' % soap_fname
|
|
)
|
|
messages.append(res_message)
|
|
|
|
# portType/operation
|
|
operation = ET.SubElement(
|
|
portType,
|
|
wsdl_ns('operation'),
|
|
name=soap_fname
|
|
)
|
|
if funcdef.doc:
|
|
ET.SubElement(
|
|
operation,
|
|
wsdl_ns('documentation')
|
|
).text = funcdef.doc
|
|
ET.SubElement(
|
|
operation, wsdl_ns('input'),
|
|
message='tns:%sRequest' % soap_fname
|
|
)
|
|
ET.SubElement(
|
|
operation, wsdl_ns('output'),
|
|
message='tns:%sResponse' % soap_fname
|
|
)
|
|
|
|
# binding/operation
|
|
operation = ET.SubElement(
|
|
binding,
|
|
wsdl_ns('operation'),
|
|
name=soap_fname
|
|
)
|
|
ET.SubElement(
|
|
operation,
|
|
wsdl_ns('operation'),
|
|
soapAction=soap_fname
|
|
)
|
|
ET.SubElement(
|
|
ET.SubElement(
|
|
operation,
|
|
wsdl_ns('input')
|
|
),
|
|
soap_ns('body'),
|
|
use='literal'
|
|
)
|
|
ET.SubElement(
|
|
ET.SubElement(
|
|
operation,
|
|
wsdl_ns('output')
|
|
),
|
|
soap_ns('body'),
|
|
use='literal'
|
|
)
|
|
|
|
return messages + [portType, binding]
|
|
|
|
def gen_service(self):
|
|
service = ET.Element(wsdl_ns('service'), name=self.service_name)
|
|
ET.SubElement(
|
|
service,
|
|
wsdl_ns('documentation')
|
|
).text = six.u('WSDL File for %s') % self.service_name
|
|
ET.SubElement(
|
|
ET.SubElement(
|
|
service,
|
|
wsdl_ns('port'),
|
|
binding='tns:%s_Binding' % self.service_name,
|
|
name='%s_PortType' % self.service_name
|
|
),
|
|
soap_ns('address'),
|
|
location=self.baseURL
|
|
)
|
|
|
|
return service
|
|
|
|
def gen_definitions(self):
|
|
attrib = {
|
|
'name': self.service_name,
|
|
'targetNamespace': str(self.tns)
|
|
}
|
|
if use_lxml:
|
|
definitions = ET.Element(
|
|
wsdl_ns('definitions'),
|
|
attrib=attrib,
|
|
nsmap={
|
|
'xs': str(xs_ns),
|
|
'soap': str(soap_ns),
|
|
'types': str(self.types_ns),
|
|
'tns': str(self.tns)
|
|
}
|
|
)
|
|
else:
|
|
definitions = ET.Element(wsdl_ns('definitions'), **attrib)
|
|
definitions.set('xmlns:types', str(self.types_ns))
|
|
definitions.set('xmlns:tns', str(self.tns))
|
|
|
|
definitions.set('name', self.service_name)
|
|
definitions.append(self.gen_types())
|
|
definitions.extend(self.gen_functions())
|
|
definitions.append(self.gen_service())
|
|
return definitions
|
|
|
|
def generate(self, format=False):
|
|
return xml_tostring(self.gen_definitions(), pretty_print=format)
|