Хотя socket.setsocketimeout
будет устанавливать время ожидания по умолчанию для новых сокетов, если вы не используете сокеты напрямую, настройку можно легко перезаписать. В частности, если библиотека вызовет socket.setblocking
на своем сокете, она сбросит время ожидания.
urllib2.open
имеет аргумент времени ожидания, однако в urllib2.Request
нет времени ожидания. Поскольку вы используете mechanize
, вам следует обратиться к их документации:
Начиная с Python 2.6, urllib2 использует атрибут .timeout для объектов Request внутри. Однако urllib2.Request не имеет аргумента конструктора тайм-аута, и urllib2.urlopen () игнорирует этот параметр. mechanize.Request имеет аргумент конструктора времени ожидания, который используется для установки атрибута с тем же именем, а mechanize.urlopen () не игнорирует атрибут времени ожидания.
источник: http://wwwsearch.sourceforge.net/mechanize/documentation.html
--- EDIT ---
Если либо socket.setsockettimeout
, либо передача тайм-аута на mechanize
работает с небольшими значениями, но не с более высокими, источник проблемы может быть совершенно другим. Во-первых, ваша библиотека может открывать несколько соединений (в данном случае - @ Cédric Julien), поэтому время ожидания применяется к каждой отдельной попытке socket.open и, если она не останавливается при первом сбое, может занять до timeout * num_of_conn
секунд. Другое дело socket.recv
: если соединение действительно медленное и вам не повезло, весь запрос может занять до timeout * incoming_bytes
, как с каждым socket.recv
мы можем получить один байт, и каждый такой вызов может занять timeout
секунд. Поскольку вы вряд ли пострадаете именно от этого темного сценария (один байт на секунды времени ожидания, вы должны быть очень грубым мальчиком), очень вероятно, что потребуются возрасты для очень медленных соединений и очень больших таймаутов.
Единственное решение, которое у вас есть, - принудительно установить таймаут для всего запроса, но здесь нет ничего общего с сокетами. Если вы работаете в Unix, вы можете использовать простое решение с сигналом ALARM
. Вы устанавливаете сигнал на timeout
секунды, и ваш запрос будет прекращен (не забудьте его поймать). Вы можете использовать оператор with
, чтобы сделать его простым и понятным, например:
import signal, time
def request(arg):
"""Your http request"""
time.sleep(2)
return arg
class Timeout():
"""Timeout class using ALARM signal"""
class Timeout(Exception): pass
def __init__(self, sec):
self.sec = sec
def __enter__(self):
signal.signal(signal.SIGALRM, self.raise_timeout)
signal.alarm(self.sec)
def __exit__(self, *args):
signal.alarm(0) # disable alarm
def raise_timeout(self, *args):
raise Timeout.Timeout()
# Run block of code with timeouts
try:
with Timeout(3):
print request("Request 1")
with Timeout(1):
print request("Request 2")
except Timeout.Timeout:
print "Timeout"
# Prints "Request 1" and "Timeout"
Если вы хотите быть более портативным, чем этот, вы должны использовать несколько более мощных пушек, например multiprocessing
, так что вы вызовете процесс для вызова вашего запроса и прекратите его в случае задержки. Поскольку это будет отдельный процесс, вам придется что-то использовать для передачи результата обратно в ваше приложение, это может быть multiprocessing.Pipe
. Вот пример:
from multiprocessing import Process, Pipe
import time
def request(sleep, result):
"""Your http request example"""
time.sleep(sleep)
return result
class TimeoutWrapper():
"""Timeout wrapper using separate process"""
def __init__(self, func, timeout):
self.func = func
self.timeout = timeout
def __call__(self, *args, **kargs):
"""Run func with timeout"""
def pmain(pipe, func, args, kargs):
"""Function to be called in separate process"""
result = func(*args, **kargs) # call func with passed arguments
pipe.send(result) # send result to pipe
parent_pipe, child_pipe = Pipe() # Pipe for retrieving result of func
p = Process(target=pmain, args=(child_pipe, self.func, args, kargs))
p.start()
p.join(self.timeout) # wait for prcoess to end
if p.is_alive():
p.terminate() # Timeout, kill
return None # or raise exception if None is acceptable result
else:
return parent_pipe.recv() # OK, get result
print TimeoutWrapper(request, 3)(1, "OK") # prints OK
print TimeoutWrapper(request, 1)(2, "Timeout") # prints None
У вас действительно нет особого выбора, если вы хотите принудительно завершить запрос через фиксированное количество секунд. socket.timeout
предоставит тайм-аут для операции с одним сокетом (connect / recv / send), но если у вас их несколько, вы можете страдать от очень длительного времени выполнения.