Блог им. Albus

Обработка таймаута на Питоне

Коллеги, помогите написать красиво кусочек кода. Обработка ситуации, когда ты заказал котировки с Финама, они не пришли, и ты пробуешь ещё раз. Сейчас я кривенько (чтобы описать задачу) написал так:

try: 
        txt=urlopen(url, timeout=20).readlines()
except timeout:
        print ("Exception!\nWait...")
        sleep (20)
        try:
                txt=urlopen(url, timeout=20).readlines()
        except timeout:
                print ("Exception!\nWait...")
                sleep (20)
                try:
                        txt=urlopen(url, timeout=20).readlines()
                except timeout:
                        print ("Котировки с Финама не пришли")
Как это написать красиво внутри цикла?
10 попыток. Если 10-я неудачная, выводим сообщение
print ("Котировки с Финама не пришли")
★4
23 комментария
def get_data(url, timeout, tries=0, max_tries=10):
  try:
    txt = urlopen(url, timeout=timeout).readlines()
  except TimeoutError:
    print(f«Retry {tries + 1} for {url}»)
    if tries + 1 <= max_tries:
       txt = get_data(url, timeout, tries + 1, max_tries)
    else:
      raise TimeoutError
  return txt
avatar
Михаил, круто! С рекурсией! Я бы так не смог ))))
Михаил, так вроде тоже можно:



Albus (Игорь Китаев), можно естественно миллионом способов. Вопрос, как в некотором смысле правильнее для долгосрочной поддержки программы. 

Лучше делить программу на очень небольшие функции.

В Питоне цикл по range обычно признак криво написанной программы.

Создание флагов перед циклом тоже обычно антипатерн. 

Захотите вы потом поменять время ожидания или количество попыток — вам прийдется читать код и менять в нескольких местах. У меня вы просто вызов функции с другими параметрами сделаете.
avatar
Михаил, все отлично

только вот вызывать рекурсивно функцию да еще много раз в exception не очень здорово

Инженер по контролю качества ПО наверняка придерется и не пропустит в продакшн
avatar
_sg_, в питоне цикл с помощью exception реализован — возможно в других языках это и не очень хорошо. 
avatar
Михаил, 
вызывать эту функцию так?
get_data(url, timeout=60, tries=0, max_tries=10)
tries = 0 — уточняю из-за неё потому что она тут глубоко участвует в рекурсии. 
Albus (Игорь Китаев), при вызове вы передаете url и timeout — остальные параметры не используете get_data(url, 60). 
avatar
Михаил, 
спасибо, работает.
Только с этой строчкой что то не то, я её заменил
    print(f«Retry {tries + 1} for {url}»)
(правильные кавычки "" тоже ставил — безрезультатно)
Albus (Игорь Китаев), кавычки smartlab автоматически заменяет. f-строки могут не работать на старой версии Питона — у вас какая?
avatar
Михаил, 3.8.2 — самая свежая
Albus (Игорь Китаев), тогда не знаю — должно работать с 3.6. Дистанционно сложно разобраться. 
avatar
Albus (Игорь Китаев), это самый правильный и читабельный способ, очевидно
avatar
И так сойдёт! Ты же трейдер, а не программист! ))
Eugene Logunov,
не нужно одновременно и false и NULL использовать.
Можно просто done = NULL
исправить while
и убрать
  if (data != NULL) {
    done = TRUE
avatar
рекурсия нужна )
avatar
рекурсию поюзать
avatar

pypi.org/project/retrying/ если хочется прям красиво сделать.

а так в цикле считать попытки и брейкать цикл при удаче.

avatar
day0markets, +1
сделать отдельно функцию запроса которая считывает данные и возвращает сами данные и флаг 1\0 — (есть ошибка получения данных/ нет ошибки)
выглядит на питоне так
for ...
 data, err = LoadData(параметры запроса)
 if err == 0:
   break
 else:
   timeout ()


avatar
sis12qw, ну такое. Имхо, вызывающая функция не должна знать о таймаутах и прочем. Ну и когда потребуется ее дернуть в другом придется лезть и смотреть что она там за флаги возвращает и опять городить огород с retry. Флаги вообще далеко не best practice, лучше exception выбрасывать и его обрабатывть
avatar
def get_data(url, timeout=10, max_tries=10):<br>    data = None<br>    try:<br>        data = urlopen(url, timeout=timeout).readlines()<br>    except TimeoutError:<br>        max_tries -= 1<br>    if data:<br>        return data<br>    if max_tries == 0:<br>        return data<br>    get_data(url, timeout, max_tries)
avatar

теги блога Albus (Игорь Китаев)

....все тэги



UPDONW
Новый дизайн