soap.py
author Radek Brich <radek.brich@devl.cz>
Wed, 03 Apr 2013 19:59:11 +0200
changeset 0 ee24ce33ab55
permissions -rw-r--r--
Add SOAP client.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     1
# minisoap - minimal SOAP client
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     2
#
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     3
# Copyright (c) 2011, 2013  Radek Brich <radek.brich@devl.cz>
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     4
#
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     5
# Permission is hereby granted, free of charge, to any person obtaining a copy
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     6
# of this software and associated documentation files (the "Software"), to deal
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     7
# in the Software without restriction, including without limitation the rights
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     8
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
     9
# copies of the Software, and to permit persons to whom the Software is
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    10
# furnished to do so, subject to the following conditions:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    11
#
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    12
# The above copyright notice and this permission notice shall be included in
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    13
# all copies or substantial portions of the Software.
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    14
#
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    15
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    16
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    17
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    18
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    19
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    20
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    21
# THE SOFTWARE.
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    22
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    23
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    24
from http.client import HTTPConnection
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    25
from urllib.parse import urlparse
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    26
from lxml import etree
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    27
from collections import OrderedDict
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    28
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    29
debug = 0
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    30
HTTPConnection.debuglevel = debug
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    31
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    32
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    33
class SoapError(Exception):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    34
    pass
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    35
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    36
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    37
class SoapResponse(OrderedDict):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    38
    def __init__(self, name):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    39
        OrderedDict.__init__(self)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    40
        self.name = name
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    41
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    42
    def _printout(self, out, res, level):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    43
        indent = level * 2 * ' '
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    44
        for k in res.keys():
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    45
            if type(res[k]) is list:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    46
                for row in res[k]:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    47
                    out.extend([indent, k, '\n'])
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    48
                    self._printout(out, row, level+1)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    49
            else:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    50
                out.extend([indent, k, ': ', str(res[k]), '\n'])
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    51
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    52
    def __str__(self):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    53
        out = [self.name, '\n']
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    54
        self._printout(out, self, 1)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    55
        return ''.join(out).rstrip()
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    56
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    57
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    58
class SoapClient:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    59
    def __init__(self, url, namespace):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    60
        self.url = url
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    61
        self.parsedurl = urlparse(url)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    62
        self.namespace = namespace
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    63
        self.conn = HTTPConnection(self.parsedurl.netloc)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    64
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    65
    def _build_soap_message(self, name, args):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    66
        SOAP = 'http://schemas.xmlsoap.org/soap/envelope/'
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    67
        root = etree.Element('{%s}Envelope' % SOAP, nsmap={'soap':SOAP})
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    68
        body = etree.SubElement(root, '{%s}Body' % SOAP)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    69
        func = etree.SubElement(body, '{%s}%s' % (self.namespace, name), nsmap={None:self.namespace})
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    70
        for key in args.keys():
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    71
            param = etree.SubElement(func, key)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    72
            param.text = str(args[key])
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    73
        if debug:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    74
            print(etree.tostring(root, pretty_print=True).decode('utf8'))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    75
        return root
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    76
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    77
    def _break_params(self, func, d):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    78
        for param in func.iterchildren():
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    79
            if len(param) == 0:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    80
                d[param.tag] = param.text
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    81
            else:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    82
                if not param.tag in d:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    83
                    d[param.tag] = []
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    84
                newd = OrderedDict()
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    85
                self._break_params(param, newd)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    86
                d[param.tag].append(newd)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    87
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    88
    def _break_soap_message(self, root):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    89
        if not root.tag.endswith('}Envelope'):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    90
            raise SoapError('Reply has no Envelope.')
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    91
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    92
        SOAP = root.tag.split('}')[0][1:]
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    93
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    94
        body_list = list(root.iterchildren('{%s}Body' % SOAP))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    95
        if len(body_list) != 1:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    96
            raise SoapError('Bad SOAP format (%d Body elements)' % len(body_list))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    97
        body = body_list[0]
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    98
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
    99
        func_list =list(body.iterchildren())
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   100
        if len(func_list) != 1:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   101
            raise SoapError('Bad SOAP format (Body has %d children, should have one)' % len(body_list))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   102
        func = func_list[0]
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   103
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   104
        if debug:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   105
            print(etree.tostring(func, pretty_print=True).decode('utf8'))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   106
        res = SoapResponse(func.tag)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   107
        self._break_params(func, res)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   108
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   109
        return res
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   110
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   111
    def _serialize_xml(self, root):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   112
        return etree.tostring(root,
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   113
            pretty_print=True,
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   114
            encoding='utf-8',
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   115
            xml_declaration=True)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   116
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   117
    def _parse_xml(self, file):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   118
        tree = etree.parse(file)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   119
        return tree.getroot()
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   120
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   121
    def _send_soap(self, body):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   122
        headers = {
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   123
            'Content-type': "text/xml;charset='utf-8'",
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   124
            'Soapaction': "''",
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   125
            'Accept': 'text/xml'}
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   126
        self.conn.request('POST', self.parsedurl.path, body, headers)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   127
        resp = self.conn.getresponse()
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   128
        if resp.status != 200:
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   129
            raise SoapError('Server replied %d %s' % (resp.status, resp.reason))
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   130
        return resp
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   131
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   132
    def __getattr__(self, name):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   133
        def fnc(**kwargs):
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   134
            root = self._build_soap_message(name, kwargs)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   135
            body = self._serialize_xml(root)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   136
            resp = self._send_soap(body)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   137
            root = self._parse_xml(resp)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   138
            return self._break_soap_message(root)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   139
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   140
        return fnc
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   141
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   142
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   143
if __name__ == '__main__':
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   144
    service = SoapClient('http://10.0.0.1:34567/', namespace='http://devl.cz/')
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   145
    res = service.my_test_request()
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   146
    print(res)
ee24ce33ab55 Add SOAP client.
Radek Brich <radek.brich@devl.cz>
parents:
diff changeset
   147