Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

The Python Web services developer: The real world, Part 2

The Amazon.com Web APIs

Scott Archer, Software Architect, GlowingOrb, Inc.
Scott Archer is a software architect and co-founder of GlowingOrb, Inc., a software tools developer focusing on model-driven solutions and their integration into core business processes. Mr. Archer holds an M.Phil in Computational Molecular Biology from the University of Hong Kong. You can contact Mr. Archer at scott.archer at glowingorb.com.
Uche Ogbuji (uche@ogbuji.net), Principal Consultant, Fourthought, Inc.
Uche Ogbuji is a consultant and co-founder of Fourthought Inc., a software vendor and consultancy specializing in XML solutions for enterprise knowledge management applications. Fourthought develops 4Suite, open source platforms for XML middleware. You can contact Mr. Ogbuji at uche@ogbuji.net.

Summary:  This column has covered the major Python APIs available for Web services processing, demonstrating basic facilities and approaches through the use of simple clients and servers. All of this has laid the groundwork for utilizing real-world Web services. Part 1 of "The Real World", showed how to search the Web using Google's Web service API. Part 2 now takes that a step further and applies those tools and understanding to a more complex real-world Web service application. This issue will focus on the Web APIs for Amazon.com -- to which the authors will show you how to connect over SOAP so that you can programmatically search the huge catalogs of Amazon.com. As a bonus, the authors show you how to wrap the Web services code in a GUI.

Date:  21 Jan 2004
Level:  Intermediate

Activity:  15096 views
Comments:  

The Amazon Web Service API

Amazon.com, often called the world's largest store, has opened up their entire catalog of products for integrating directly into other Web sites using either SOAP or REST (XML over HTTP). Through their various partnership programs, associates and third-party vendors can earn referral fees, manage inventories, and search product information for competitive pricing. In order to show off some of the utility of the Amazon Web service API, we are going to show you how to build a GUI tool that lets you search for items of similar type and within several product categories in the Amazon.com catalog. As is typical of Web service providers, the examples and tools provided by the Amazon.com developer's kit do not include Python bindings or libraries. Not to worry, though -- working with the Amazon API through Python is very straightforward, perhaps even simpler than the usual languages and tools.


Setting up

For this project, our tool will be crafted using Python 2.3; SOAPpy version 0.10.4 will be used to proxy and marshall the API, and we'll use the wxPython toolkit for building our GUI. SOAPpy, since version 0.10.3, depends on the IEEE 754 floating point handler package fpconst (currently version 0.6.0). Hopefully, this will be included in a future Python release. See Resources for links to download these tools.

Go to the Amazon.com Web services site (see Resources) and download the free developer's kit and then register for a free developer's token, which you will need to pass in every method call to the Web service API. Before we get too deep into building our tool, let's test the Amazon API. Let's perform a keyword search on Amazon.com for books related to 'spotted owl'. Digging into the WSDL for the API, we find an appropriate method, the KeywordSearchRequest (see Listing 1).


Listing 1. Excerpt from the Amazon Web service API WSDL showing the KeywordSearchRequest.
  
...
<message name="KeywordSearchRequest">
    <!-- Messages for Amazon Web APIs -->
    <part name="KeywordSearchRequest" type="typens:KeywordRequest"/>
</message>

<message name="KeywordSearchResponse">
    <part name="return" type="typens:ProductInfo"/>
</message>

<xsd:complexType name="KeywordRequest">
<xsd:all>
    <xsd:element name="keyword" type="xsd:string"/>
    <xsd:element name="page" type="xsd:string"/>
    <xsd:element name="mode" type="xsd:string"/>
    <xsd:element name="tag" type="xsd:string"/>
    <xsd:element name="type" type="xsd:string"/>
    <xsd:element name="devtag" type="xsd:string"/>
    <xsd:element name="sort" type="xsd:string" minOccurs="0"/>
    <xsd:element name="locale" type="xsd:string" minOccurs="0"/>
    <xsd:element name="price" type="xsd:string" minOccurs="0"/>
</xsd:all>
</xsd:complexType>

