26

I am using xmlrpclib.ServerProxy to make RPC calls to a remote server. If there is not a network connection to the server it takes the default 10 seconds to return a socket.gaierror to my program.

This is annoying when doing development without a network connection, or if the remote server is down. Is there a way to update the timeout on my ServerProxy object?

I can't see a clear way to get access to the socket to update it.

ashchristopher
  • 25,143
  • 18
  • 48
  • 49

9 Answers9

22

An more straightforward solution is at: http://www.devpicayune.com/entry/200609191448

import xmlrpclib 
import socket

x = xmlrpclib.ServerProxy('http:1.2.3.4')  
socket.setdefaulttimeout(10)        #set the timeout to 10 seconds 
x.func_name(args)                   #times out after 10 seconds
socket.setdefaulttimeout(None)      #sets the default back
azarias
  • 689
  • 1
  • 7
  • 7
  • How does the socket apply to the xmlrpclib? We are never setting the socket on x. – ashchristopher Nov 26 '09 at 15:33
  • 1
    @ashchristopher: I have not tested this code, but it looks like setdefaulttimeout is a global default value. So if xmlrpclib does not set any specific timeout value, it will probably use the global default. – MiniQuark Oct 18 '10 at 12:02
  • 1
    @MiniQuark: Correct, it's a global default (None) which means that new socket objects have no timeout. The setdefaulttimeout function has the advantage that it works for HTTPS/SSL sockets, where the other posted examples only work with HTTP. – Jeff Bauer Jan 28 '11 at 21:41
  • @Jeff Bauer: the community wiki example with HTTP can also be done with subclassing HTTPSConnection, HTTPS and SafeTransport. This should work the very same way. – housemaister Apr 08 '11 at 13:24
  • 1
    Definitely the winner for my code. I added 2 lines and done compared to adding 3 classes what the others suggest. TL;DR by the way. – Giszmo Jul 26 '13 at 01:20
18

clean non global version.

import xmlrpclib
import httplib


class TimeoutHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        httplib.HTTPConnection.connect(self)
        self.sock.settimeout(self.timeout)


class TimeoutHTTP(httplib.HTTP):
    _connection_class = TimeoutHTTPConnection

    def set_timeout(self, timeout):
        self._conn.timeout = timeout


class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=10, *l, **kw):
        xmlrpclib.Transport.__init__(self, *l, **kw)
        self.timeout = timeout

    def make_connection(self, host):
        conn = TimeoutHTTP(host)
        conn.set_timeout(self.timeout)
        return conn


class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout=10, *l, **kw):
        kw['transport'] = TimeoutTransport(
            timeout=timeout, use_datetime=kw.get('use_datetime', 0))
        xmlrpclib.ServerProxy.__init__(self, uri, *l, **kw)


if __name__ == "__main__":
    s = TimeoutServerProxy('http://127.0.0.1:9090', timeout=2)
    s.dummy()
Cenk Alti
  • 2,792
  • 2
  • 26
  • 25
  • Works perfectly for me, Python2.5 on Debian. BTW, you have a spurious '.' character in the imports, and 'import socket' is unused. – Jonathan Hartley Oct 05 '10 at 15:46
  • 5
    Not working for me on Python 2.7. When I make an RPC call, I get the error `AttributeError: TimeoutHTTP instance has no attribute 'getresponse'` – AFoglia Aug 03 '11 at 07:54
  • get AttributeError: module 'http.client' has no attribute 'HTTPClient' when I migrate to python3. – LetsOMG Aug 28 '18 at 22:51
5

I wanted a small, clean, but also explicit version, so based on all other answers here, this is what I came up with:

import xmlrpclib


class TimeoutTransport(xmlrpclib.Transport):

    def __init__(self, timeout, use_datetime=0):
        self.timeout = timeout
        # xmlrpclib uses old-style classes so we cannot use super()
        xmlrpclib.Transport.__init__(self, use_datetime)

    def make_connection(self, host):
        connection = xmlrpclib.Transport.make_connection(self, host)
        connection.timeout = self.timeout
        return connection


class TimeoutServerProxy(xmlrpclib.ServerProxy):

    def __init__(self, uri, timeout=10, transport=None, encoding=None, verbose=0, allow_none=0, use_datetime=0):
        t = TimeoutTransport(timeout)
        xmlrpclib.ServerProxy.__init__(self, uri, t, encoding, verbose, allow_none, use_datetime)


proxy = TimeoutServerProxy(some_url)

I didn't realize at first xmlrpclib has old-style classes so I found it useful with a comment on that, otherwise everything should be pretty self-explanatory.

I don't see why httplib.HTTP would have to be subclassed as well, if someone can enlighten me on this, please do. The above solution is tried and works.

Markus Amalthea Magnuson
  • 8,415
  • 4
  • 41
  • 49
2

Here is code that works on Python 2.7 (probably for other 2.x versions of Python) without raising AttributeError, instance has no attribute 'getresponse'.


class TimeoutHTTPConnection(httplib.HTTPConnection):
    def connect(self):
        httplib.HTTPConnection.connect(self)
        self.sock.settimeout(self.timeout)

class TimeoutHTTP(httplib.HTTP):
    _connection_class = TimeoutHTTPConnection

    def set_timeout(self, timeout):
        self._conn.timeout = timeout

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *args, **kwargs):
        xmlrpclib.Transport.__init__(self, *args, **kwargs)
        self.timeout = timeout

    def make_connection(self, host):
        if self._connection and host == self._connection[0]:
            return self._connection[1]

        chost, self._extra_headers, x509 = self.get_host_info(host)
        self._connection = host, httplib.HTTPConnection(chost)
        return self._connection[1]


