26

Is there any example code of a cpython (not IronPython) client which can call Windows Communication Foundation (WCF) service?

bhadra
  • 12,887
  • 10
  • 54
  • 47
  • Can we safely assume that you're trying to call a SOAP- or REST-based service over HTTP? I doubt short of some sort of COM bridge between Python and a WCF client would be able to successfully call a TCP WCF service. – technomalogical Nov 09 '08 at 21:46

7 Answers7

19

I used .

from suds.client import Client

print "Connecting to Service..."
wsdl = "http://serviceurl.com/service.svc?WSDL"
client = Client(wsdl)
result = client.service.Method(variable1, variable2)
print result

That should get you started. I'm able to connect to exposed services from WCF and a RESTful layer. There needs to be some data massaging to help do what you need, especially if you need to bind to several namespaces.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
r3nrut
  • 1,045
  • 2
  • 11
  • 28
  • 1
    No module named suds.client, where do you get suds from? – Itay Levin Nov 24 '14 at 12:35
  • @ItalyLevin you need to use pip. – S. Dixon Mar 18 '15 at 15:26
  • I got the No module named 'client' error when trying to install with pip on Windows 7 – user2258651 Aug 09 '17 at 13:39
  • If you have python installed and have used pip to install the suds package, you should be able to import Client. If you continue to have issues you can attempt to run debug. Docs found here: https://webkul.com/blog/python-suds-client/ For full debugging the output use the following methods. import logging logging.basicConfig(level=logging.INFO) logging.getLogger(‘suds.client’).setLevel(logging.DEBUG) logging.getLogger(‘suds.transport’).setLevel(logging.DEBUG) logging.getLogger(‘suds.xsd.schema’).setLevel(logging.DEBUG logging.getLogger(‘suds.wsdl’).setLevel(logging.DEBUG) – r3nrut Nov 06 '17 at 15:56
10

TL;DR: For wsHttpBinding (SOAP 1.2) use zeep


In case someone is having trouble using suds (or suds-jurko for that matter) with WCF and wsHttpBinding (which is SOAP 1.2):

  • suds is pretty much dead (can't even pip install it on python 3)
  • suds-jurko seems kind-of dead. The 0.6 release has a very annoying infinite recursion bug (at least on the WSDL exposed by our service) which is fixed in the tip but that's not released and it's been 1.5years (at time of this writing in Feb'17) since the last commit.
    It works on python 3 but doesn't support SOAP 1.2. Sovetnikov's answer is an attempt to get it working with 1.2 but I haven't managed to make it work for me.
  • zeep seems to be the current way to go and worked out of the box (I'm not affiliated with zeep, it just works for me and I spent several hours banging my head against a brick wall trying to make suds work). For zeep to work, the WCF service host configuration must include <security mode="None"/> under the wsHttpBinding node Actually zeep seems to support username and signature (x509) based WS-SE but I haven't tried that so can't speak to any problems around it.
ChrisWue
  • 18,612
  • 4
  • 58
  • 83
  • can you add an example for how to implement wsHttpBinding, stuck for hours can't implement it. – harshil9968 Aug 31 '17 at 18:03
  • @harshil9968: Not 100% sure what you're after. Instantiating a zeep client is as simple as `client = zeep.Client('http://computerip:port/myservice/?wsdl')`. How to create a WCF service implementation is a bit out of scope for this question – ChrisWue Sep 01 '17 at 01:51
  • this creates with http-binding, not wshttp-binding. I'm getting error from the client because of that. – harshil9968 Sep 01 '17 at 08:42
  • Is there anyway around your last bullet point? I'm trying to consume a vendor's service and I don't think they'll be willing to change it just for us. – Harabeck Feb 01 '18 at 16:11
  • @Harabeck - Looks like support for WSSE is available now (http://docs.python-zeep.org/en/master/wsse.html) – ChrisWue Apr 11 '18 at 22:34
8

WCF needs to expose functionality through a communication protocol. I think the most commonly used protocol is probably SOAP over HTTP. Let's assume that's what you're using then.

Take a look at this chapter in Dive Into Python. It will show you how to make SOAP calls.

I know of no unified way of calling a WCF service in Python, regardless of communication protocol.

Dan
  • 3,665
  • 1
  • 31
  • 39
2

Just to help someone to access WCF SOAP 1.2 service with WS-Addressing using suds. Main problem is to inject action name in every message.

This example for python 3 and suds port https://bitbucket.org/jurko/suds.

Example uses custom authentification based on HTTP headers, i leave it as is.

TODO: Automatically get api_direct_url from WSDL (at now it is hard coded).

from suds.plugin import MessagePlugin
from suds.sax.text import Text
from suds.wsse import Security, UsernameToken
from suds.sax.element import Element
from suds.sax.attribute import Attribute
from suds.xsd.sxbasic import Import

api_username = 'some'
api_password = 'none'

class api(object):
    api_direct_url = 'some/mex'
    api_url = 'some.svc?singleWsdl|Wsdl'

    NS_WSA = ('wsa', 'http://www.w3.org/2005/08/addressing')

    _client_instance = None
    @property
    def client(self):
        if self._client_instance:
            return self._client_instance
        from suds.bindings import binding
        binding.envns = ('SOAP-ENV', 'http://www.w3.org/2003/05/soap-envelope')

        api_inst = self
        class _WSAPlugin(MessagePlugin):
            def marshalled(self, context):
                api_inst._marshalled_message(context)

        self._client_instance = Client(self.api_url,
                             plugins=[_WSAPlugin()],
                             headers={'Content-Type': 'application/soap+xml',
                                      'login':api_username,
                                      'password': api_password}
                             )
        headers = []
        headers.append(Element('To', ns=self.NS_WSA).setText(self.api_direct_url))
        headers.append(Element('Action', ns=self.NS_WSA).setText('Blank'))
        self._client_instance.set_options(soapheaders=headers)

        cache = self._client_instance.options.cache
        cache.setduration(days=10)
        return self._client_instance

    def _marshalled_message(self, context):
        def _children(r):
            if hasattr(r, 'children'):
                for c in r.children:
                    yield from _children(c)
                    yield c
        for el in _children(context.envelope):
            if el.name == 'Action':
                el.text = Text(self._current_action)
                return

    _current_action = None
    def _invoke(self, method, *args):
        try:
            self._current_action = method.method.soap.action.strip('"')
            return method(*args)
        finally:
            self._current_action = None

    def GetRequestTypes(self):
        return self._invoke(self.client.service.GetRequestTypes)[0]

    def GetTemplateByRequestType(self, request_type_id):
        js = self._invoke(self.client.service.GetTemplateByRequestType, request_type_id)
        return json.loads(js)

    def GetRequestStatus(self, request_guid):
        return self._invoke(self.client.service.GetRequestStatus, request_guid)

    def SendRequest(self, request_type_id, request_json):
        r = json.dumps(request_json, ensure_ascii=False)
        return self._invoke(self.client.service.SendRequest, request_type_id, r)
Sovetnikov
  • 439
  • 4
  • 8
1

if you need binary serialized communication over tcp then consider implementing solution like Thrift.

mamu
  • 12,184
  • 19
  • 69
  • 92
1

I do not know of any direct examples, but if the WCF service is REST enabled you could access it through POX (Plain Old XML) via the REST methods/etc (if the service has any). If you are in control of the service you could expose endpoints via REST as well.

Donn Felker
  • 9,553
  • 7
  • 48
  • 66
0

Even if there is not a specific example of calling WCF from Python, you should be able to make a fully SOAP compliant service with WCF. Then all you have to do is find some examples of how to call a normal SOAP service from Python.

The simplest thing will be to use the BasicHttpBinding in WCF and then you can support your own sessions by passing a session token with each request and response.

Wayne Bloss
  • 5,370
  • 7
  • 50
  • 81