<xsd:complexType name="ProductInfo">
<xsd:all>
    <xsd:element name="TotalResults" type="xsd:string" minOccurs="0"/>
    <!-- Total number of Search Results -->
    
    <xsd:element name="TotalPages" type="xsd:string" minOccurs="0"/>
    <!-- Total number of Pages of Search Results -->
    
    <xsd:element name="ListName" type="xsd:string" minOccurs="0"/>
    <!-- Listmania list name -->
    
    <xsd:element name="Details" type="typens:DetailsArray" minOccurs="0"/>
</xsd:all>
</xsd:complexType>
...  

The KeywordSearchRequest takes a single parameter, a structure containing the required parameters for a query. The obvious keyword parameter is first. Amazon.com will return results in pages of up to ten items, specified by the page parameter. The remaining parameters are not so obvious. Consulting the API documentation (yes, we have to look beyond the WSDL), we find that mode designates product-line, such as "books," "music," "dvd," or "kitchen" -- and the term used must match a list of specific categories. We will be using the US version of the mode parameter here. Alternatively, the API has UK, German, and Japanese versions of mode -- for example "books-uk," "books-de," and "books-jp," respectively.

Users in Amazon's Associates program can place their Associates ID in the tag parameter, potentially earning referral fees for purchases made in connection with the query. The details returned for each item can be set through the type parameter, allowed values being "lite" or "heavy." Finally (for US) is the devtag which must contain your developer's token. The remaining parameters are straightforward, but we won't use them here.

Upon successfully completing the query, a KeywordSearchResponse will be returned or, if the query found no matching items, a SOAP fault will be thrown that contains a message stating so. The actual response information is wrapped in a ProductInfo structure. Beyond the obvious TotalResults and TotalPages, we are interested in the Details component. The contents of the Details structure within the DetailsArray for each result depends on the mode product-line, as specified in the API documentation (see Resources).

So, let's write a simple app to perform a test query (see Listing 2).


Listing 2. amazon_soap_test.py - Simple Python code to test the Amazon Web service API.
   
#!/usr/bin/python

import SOAPpy

url = 'http://soap.amazon.com/schemas3/AmazonWebServices.wsdl'
proxy = SOAPpy.WSDL.Proxy(url)

# show methods retrieved from WSDL
print '%d methods in WSDL:' % len(proxy.methods) + '\n'
for key in proxy.methods.keys():
    print key
print

# search request
_query = 'spotted owl'
request = { 'keyword':  _query, 
            'page':     '1', 
            'mode':     'books', 
            'tag':      '', 
            'type':     'lite', 
            'devtag':   'INSERT YOUR TOKEN HERE' }

results = proxy.KeywordSearchRequest(request)

# display results
print 'Amazon.com search for  " ' + _query + ' "\n'
print 'total pages of results (max 10 per page): ' + str(results.TotalPages)
print 'total results: ' + str(results.TotalResults) + '\n'

# only show first result here
if (results.TotalResults > 0):
    print 'displaying first result (of %s):\n' %results.TotalResults
    details = results.Details[0]
    
    # we must use the _keys() method of SOAPpy Types.py for arrayType
    for key in details._keys():
        print key + ': ' + details[key]
    print

After importing the SOAPpy library, we use it to create a proxy for the Amazon Web service API from the WSDL published on their site. The WSDL.Proxy from SOAPpy offers a powerful tool for accessing APIs defined with WSDL. Before making any calls using the proxy, we print out a list of the methods exposed in the Amazon API. Calling SOAP methods is then as simple as constructing the appropriate parameters, as specified in the WSDL and described in the API documentation, and making the method call on the proxy object. In this case, we create a request structure, populating it with parameters appropriate to our query.

Note that we use the _keys() method from SOAPpy's Types.py module, as the details component of the results is a SOAP arrayType, which maps roughly to a Python dictionary.

There is a bug in version 0.10.4 of SOAPpy on trying to load a WSDL definition from a URL (see Listing 3).


Listing 3. Traceback of using SOAPpy to load WSDL from URL.
Traceback (most recent call last):
  File "./amazon_soap_test.py", line 21, in ?
    proxy = SOAPpy.WSDL.Proxy(url)
  File "/usr/lib/python2.3/site-packages/SOAPpy/WSDL.py", line 61, in __init__
    self.wsdl = reader.loadFromStream(stream)
  File "/usr/lib/python2.3/site-packages/SOAPpy/wstools/WSDLTools.py", 
  line 28, in loadFromStream
    wsdl.location = file.name
