Did you know? Programmers convert coffee to code.

If you like my articles, sponsor me a coffee.

Python and the GAE — making things Pythonic

After an introduction into “hardcore” SOAP development in Java on the Google App Engine (GAE) I’ll look at the Python side and make a Python project to compare between the two versions.

This article is interesting in two parts: not just because I’ll write about communicating with a SOAP service from Python — I’ll make this from the GAE. These two are currently unknown territories for me. Let’s see what I reach at the end of the article.

First of all, I do not want  to introduce how to install and run the GAE locally. For this they have a great tutorial here. Please follow the guidelines there to get a system up and running. The code for this article I’ll provide at my GitHub account.

UTF-8

Yes this is always a good point in the software development: ASCII and “only English” is out, UTF-8 (or UTF-16) is in. In a global community you have to provide websites and web applications in other languages than English too. So let’s see, what does it mean for the GAE and Python development.

To get an example of this, I’ll create a “Hello World” with Chinese symbols although I do not speak Chinese. For the example I’ll use the helloworld from the Python GAE tutorial and replace the output text with “你好世界“:

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write('你好世界')

application = webapp2.WSGIApplication([
    ('/', MainPage),
], debug=True)

Then after starting the development server and locating the application at localhost:8080 you can see only a blank screen. The Log shows the reason for this:

ERROR    2014-06-21 09:26:10,514 wsgi.py:262]
Traceback (most recent call last):
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 239, in Handle
    handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 298, in _LoadHandler
    handler, path, err = LoadObject(self._handler)
  File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/runtime/wsgi.py", line 84, in LoadObject
    obj = __import__(path[0])
  File "/Users/GHajba/github/python/GAE/helloworld/helloworld.py", line 6
SyntaxError: Non-ASCII character '\xe4' in file /Users/GHajba/github/python/GAE/helloworld/helloworld.py on line 6, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
INFO     2014-06-21 09:26:10,519 module.py:639] default: "GET / HTTP/1.1" 500 -

Not good, is it? Well, line 10 (highlighted above) explains at the end what the cause is: there’s no encoding specified in the Python source file. And shows an URL to a PEP (Python Enhancement Proposal). However the URL is not accurate, you can look up the PEP0263 here.

So it is needed to add the file encoding to the script. I’ll insert it as line 1 just right before the import statement:

# coding=utf-8
import webapp2

# rest of the code omitted

If we reload the page we get some characters

你好世界

instead of a blank screen however this is not exactly Chinese 🙂 However it is, only the string is not unicode, this we can change with adding the “u” in front of the string as follows:

self.response.write(u'你好世界')

If we reload the site we get our expected Chinese “Hello World”.

So this was the introduction to UTF-8 at the Google App Engine.

SOAP

Now back to the main topic of SOAP with Python and GAE. Unfortunately there is no out-of-the-box support for SOAP.  As I told in the previous related post “SOAP is out, REST is in.”. I am glad that we had to create the SOAP bindig in Java. But it is not impossible to add a third party library (such as Suds) to your GAE application.

I tried to install some SOAP libraries to my Python (Version 2.7) and I ended with Suds which is a newer (2010!) SOAP client library where you can add custom headers to your request — as it is required for our service endpoint.

However after adding the header I get an “Invalid XML” as response from the server and I guess it is because the namespaces in the XML (the implementation on the other side has to be something “hardcore” implementation too).  But this is only a pain in the neck, so I’ll skip the part where I use the SOAP endpoint which we had to use in the Java version at work and add a public endpoint to this article — to get something valuable too.

As mentioned before I’ll stuck with suds, however the last valuable commit has been done in 2010. This means that Python buried SOAP and is open for REST services only.

I’ll use the Weather service of cdyne (http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL). The code is simple: I’ll create the service, look at the endpoints and call one or two os them.

Example

And here it comes the simple SOAP request to the Weather service.

Note: I do not know if suds has any prerequisites (as PyXML for example), because I installed those for SOAPpy previously so I cannot tell.

Creating a suds client is easy:

from suds.client import Client

client = Client('http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL')
print client

This code snippet creates a SOAP client from the WSDL which you can use to send requests to the server.  With “print” you can display the ports, types, and methods of the service endpoint:

Suds ( https://fedorahosted.org/suds/ )  version: 0.4 GA  build: R699-20100913
Service ( Weather ) tns="http://ws.cdyne.com/WeatherWS/"
   Prefixes (1)
      ns0 = "http://ws.cdyne.com/WeatherWS/"
   Ports (2):
      (WeatherSoap)
         Methods (3):
            GetCityForecastByZIP(xs:string ZIP, )
            GetCityWeatherByZIP(xs:string ZIP, )
            GetWeatherInformation()
         Types (8):
            ArrayOfForecast
            ArrayOfWeatherDescription
            Forecast
            ForecastReturn
            POP
            WeatherDescription
            WeatherReturn
            temp
      (WeatherSoap12)
         Methods (3):
            GetCityForecastByZIP(xs:string ZIP, )
            GetCityWeatherByZIP(xs:string ZIP, )
            GetWeatherInformation()
         Types (8):
            ArrayOfForecast
            ArrayOfWeatherDescription
            Forecast
            ForecastReturn
            POP
            WeatherDescription
            WeatherReturn
            temp

Let’s call a current weather on… Chicago, Illinois. Do not ask me why I’ve chosen Chicago.

chicago = client.service.GetCityWeatherByZIP(60656)
print chicago

Calling a method of the endpoint only takes to call the “service.nameOfTheEndpointMethod” on the client object. If you forget to pass a parameter or type in a wrong parameter (in the current example a ZIP) you get an according response from the server. If you call a service endpoint which does not exist you’ll get an exception raised from suds.

A sample result could be:

(WeatherReturn){
   Success = True
   ResponseText = "City Found"
   State = "IL"
   City = "Chicago"
   WeatherStationCity = "Maywood"
   WeatherID = 14
   Description = "Cloudy"
   Temperature = "27"
   RelativeHumidity = "63"
   Wind = "S9"
   Pressure = "29.95F"
   Visibility = None
   WindChill = None
   Remarks = None
}

 Installing 3rd-party libraries to your GAE application

This section describes how to add something to your application if it is not provided by the GAE. I’ll do this with Suds — however with some trick.

I’ve spent some time figuring out how to import a third-party library into your GAE application and finally I found that the simplest way is to copy the library to your application and import it. So I’ve created a “lib” folder in in the application’s folder and copied the suds folder into this “lib” folder. The only problem with this was that Suds caches the WSDL on the file system — which is prohibited by the GAE. So there is the need of a workaround and I found it in a GitHub repository: appengine-suds. This version of Suds does not cache the WSDL on the file system so it can be used with the GAE.

The application is deployed here.

The GUI is made with Jinja2 an HTML template engine for Python. It is easy to use and it comes with GAE installed. And it makes a lot of coding removable (just look at my commits of the project).

Well I had to admit that this API does not seem to work. Every time I ask for the weather in Chicago I always get 79°F and Cloudy. I guess this is a half-broken API because it cannot be always the same weather in Chicago. And it shall not work for other ZIPs. Perhaps it is really true that SOAP is dead?

Source code

The source code is available at my GitHub repository (as always) here.

GHajba
 

Senior developer, consultant, author, mentor, apprentice.

I love to share my knowledge and insights what I achieve through my daily work which is not trivial — at least not for me.

Click Here to Leave a Comment Below 0 comments
%d bloggers like this: