Как изящно прервать загрузку urllib2?

Для создания OpenerDirector я использую urllib2 build_opener() для OpenerDirector . Я использую OpenerDirector для получения медленной страницы и поэтому имеет большой тайм-аут.

Все идет нормально.

Однако в другом потоке мне сказали прекратить загрузку – допустим, пользователь выбрал для выхода из программы в GUI.

Есть ли способ сообщить, что загрузка urllib2 должна прекратиться?

  • Как правильно разобрать HTML в кодировке UTF-8 в строки Unicode с помощью BeautifulSoup?
  • Обработка rss-перенаправлений с помощью Python / urllib2
  • Python urllib над TOR?
  • Подмена IP-адреса источника HTTP-запроса
  • Поддержка https proxy в библиотеке запросов python
  • Python: Войдите в систему, используя urllib
  • urllib2 / запросы и относительный путь HTTP
  • Переопределение urllib2.HTTPError или urllib.error.HTTPError и чтение ответа HTML в любом случае
  • 4 Solutions collect form web for “Как изящно прервать загрузку urllib2?”

    Нет чистого ответа. Есть несколько уродливых.

    Вначале я задавал в вопросе отвергнутые идеи. Поскольку стало ясно, что нет правильных ответов, я решил опубликовать различные субоптимальные альтернативы в качестве ответа на список. Некоторые из них вдохновлены комментариями, спасибо.

    Поддержка библиотек

    Идеальное решение было бы, если OpenerDirector предложил оператор отмены.

    Это не. Авторы библиотек отмечают: если вы обеспечиваете длительные медленные операции, вам необходимо предоставить способ их отмены, если люди будут использовать их в реальных приложениях.

    Уменьшить время ожидания

    Как общее решение для других, это может сработать. При меньшем тайм-ауте он будет более восприимчивым к изменениям обстоятельств. Однако это также приведет к сбою загрузки, если они не были полностью завершены в тайм-аут, поэтому это компромисс. В моей ситуации это несостоятельно.

    Прочитайте загрузку в кусках.

    Опять же, как общее решение, это может сработать. Если загрузка состоит из очень больших файлов, вы можете прочитать их в небольших кусках и прервать после чтения фрагмента.

    К сожалению, если (как и в моем случае) задержка заключается в получении первого байта, а не в размере файла, это не поможет.

    Убейте всю нить.

    Хотя есть некоторые агрессивные методы для уничтожения потоков, в зависимости от операционной системы, они не рекомендуются . В частности, они могут привести к возникновению взаимоблокировок. См. Две статьи Эли Бендерски (через @JBernardo).

    Просто не реагируйте

    Если операция прерывания была инициирована пользователем, может быть проще просто не отвечать на запросы и не действовать по запросу до завершения открытой операции.

    Является ли эта неприемлемость приемлемой для ваших пользователей (подсказка: нет!), Зависит от вашего проекта.

    Он также продолжает размещать спрос на сервере, даже если результат известен как ненужный.

    Позвольте этому заглянуть в другой поток.

    Если вы создаете отдельный поток для запуска операции и затем обмениваетесь этим потоком прерывистым способом, вы можете отказаться от заблокированного потока и начать работу над следующей операцией. В конце концов, поток будет разблокирован, а затем он может изящно отключиться.

    Нить должна быть демоном , поэтому она не блокирует полное закрытие приложения.

    Это даст пользователю отзывчивость, но это означает, что сервер должен будет продолжать поддерживать его, даже если результат не нужен.

    Перепишите методы сокетов на основе опроса.

    Как описано в ответе @ Luke, возможно предоставление (хрупких?, Не переносимых?) Расширений для стандартных библиотек Python.

    Его решение изменяет операции сокета от блокировки до опроса. Другое может разрешить выключение через метод socket.shutdown() (если это действительно прервет заблокированный сокет – не проверено).

    Решение на основе Twisted может быть более чистым. Смотри ниже.

    Замените сокеты асинхронными, не потоковыми библиотеками.

    Среда Twisted предоставляет заменяемый набор библиотек для сетевых операций, управляемых событиями. Я понимаю, это означает, что все разные коммуникации могут обрабатываться одним потоком без блокировки.

    саботаж

    Возможно, можно будет перемещаться по OpenerDirector , чтобы найти блокировку базового уровня, которая блокирует, и саботировать ее напрямую (достаточно ли socket.shutdown() ?), Чтобы вернуть ее.

    Тьфу.

    Поместите его в отдельный (убивающий) процесс

    Поток, который считывает сокет, может быть перемещен в отдельный процесс, а межпроцессная связь может использоваться для передачи результата. Этот IPC может быть прерван раньше клиентом, а затем весь процесс может быть убит.

    Попросите веб-сервер отменить

    Если вы контролируете считываемый веб-сервер, ему может быть отправлено отдельное сообщение с просьбой закрыть сокет. Это должно заставить заблокированный клиент реагировать.

    Я не вижу встроенного механизма для этого. Я бы просто переместил OpenerDirector в свой собственный процесс потока, поэтому было бы безопасно его убить.

    Примечание: нет способа «убить» поток в python (спасибо JBernardo). Тем не менее, можно создать исключение в потоке, но, скорее всего, это не сработает, если поток блокирует сокет.

    Вот начало для другого подхода. Он работает, расширяя часть стека httplib, чтобы включить неблокирующую проверку для ответа сервера. Вам нужно будет внести несколько изменений, чтобы реализовать это в своем потоке. Также обратите внимание, что он использует некоторые недокументированные биты urllib2 и httplib, поэтому окончательное решение для вас, вероятно, будет зависеть от версии используемого вами Python (у меня есть 2.7.3). Сотрясайте файлы urllib2.py и httplib.py; они вполне читаемы.

     import urllib2, httplib, select, time class Response(httplib.HTTPResponse): def _read_status(self): ## Do non-blocking checks for server response until something arrives. while True: sel = select.select([self.fp.fileno()], [], [], 0) if len(sel[0]) > 0: break ## <--- Right here, check to see whether thread has requested to stop ## Also check to see whether timeout has elapsed time.sleep(0.1) return httplib.HTTPResponse._read_status(self) class Connection(httplib.HTTPConnection): response_class = Response class Handler(urllib2.HTTPHandler): def http_open(self, req): return self.do_open(Connection, req) h = Handler() o = urllib2.build_opener(h) f = o.open(url) print f.read() 

    Также обратите внимание, что в стеке есть много мест, которые могут блокировать; этот пример охватывает только один из них – сервер получил запрос, но требует много времени для ответа.

    Я нахожу подход с размещением всех ваших связанных с urllib заданий в потоках, наиболее подходящих из-за блокировки характера urllib. Тогда можно вообще прекратить выполнение задач, включая запросы. Убийство нитей действительно небезопасно, но сбор исключений должен быть безопасным.

    Так вот как создать исключение в потоке ( doc ):

     import ctypes ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(your_thread.ident), ctypes.py_object(your_exception)) 

    Если сокет в данный момент будет находиться в состоянии блокировки (соединения), исключение будет возбуждено сразу же после возобновления потока.

    Interesting Posts
    Python - лучший язык программирования в мире.