AttributeError: addinfourl instance has no attribute 'name'

Directly loading the WSDL from a URL requires a little quick-fix to the WSDL.py module in SOAPpy 0.10.4 -- shown in Listing 4 -- basically, you change the assignment call from reader.loadFromStream(stream) to reader.loadFromURL(wsdlsource).


Listing 4. Diff showing a quick-fix to WSDL.py in SOAPpy 0.10.4.
   
60,61c60
<                 stream = urllib.urlopen(wsdlsource)
<                 self.wsdl = reader.loadFromStream(stream)
---
>                 self.wsdl = reader.loadFromURL(wsdlsource)   

And voila! Our little test app works (see Listing 5). Of course, rather than modifying SOAPpy, we could also retrieve the WSDL file from Amazon.com using a browser (see Resources) and then load it from a local file -- this does work in the 0.10.4 version of SOAPpy.


Listing 5. Output of amazon_soap_test.py.
   
[scott@baal amazon_api]$ python amazon_soap_test.py
26 methods in WSDL:

BlendedSearchRequest
WishlistSearchRequest
ClearShoppingCartRequest
MarketplaceSearchRequest
BrowseNodeSearchRequest
SimilaritySearchRequest
SkuSearchRequest
SellerSearchRequest
SellerProfileSearchRequest
ActorSearchRequest
AsinSearchRequest
ListManiaSearchRequest
AuthorSearchRequest
GetShoppingCartRequest
PowerSearchRequest
ExchangeSearchRequest
DirectorSearchRequest
TextStreamSearchRequest
ModifyShoppingCartItemsRequest
KeywordSearchRequest
ArtistSearchRequest
UpcSearchRequest
GetTransactionDetailsRequest
AddShoppingCartItemsRequest
RemoveShoppingCartItemsRequest
ManufacturerSearchRequest

Amazon.com search for  " spotted owl "

total pages of results (max 10 per page): 6
total results: 52

displaying first result (of 52):

Asin: 0060248912
ImageUrlSmall: http://images.amazon.com/images/P/0060248912.01.THUMBZZZ.jpg
ProductName: There's an Owl in the Shower
ListPrice: $14.95
Availability: THIS TITLE IS CURRENTLY NOT AVAILABLE. 
If you would like to purchase this title, we recommend 
that you occasionally check this page to see if it has become available.
ReleaseDate: September, 1995
Catalog: Book
<SOAPpy.Types.typedArrayType at 1084200524>
Manufacturer: Harpercollins Juvenile Books
Url: http://www.amazon.com/exec/obidos/ASIN/0060248912/?dev-t=
D1V63VYIH286CL%26camp=2025%26link_code=sp1
UsedPrice: $1.25
ImageUrlMedium: http://images.amazon.com/images/P/0060248912.01.MZZZZZZZ.jpg
ImageUrlLarge: http://images.amazon.com/images/P/0060248912.01.LZZZZZZZ.jpg
OurPrice: $14.95

As you can see from the first part of Listing 5, there are 26 request methods defined in the WSDL for the Amazon API. Details on parameters and return structures can be found in the WSDL and the developer documentation from Amazon. We'll only be using the KeywordSearchRequest method in our examples here, although the code could certainly be extended to perform more advanced queries.


Now for something completely different ...

Now we can take our knowledge of the Amazon Web service API and do something useful with it. Python is often faulted by many people for not keeping up with the other SOAP-aware languages in the area of GUI creation. Actually, it's very easy to create powerful and truly cross-platform user interfaces with Python. To prove it, we'll create a simple little GUI application using Python and the wxPython UI library to perform keyword searches for books, music, and dvds. Figure 1 shows our GUI in action.


Figure 1. A simple GUI to search Amazon.com for books, music, and dvds.
Amazon.com SOAP Widget

After selecting the product line (also known as mode in Amazon speak), enter your keyword query (words separated by spaces) and hit the 'Search Amazon' button. The lights on your network hub will start blinking and after a few seconds, the 'Query Results' list box will fill. Select an item to see details and, if available, an image of the item. For simplicity, we only fetch the first 'page' of up to ten items. This could easily be extended.

Listing 6 shows the code for our application. Basically, it's our test application embedded in a user-interface and extended to search for music and dvds and to display details and images of found items.