transport = TimeoutTransport(timeout=timeout)
xmlrpclib.ServerProxy.__init__(self, uri, transport=transport, allow_none=True)
mtasic85
  • 3,905
  • 2
  • 19
  • 28
1

Here another smart and very pythonic solution using Python's with statement:

import socket
import xmlrpc.client

class MyServerProxy:
    def __init__(self, url, timeout=None):
        self.__url = url
        self.__timeout = timeout
        self.__prevDefaultTimeout = None

    def __enter__(self):
        try:
            if self.__timeout:
                self.__prevDefaultTimeout = socket.getdefaulttimeout()
                socket.setdefaulttimeout(self.__timeout)
            proxy = xmlrpc.client.ServerProxy(self.__url, allow_none=True)
        except Exception as ex:
            raise Exception("Unable create XMLRPC-proxy for url '%s': %s" % (self.__url, ex))
        return proxy
    def __exit__(self, type, value, traceback):
        if self.__prevDefaultTimeout is None:
            socket.setdefaulttimeout(self.__prevDefaultTimeout)

This class can be used like this:

with MyServerProxy('http://1.2.3.4', 20) as proxy:
    proxy.dummy()
gecco
  • 17,969
  • 11
  • 51
  • 68
  • I think `__exit__()` is wrong here - it should not test the previous timeout value for None, it should just set whatever was there before (be it a float or None). – Martin Pecka Apr 03 '22 at 22:50
  • @MartinPecka I agree, I also think in `__exit__()` that it should just `setdefaulttimeout` to whatever `__prevDefaultTimeout` is. Also, I think `self.__prevDefaultTimeout = socket.getdefaulttimeout()` should be moved above the `if`, like the first line of `__enter__`. – RcoderNY Jun 08 '22 at 01:38
1

Based on the one from antonylesuisse, a working version (on python >= 2.6).

# -*- coding: utf8 -*-
import xmlrpclib
import httplib
import socket

class TimeoutHTTP(httplib.HTTP):
   def __init__(self, host='', port=None, strict=None,
                timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
        if port == 0:
            port = None
        self._setup(self._connection_class(host, port, strict, timeout))

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *args, **kwargs):
        xmlrpclib.Transport.__init__(self, *args, **kwargs)
        self.timeout = timeout

    def make_connection(self, host):
        host, extra_headers, x509 = self.get_host_info(host)
        conn = TimeoutHTTP(host, timeout=self.timeout)
        return conn

class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                 *args, **kwargs):
        kwargs['transport'] = TimeoutTransport(timeout=timeout,
                                    use_datetime=kwargs.get('use_datetime', 0))
        xmlrpclib.ServerProxy.__init__(self, uri, *args, **kwargs)
KangOl
  • 313
  • 1
  • 6
1

Based on the one from antonylesuisse, but works on Python 2.7.5, resolving the problem:AttributeError: TimeoutHTTP instance has no attribute 'getresponse'

class TimeoutHTTP(httplib.HTTP):
    def __init__(self, host='', port=None, strict=None,
                timeout=socket._GLOBAL_DEFAULT_TIMEOUT):
        if port == 0:
            port = None
        self._setup(self._connection_class(host, port, strict, timeout))

    def getresponse(self, *args, **kw):
        return self._conn.getresponse(*args, **kw)

class TimeoutTransport(xmlrpclib.Transport):
    def __init__(self,  timeout=socket._GLOBAL_DEFAULT_TIMEOUT, *l, **kw):
        xmlrpclib.Transport.__init__(self, *l, **kw)
        self.timeout=timeout

    def make_connection(self, host):
        host, extra_headers, x509 = self.get_host_info(host)
        conn = TimeoutHTTP(host, timeout=self.timeout)
        return conn

class TimeoutServerProxy(xmlrpclib.ServerProxy):
    def __init__(self, uri, timeout= socket._GLOBAL_DEFAULT_TIMEOUT, *l, **kw):
        kw['transport']=TimeoutTransport(timeout=timeout, use_datetime=kw.get('use_datetime',0))
        xmlrpclib.ServerProxy.__init__(self, uri, *l, **kw)

proxy = TimeoutServerProxy('http://127.0.0.1:1989', timeout=30)
print proxy.test_connection()
lengxuehx
  • 1,500
  • 1
  • 18
  • 25
1

Here is a verbatim copy from http://code.activestate.com/recipes/473878/

def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
        threading.Thread.__init__(self)
        self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(timeout_duration)
    if it.isAlive():
        return default
    else:
        return it.result
M.Babcock
  • 18,753
  • 6
  • 54
  • 84
monkut
  • 42,176
  • 24
  • 124
  • 155
0

The following example works with Python 2.7.4.

import xmlrpclib
from xmlrpclib import *
import httplib

def Server(url, *args, **kwargs):
    t = TimeoutTransport(kwargs.get('timeout', 20))
    if 'timeout' in kwargs:
       del kwargs['timeout']
    kwargs['transport'] = t
    server = xmlrpclib.Server(url, *args, **kwargs)
    return server

TimeoutServerProxy = Server

class TimeoutTransport(xmlrpclib.Transport):

    def __init__(self, timeout, use_datetime=0):
        self.timeout = timeout
        return xmlrpclib.Transport.__init__(self, use_datetime)

    def make_connection(self, host):
        conn = xmlrpclib.Transport.make_connection(self, host)
        conn.timeout = self.timeout
        return connrpclib.Server(url, *args, **kwargs)
Markus Amalthea Magnuson
  • 8,415
  • 4
  • 41
  • 49
Slava
  • 1
  • 1