Last Updated: February 25, 2016
·
1.17K
· xdmnl

Using Pypi XML-RPC API with Ruby

I had to play with the Pypi API today. Unfortunately, their JSON API is too limited for what I wanted to do.
If you need to browse, search or even get a list of the released versions of a package, you need to use their XML-RPC API.

Quite simple? Not so fast!

In Ruby, a XML-RPC request would be something like:

require 'xmlrpc/client'
client = XMLRPC::Client.new_from_uri('https://testpypi.python.org/pypi')
result = client.call(:list_packages)
puts result.inspect

But the code above raises RuntimeError: Wrong content-type (received 'text/html' but expected 'text/xml') with the HTML content of the page https://testpypi.python.org/pypi.

Spoiler: It's all about HTTP headers.

As the Pypi documentation says:

XML-RPC requests are detected by CONTENT_TYPE=text/xml variable in CGI environment and processed by rpc.RequestHandler().__call__().

And if we look at Pypi source code, a XML-RPC requests requires that CONTENT_TYPE should be exactly text/xml.

Unfortunately for us, XMLRPC::Client sets the Content-Type header to text/xml; charset=utf-8 which doesn't match Pypi's condition.

Let's update our code:

require 'xmlrpc/client'
client = XMLRPC::Client.new_from_uri('https://testpypi.python.org/pypi')
client.http_header_extra = { 'Content-Type' => 'text/xml' }
result = client.call(:list_packages)
puts result.inspect

And here we are, a nice array of Python package names!

Note: In this example we use testpypi.python.org which is the alternate server for test purpose. In production, you'll want to use pypi.python.org.

1 Response
Add your response

Thanks. I would have given up on this.

over 1 year ago ·