Listing 6. amazon_widget.py - Python code for the Amazon.com Search GUI.
   
#!/usr/bin/python

# setup SOAP proxy
import SOAPpy
file = 'AmazonWebServices.wsdl'
amazon = SOAPpy.WSDL.Proxy(file)

# import the wxPython libraries
import wxPython.wx
from wxPython.wx import *
import urllib

# developer token for amazon api
DEV_TOKEN = 'INSERT YOUR TOKEN HERE'
 
# event ids
ID_SEARCH   = 100
ID_SELCHG   = 101

#------------------------------------------------------------------------------
# amazonWidgetFrame - the main window for our app
#------------------------------------------------------------------------------
class amazonWidgetFrame(wxFrame):

    def __init__(self, parent, ID, title):
        wxFrame.__init__(self, parent, ID, title, wxDefaultPosition, wxSize(500, 475))
        
        # add a status bar
        self.CreateStatusBar()
        self.SetStatusText("")
        
        # add other widgets to frame
        frame_box = wxBoxSizer(wxVERTICAL)
        
        # query part
        query_box = wxBoxSizer(wxHORIZONTAL)
        
        # set the mode (product line to search)
        mode_box = 
        wxStaticBoxSizer(wxStaticBox(self, -1, "Product Line"), wxVERTICAL)
        self.mode_books = 
        wxRadioButton(self, -1, "books", 
                                        wxDefaultPosition, wxDefaultSize, wxRB_GROUP)
        self.mode_music = wxRadioButton(self, -1, "music", 
                                        wxDefaultPosition, wxDefaultSize)
        self.mode_dvds = wxRadioButton(self, -1, "dvd", 
                                        wxDefaultPosition, wxDefaultSize)
        mode_box.Add(self.mode_books, 0, wxLEFT|wxRIGHT, 10)
        mode_box.Add(self.mode_music, 0, wxLEFT|wxRIGHT, 10)
        mode_box.Add(self.mode_dvds, 0, wxLEFT|wxRIGHT, 10)
        query_box.Add(mode_box, 0, wxALL, 5)
        
        # edit and button
        qtext_box = 
        wxStaticBoxSizer(wxStaticBox(self, -1, "Enter Query"), wxHORIZONTAL)
        self.search_text = 
        wxTextCtrl(self, -1, "python web", wxDefaultPosition, (200, -1))
        qtext_box.Add(self.search_text, 1, wxALL, 5)        
        qtext_box.Add(wxButton(self, ID_SEARCH, "Search Amazon"), 0, wxALL, 5)
        query_box.Add(qtext_box, 0, wxALL, 5)

        frame_box.Add(query_box, 0, wxBOTTOM, 5)

        # results part
        results_box = wxBoxSizer(wxHORIZONTAL)
        
        # details        
        details_box = 
        wxStaticBoxSizer(wxStaticBox(self, -1, "Query Results"), wxVERTICAL)
        self.results_list = 
        wxListBox(self, ID_SELCHG, wxDefaultPosition, (300,175),
                                 [], wxLB_ALWAYS_SB)
        details_box.Add(self.results_list, 0, wxALL, 5)        
        
        hbox = wxBoxSizer(wxHORIZONTAL)
        tbox = wxBoxSizer(wxVERTICAL)
        tbox.Add(wxStaticText(self, -1, "Author/Artist: "), 0, wxLEFT, 5)
        tbox.Add(wxStaticText(self, -1, ""), 0, wxLEFT, 5)
        hbox.Add(tbox, 0)
        hbox.Add(20,0,1)
        self.authors = wxListBox(self, -1, wxDefaultPosition, (200,65), [])
        hbox.Add(self.authors, 0, wxALIGN_RIGHT)
        details_box.Add(hbox, 0, wxBOTTOM, 5)        

        hbox = wxBoxSizer(wxHORIZONTAL)
        hbox.Add(wxStaticText(self, -1, "Release Date: "), 0, wxLEFT, 5)
        self.release_date = wxStaticText(self, -1, "                  ", 
        wxDefaultPosition, wxDefaultSize)
        hbox.Add(self.release_date, 0)
        details_box.Add(hbox, 0)        
        
        hbox = wxBoxSizer(wxHORIZONTAL)
        hbox.Add(wxStaticText(self, -1, "List Price: "), 0, wxLEFT, 5)
        self.list_price = wxStaticText(self, -1, "           ", 
        wxDefaultPosition, wxDefaultSize)
        hbox.Add(self.list_price, 0)
        details_box.Add(hbox, 0)        
        
        hbox = wxBoxSizer(wxHORIZONTAL)
        hbox.Add(wxStaticText(self, -1, "Amazon.com Price: "), 0, wxLEFT, 5)
        self.amazon_price = wxStaticText(self, -1, "           ", 
        wxDefaultPosition, wxDefaultSize)
        hbox.Add(self.amazon_price, 0)
        details_box.Add(hbox, 0, wxBOTTOM, 5)        

        results_box.Add(details_box, 0, wxALL, 5)        
        
        # image
        image_box = 
        wxStaticBoxSizer(wxStaticBox(self, -1, "Product Image"), wxHORIZONTAL)
        
        # inline class for drawing our product image 
        class amazonImagePanel(wxPanel):
            def __init__(self, parent, id, position, size):
                wxPanel.__init__(self, parent, id, position, size)
                self.parent = parent        
                self.image = None
                EVT_PAINT(self, self.OnPaint)

            def OnPaint(self, evt):
                dc = wxPaintDC(self)
                if self.image:
                    bitmap = wxBitmapFromImage(self.image)
                    dc.DrawBitmap(bitmap, 0, 0, false)
        
        self.image_canvas = amazonImagePanel(self, -1, wxDefaultPosition, (130,150))
        image_box.Add(self.image_canvas, 1, wxALL, 5)
                
        results_box.Add(image_box, 0, wxALL, 5)
        
        # finally...
        frame_box.Add(results_box, 0, wxTOP, 5)
        
        # add main sizer
        self.SetSizer(frame_box)
        
        # set event handlers
        EVT_BUTTON(self, ID_SEARCH, self.OnSearch)
        EVT_LISTBOX(self, ID_SELCHG, self.OnSelectionChange)

        
    #---------------------------------------------------------------------
    # build and call query - if returns OK, set the GUI fields
    #---------------------------------------------------------------------
    def OnSearch(self, event):
        # fetch mode and query text
        query = self.search_text.GetValue()
        self.mode = 'books'
        if self.mode_music.GetValue():
            self.mode = 'music'
        elif self.mode_dvds.GetValue():
            self.mode = 'dvd'        
        request = { 'keyword': query, 'page': '1', 'mode': self.mode, 
                    'tag': '', 'type': 'lite', 'devtag':  DEV_TOKEN }

        # do the query
        try:
            tmp_results = amazon.KeywordSearchRequest(request)
        except SOAPpy.faultType:
            self.SetStatusText('There were no exact matches for the search')
        else:                    
            self.results = tmp_results
            self.SetStatusText('total results: ' + str(self.results.TotalResults) +
                               '  (only showing first 10)')
            # load up results box             
            items = []
            images = []
            for detail in self.results.Details:
                items.append(detail['ProductName'])
                images.append(detail['ImageUrlMedium'])
            self.results_list.Set(items)
            self.results_list.SetFirstItem(0)
            
            # clear displayed image
            self.authors.Set([])
            self.release_date.SetLabel('')                
            self.list_price.SetLabel('')
            self.amazon_price.SetLabel('')
            self.image_canvas.image = None
            self.image_canvas.Refresh()

            # cache images
            self.loadImages(images)
            
            
    #---------------------------------------------------------------------
    # whenever the product selection changes, update other fields
    #---------------------------------------------------------------------
    def OnSelectionChange(self, event):
        # fetch selected item from results
        sel = self.results_list.GetSelections()
        detail = self.results.Details[sel[0]]
        
        # set the author/artist field
        items = []
        if self.mode == 'books':
            if 'Authors' in detail._keys():
                authors = detail['Authors']
                for author in authors:
                    items.append(author)
                self.authors.Set(items)
        elif self.mode == 'music':
            if 'Artists' in detail._keys():
                authors = detail['Artists']
                for author in authors:
                    items.append(author)
                self.authors.Set(items)
        elif self.mode == 'dvd':
            # only in 'heavy' search type
            if 'Directors' in detail._keys():
                authors = detail['Directors']
                for author in authors:
                    items.append(author)
                self.authors.Set(items)

        # set release date and pricing fields
        if 'ReleaseDate' in detail._keys():
            self.release_date.SetLabel(detail['ReleaseDate'])                
        if 'ListPrice' in detail._keys():
            self.list_price.SetLabel(detail['ListPrice'])
        if 'OurPrice' in detail._keys():
            self.amazon_price.SetLabel(detail['OurPrice'])
        
        # fetch image
        image_name = 'image_tmp_' + str(sel[0]) + '.jpg'
        self.image_canvas.image = wxImage(image_name)
        self.image_canvas.Refresh()
        
        
    #---------------------------------------------------------------------
    # retrieve from amazon.com and temporarily save images
    #---------------------------------------------------------------------
    def loadImages(self, images):
        i = 0
        for url in images:
            image_name = 'image_tmp_' + str(i) + '.jpg'
            pic = urllib.urlopen(url)
            img = pic.read()
            pic.close()
            f = open(image_name,'wb')
            f.write(img)
            f.close()
            i = i+1

                
