4

I would like some advice. I have encountered the following error in Python 2.6:

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    s.Search(query)
  File "/usr/lib/python2.6/xmlrpclib.py", line 1199, in __call__
  return self.__send(self.__name, args)
  File "/usr/lib/python2.6/xmlrpclib.py", line 1489, in __request
  verbose=self.__verbose
  File "/usr/lib/python2.6/xmlrpclib.py", line 1253, in request
  return self._parse_response(h.getfile(), sock)
  File "/usr/lib/python2.6/xmlrpclib.py", line 1392, in _parse_response
  return u.close()
  File "/usr/lib/python2.6/xmlrpclib.py", line 838, in close
  raise Fault(**self._stack[0])
  Fault: <Fault 1: "<type 'exceptions.TypeError'>:dictionary key must be string">

My code is serving up part of a mini search engine using Django. In Python 3 everything runs like a dream, but Django is not available for Python 3 so I need to backdate my code, which is where the problem comes from.

My code (client.py):

# -*- coding: utf-8 -*-
from __future__ import unicode_literals # This was suggested elsewhere
import xmlrpclib

s = xmlrpclib.ServerProxy('http://localhost:11210')
data = s.Search('מלאכא') # tried prefixing with 'u'
print data

My code (Server.py):

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import pickle, sys, xmlrpclib
from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
from collections import defaultdict

docscores = pickle.load(open("docscores.pkl", "rb"))
print ("Everything loaded. No errors.")

# Restrict to a particular path.
class RequestHandler(SimpleXMLRPCRequestHandler):
    rpc_paths = ('/RPC2',)

# Create server
server = SimpleXMLRPCServer(("localhost", 11210), requestHandler=RequestHandler)

server.register_introspection_functions()

def Search(query):
    results = docscores[query]
    return results

server.register_function(Search, 'Search')

# Run the server's main loop
server.serve_forever()

As you can see it's pretty simple, but I get a 'dictionary key must be string' when parsing the unicode string across to the server from the client. However, the server seems quite happy and produces the following feedback, which shows it has accessed my pickled dictionary (returning a doc number and count of the ngram):

{160: 3, 417: 1, 35: 1, 133: 1, 376: 1, 193: 1, 380: 1, 363: 1, 364: 1, 126: 1, 47: 1, 145: 1, 147: 1, 382: 1, 246: 3, 121: 4, 440: 1, 441: 1, 444: 1, 280: 1}
 localhost.localdomain - - [09/Aug/2011 13:32:23] "POST /RPC2 HTTP/1.0" 200 -

If I do: type(query) The result is:

I've also tried reload(sys), prefixing u'unicode_string', u"".join(unicode_string), and query.decode('utf-8')`, but still get this error, or end up with more errors relating to unicode/ascii decoding.

Does anyone have any ideas how I can get around this error? Or is there an alternative to XMLPRPCServer for serving data between a server instance and a client in Python 2.6?

Many thanks in advance.

Martyn
  • 1,107
  • 1
  • 10
  • 12

1 Answers1

6

The doc of xmlrpclib state that for a python dictionary to be marshaled through XML the keys should be strings:

A Python dictionary. Keys must be strings, values may be any conformable type. Objects of user-defined classes can be passed in; only their dict attribute is transmitted.

So you should change your server Search method to return a dictionary with strings as keys:

def Search(query):
    results = docscores[query]
    # I believe results is now a dictionary in the form {<integer>: <integer>}
    return dict((str(key), value) for key, value in results.items())
mouad
  • 67,571
  • 18
  • 114
  • 106
  • Thank you @mouad, that solved the problem, I was approaching it from the wrong side of the server. Thank you also for a very speedy response! :-) – Martyn Aug 09 '11 at 15:21