Introspecting a WSDL with Python Zeep
Asked Answered
H

2

24

I am attempting to use Zeep to describe the operations and types in a given WSDL, so that a program knows the operation names, their parameter names, the parameter types, and parameter attributes.

This info will be used to dynamically generate a UI for a given WSDL.

What I have got so far is just the string representations of the operations and types. Using code similar to what is found in this answer.

Here's an example:

from zeep import Client
import operator

wsdl = 'http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl'
client = Client(wsdl)

# get each operation signature
for service in client.wsdl.services.values():
    print("service:", service.name)
    for port in service.ports.values():
        operations = sorted(
            port.binding._operations.values(),
            key=operator.attrgetter('name'))

        for operation in operations:
            print("method :", operation.name)
            print("  input :", operation.input.signature())
            print()
    print()

# get a specific type signature by name
complextype = client.get_type('ns0:CartGetRequest')
print(complextype.name)
print(complextype.signature())

This gives an output like the following (shortened for brevity)

[...]

method : CartCreate
  input : MarketplaceDomain: xsd:string, AWSAccessKeyId: xsd:string, AssociateTag: xsd:string, Validate: xsd:string, XMLEscaping: xsd:string, Shared: ns0:CartCreateRequest, Request: ns0:CartCreateRequest[]

method : CartGet
  input : MarketplaceDomain: xsd:string, AWSAccessKeyId: xsd:string, AssociateTag: xsd:string, Validate: xsd:string, XMLEscaping: xsd:string, Shared: ns0:CartGetRequest, Request: ns0:CartGetRequest[]

[...]


CartGetRequest
{http://webservices.amazon.com/AWSECommerceService/2011-08-01}CartGetRequest(CartId: xsd:string, HMAC: xsd:string, MergeCart: xsd:string, ResponseGroup: xsd:string[])

The string representations returned by .signature() have the names and types, but I don't know how to parse them out individually. I have tried looping over each objects attrs with dir() as well, and they don't contain this info. It seems to be nested much deeper.

I could parse the string representations themselves, but then I am also missing whether or not the parameter is optional (more specifically, if it has the attribute minOccurs=0

It seems like SOAPpy actually has this functionality, but isn't maintained anymore.

So is there a way to introspect a WSDL with zeep that provides granular information about each operation, it's parameter names, types, and attributes similar to the SOAPpy implementation? Or should I parse the signature, or alternatively, parse the WSDL with a regular XML parser.

Hillell answered 29/4, 2018 at 17:26 Comment(5)
Try running dir() on the objects to see their available attributes in methods. It will be easier for others to help you if you provide an example that they can actually execute. Currently, your example has formatting issue as well as referencing variables not found in your snippet.Biedermeier
@Biedermeier Yeah forgot to add the imports, edited. Also, I've tried dir(), of course, but it does not have the information I'm looking for. I've even tried looping over each attribute found with dir and getting the repr() of that attribute or the result if it's callable, but zeep seems to be a little complex to pull it apart like that. The info must be nested way deeper. I'm currently reading the package code, but its massive and unruly for a relative novice like myself.Hillell
I played around with it a bit in pdb. Does operation.input.body.type.elements give you what you want?Biedermeier
@Biedermeier Yes, there it is, I can parse everything I need out of there by recursively traversing the element attributes. Post this as an answer and I'll accept it and add some code of mine which is working for posterity. Also, out of curiosity, you found this solely by using pdb?Hillell
No, I was also looking at the zeep source code.Biedermeier
B
7

You can access parameter elements with operation.input.body.type.elements, which is a list of tuples containing element objects. These objects contain information such as the type.

(Pdb) operation.input.body.type.elements
[('MarketplaceDomain', <Element(name='MarketplaceDomain', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('AWSAccessKeyId', <Element(name='AWSAccessKeyId', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('AssociateTag', <Element(name='AssociateTag', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('Validate', <Element(name='Validate', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('XMLEscaping', <Element(name='XMLEscaping', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('Shared', <Element(name='Shared', type=<zeep.xsd.dynamic_types.BrowseNodeLookupRequest object at 0x7f1bd8177e48>)>), ('Request', <Element(name='Request', type=<zeep.xsd.dynamic_types.BrowseNodeLookupRequest object at 0x7f1bd8177e48>)>)]
(Pdb) operation.input.body.type.elements[0][1].name
'MarketplaceDomain'
(Pdb) operation.input.body.type.elements[0][1].type.name
'string'
(Pdb) operation.input.body.type.elements[0][1].is_optional
True
Biedermeier answered 30/4, 2018 at 1:42 Comment(0)
H
26

Based on the answer from jordanm, I used the following to get all of the data I needed on the available methods

from zeep import Client
from pprint import pprint

wsdl = 'http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl'
client = Client(wsdl)


def parseElements(elements):
    all_elements = {}
    for name, element in elements:
        all_elements[name] = {}
        all_elements[name]['optional'] = element.is_optional
        if hasattr(element.type, 'elements'):
            all_elements[name]['type'] = parseElements(
                element.type.elements)
        else:
            all_elements[name]['type'] = str(element.type)

    return all_elements


interface = {}
for service in client.wsdl.services.values():
    interface[service.name] = {}
    for port in service.ports.values():
        interface[service.name][port.name] = {}
        operations = {}
        for operation in port.binding._operations.values():
            operations[operation.name] = {}
            operations[operation.name]['input'] = {}
            elements = operation.input.body.type.elements
            operations[operation.name]['input'] = parseElements(elements)
        interface[service.name][port.name]['operations'] = operations


pprint(interface)
Hillell answered 30/4, 2018 at 3:16 Comment(3)
Brillant, Thanks. This also works when you can't use python -m zeep wsdl_file (e.g. when wsdl lives behind Authentication)Cota
How do we call a soapOperation here ? can we do operation(param1, param2) ?Lethalethal
Hi @Hillell ! How can we query the SOAP API once we figure out the operation? I have a similar question that your answer was able to help me solve at least a part of it. I'm trying to figure out how to get to the operation that I need using this information.Irreplaceable
B
7

You can access parameter elements with operation.input.body.type.elements, which is a list of tuples containing element objects. These objects contain information such as the type.

(Pdb) operation.input.body.type.elements
[('MarketplaceDomain', <Element(name='MarketplaceDomain', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('AWSAccessKeyId', <Element(name='AWSAccessKeyId', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('AssociateTag', <Element(name='AssociateTag', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('Validate', <Element(name='Validate', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('XMLEscaping', <Element(name='XMLEscaping', type=<zeep.xsd.types.builtins.String object at 0x7f1bd8a4b320>)>), ('Shared', <Element(name='Shared', type=<zeep.xsd.dynamic_types.BrowseNodeLookupRequest object at 0x7f1bd8177e48>)>), ('Request', <Element(name='Request', type=<zeep.xsd.dynamic_types.BrowseNodeLookupRequest object at 0x7f1bd8177e48>)>)]
(Pdb) operation.input.body.type.elements[0][1].name
'MarketplaceDomain'
(Pdb) operation.input.body.type.elements[0][1].type.name
'string'
(Pdb) operation.input.body.type.elements[0][1].is_optional
True
Biedermeier answered 30/4, 2018 at 1:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.