#------------------------------------------------------------------------------
# simple wxApp
#------------------------------------------------------------------------------
class amazonWidgetApp(wxApp):

    def __init__(self, parent):
        wxApp.__init__(self, parent)

        
    def OnInit(self):
        # start GUI
        frame = amazonWidgetFrame(NULL, -1, "Amazon.com SOAP Widget")
        frame.Show(true)
        self.SetTopWindow(frame)
        return true


#------------------------------------------------------------------------------
# run this when module gets called
#------------------------------------------------------------------------------
if __name__ == '__main__':
       
    # must call this for wxImage to load JPEGs
    wx.wxInitAllImageHandlers()
    
    app = amazonWidgetApp(0)
    app.MainLoop()

We chose to use the wxPython library rather than TkInter or other smaller GUI toolkits available for Python. It's basically a Python wrapper around the wxWindows library, a very fast and complete cross-platform windowing framework. For illustrative purposes in creating the GUI, we did the control layout by hand, rather than use one of the available wxWindows WYSIWYG layout tools, such as wxDesigner. This makes up the bulk of the code in the amazonFrame.__init__( ) method. We made extensive use of the wxBoxSizer control to manage the layout of the interesting controls.

One thing to note is the requirement to import SOAPpy and create our proxy before importing wxPython, due to a threading issue with the wxWindows event loop. Also, we load the WSDL from local file rather than from the Amazon.com site for start-up performance reasons.

