Comparison of ruby and python line by line (3/3)

This post is the last part of this comparison. I am going to demonstrate how to develop XML-RPC server and client in Ruby and Python. As I said earlier, I don’t know much about Ruby. Below ruby codes are based on an article in Linux Journal #144. I just tried to develop similar program in Python. There are 2 parts; server and client. Let me start from the server.

  1. Specify executable
    Ruby
    #!/usr/bin/ruby
    Python
    #!/usr/bin/python
  2. Import modules
    Ruby
    require ’net/http’
    require ’rexml/document’
    require ’xmlrpc/server’
    Python
    import sys
    import re
    import urllib
    from xml.dom.ext.reader import Sax2
    from xml.dom.ext import Print
    from xml import xpath
    from SimpleXMLRPCServer import SimpleXMLRPCServer
  3. Initialize regular expressions
    Ruby
    not_in_collection_re = /class="yourEntryWouldBeHereData"/ix
    on_shelf_re = /CHECK SHELF/ix
    checked_out_re = /DUE /ix
    Python
    not_in_collection_re = re.compile(r’class="yourEntryWouldBeHereData"’,re.I)
    on_shelf_re = re.compile(r’CHECK SHELF’,re.I)
    checked_out_re = re.compile(r’DUE ’,re.I)
  4. Declare XML-RPC server
    Ruby
    server = XMLRPC::Server.new(8080)
    Python
    server = SimpleXMLRPCServer((’’,8080))
  5. Add an handler
    Ruby
    server.add_handler(name="atf.books",
                       signature=[’array’,’array’]) do |isbns|
     
      output = []
     
      isbns.each do |isbn|
     
        isbn_output = {’ISBN’ => isbn}
     
        if not isbn.match(/[0-9xX]{10}/)
          isbn_output[’message’] = "ISBN #{isbn} is invalid."
          output << isbn_output
          next
        end
     
        amazon_params = {’Service’ => ’AWSECommerceService’,
                         ’Operation’ => ’ItemLookup’,
                         ’AWSAccessKeyId’ => ’XXXXXXXXXXXXXXXXXXXXXXX’,
                         ’ItemId’ => isbn,
                         ’ResponseGroup’ => ’Medium,OfferFull’,
                         ’MerchantId’ => ’All’}.map{|key,value|"#{key}=#{value}"}.join("&")
     
        amazon_response = Net::HTTP.get_response(’webservices.amazon.com’,
                                                 ’/onca/xml?’ << amazon_params)
     
        xml = REXML::Document.new(amazon_response.body)
     
        new_price = xml.root.elements["Items/Item/OfferSummary/LowestNewPrice/FormattedPrice"]
        if new_price.nil?
          isbn_output[’New’] = "None available"
        else
          isbn_output[’New’] = "#{new_price.text}"
        end
     
        used_price = xml.root.elements["Items/Item/OfferSummary/LowestUsedPrice/FormattedPrice"]
        if used_price.nil?
          isbn_output[’Used’] = "None available"
        else
          isbn_output[’Used’] = "#{used_price.text}"
        end
     
        collectible_price = xml.root.elements["Items/Item/OfferSummary/LowestCollectiblePrice/FormattedPrice"]                   
        if collectible_price.nil?
          isbn_output[’Collectible’] = "None available"
        else
          isbn_output[’Collectible’] = "#{collectible_price.text}"    end
     
        response = Net::HTTP.get_response(’catalog.skokielibrary.info’,
                                        "/search~S4/i?SEARCH=#{isbn}")
          
        if not_in_collection_re.match(response.body)
          isbn_output[’Library’] = "Library: Not in the Skokie collection."
        elsif checked_out_re.match(response.body)
          isbn_output[’Library’] = "Checked out."
        elsif on_shelf_re.match(response.body)
          isbn_output[’Library’] = "On the shelf."
        else
          isbn_output[’Library’] = "Unparseable response."
        end
     
        output << isbn_output
      end
     
      output
    end
    Python
    def atf_books(isbns):
        output = []
     
        for isbn in isbns:
            isbn_output = {’ISBN’: isbn} 
            if not re.match(r’[0-9xX]{10}’,isbn):
                isbn_output[’message’] = ’ISBN %s is invalid.’ % isbn
                output.append(isbn_output)
                continue
     
            amazon_params = {
                ’Service’: ’AWSECommerceService’,
                ’Operation’: ’ItemLookup’,
                ’AWSAccessKeyId’: ’XXXXXXXXXXXXXXXXXXXXXXX’,
                ’ItemId’: isbn,
                ’ResponseGroup’: ’Medium,OfferFull’,
                ’MerchantId’: ’All’,
            }
     
            amazon_response = urllib.urlopen(’http://webservices.amazon.com/onca/xml’,urllib.urlencode(amazon_params))
            xml = Sax2.FromXmlStream(amazon_response).documentElement
     
            try:
                new_price = xpath.Evaluate(’Items/Item/OfferSummary/LowestNewPrice/FormattedPrice’,xml)[0]
                isbn_output[’New’] = new_price.childNodes[0].nodeValue
            except:
                isbn_output[’New’] = ’None available’
     
            try:
                used_price = xpath.Evaluate(’Items/Item/OfferSummary/LowestUsedPrice/FormattedPrice’,xml)[0]
                isbn_output[’Used’] = used_price.childNodes[0].nodeValue
            except:
                isbn_output[’Used’] = ’None available’
     
            try:
                collectible_price = xpath.Evaluate(’Items/Item/OfferSummary/LowestCollectiblePrice/FormattedPrice’,xml)[0]
                isbn_output[’Collectible’] = collectible_price.childNodes[0].nodeValue
            except:
                isbn_output[’Collectible’] = ’None available’ 
    
            response = urllib.urlopen(’http://catalog.skokielibrary.info/search~S4/i’,’SEARCH=%s’ % isbn)
            body = response.read() 
            if not_in_collection_re.search(body):
                isbn_output[’Library’] = ’Library: Not in the Skokie collection.’
            elif checked_out_re.search(body):
                isbn_output[’Library’] = ’Checked out.’
            elif on_shelf_re.search(body):
                isbn_output[’Library’] = ’On the shelf.’
            else:
                isbn_output[’Library’] = ’Unparseable response’
            
            output.append(isbn_output) 
        return output
    
    server.register_function(atf_books,’atf.books’)
  6. Serve clients
    Ruby
    server.serve
    Python
    server.serve_forever()

You might notice a big difference in the 5th step. Ruby allows to write in-line function very easy. To do the same in Python, the procedure must be written in lambda form. I don’t want to use lambda to implement very long code like thist. Instead, I decided to wrap the routine in a function. And again in 6th step, you might find that a method was called without (). I think this is very confusing feature. How can I know the statement is just to refer to the method or to call the method. Then below is the client.

  1. Specify executable
    Ruby
    #!/usr/bin/ruby
    Python
    #!/usr/bin/python
  2. Import modules
    Ruby
    require ’xmlrpc/client’
    Python
    import sys
    from xmlrpclib import ServerProxy
  3. Initialize arguments
    Ruby
    isbns = ARGV
    Python
    isbns = sys.argv[1:]
  4. Declare XML-RPC client
    Ruby
    server = XMLRPC::Client.new2("http://127.0.0.1:8080/",nil,120)
    Python
    server = ServerProxy(’http://127.0.0.1:8080’)
  5. Call a method
    Ruby
    begin
      results = server.call("atf.books",isbns)
    rescue XMLRPC::FaultException => e
      puts "Error:"
      puts e.faultCode
      puts e.faultString
    end
    Python
    try:
        results = server.atf.books(isbns)
    except Exception,why:
        print why
        sys.exit()
  6. Loop through the results
    Ruby
    results.each do |result|
      result.each do |key,value|
        if key == ’ISBN’:
          puts "ISBN: #{value}\n"
        else
          puts "\t#{key}: #{value}\n"
        end
      end
    end
    Python
    for result in results:
        print ’ISBN: %s’ % result[’ISBN’]
        for key,value in result.items():
            if key != ’ISBN’:
                print ’\t%s: %s’ % (key,value)

Can you notice significant difference between Ruby and Python? Ruby’s hash does preserve order but Python doesn’t. So I have to change some logic in the 6th step to show reasonable output.

Don’t forget to read part 1, 2, 3.

Technorati Tags: , , , , , , ,

Post new comment