When the application is run, amazonWidgetApp is created and MainLoop( ) is called to start the event loop. Note the class amazonImagePanel defined inline on the __init__( ) method of amazonWidgetFrame. We define two main event handlers: OnSearch( ) to handle pressing the "Search Amazon" button and OnSelectionChange( ) to handle selection of items in the "Query Results" list box.

OnSearch( ) gets the mode setting from the GUI, builds a request structure, and then calls the KeywordSearchRequest SOAP method on the amazon SOAPpy proxy. The SOAP method call is performed within a try - except block, which is generally good style, but here we do it specifically to catch a SOAP.faultType exception. We should check the exception type, but here we'll just assume it means that no results were found. If all is OK, we keep the results in the self.results attribute of amazonWidgetFrame, populate the "Query Results" list box, and fetch from Amazon.com all of the images referenced in the details of the results.

OnSelectionChange( ) fetches the appropriate details from self.results and populates the respective components on amazonWidgetFrame, finally loading the right image and forcing a repaint of the canvas.


Resources

About the authors

Scott Archer is a software architect and co-founder of GlowingOrb, Inc., a software tools developer focusing on model-driven solutions and their integration into core business processes. Mr. Archer holds an M.Phil in Computational Molecular Biology from the University of Hong Kong. You can contact Mr. Archer at scott.archer at glowingorb.com.

Uche Ogbuji

Uche Ogbuji is a consultant and co-founder of Fourthought Inc., a software vendor and consultancy specializing in XML solutions for enterprise knowledge management applications. Fourthought develops 4Suite, open source platforms for XML middleware. You can contact Mr. Ogbuji at uche@ogbuji.net.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services
ArticleID=11874
ArticleTitle=The Python Web services developer: The real world, Part 2
publish-date=01212004
author1-email=
author1-email-cc=
author2-email=uche@ogbuji.net
author2-email-cc=