Должен ли я перейти с «urllib.request.urlretrieve (..)» на «urllib.request.urlopen (..)»? - PullRequest
0 голосов
/ 06 июля 2019

1. Проблема устаревания

В Python 3.7 я загружаю большой файл из URL, используя функцию urllib.request.urlretrieve(..). В документации (https://docs.python.org/3/library/urllib.request.html) я прочитал следующее чуть выше urllib.request.urlretrieve(..) документов:

Устаревший интерфейс
Следующие функции и классы портированы из urllib модуля Python 2 (в отличие от urllib2). Они могут устареть в какой-то момент в будущем.


2. Поиск альтернативы

Чтобы сохранить мой код в будущем, я ищу альтернативу. В официальных документах по Python конкретный документ не упоминается, но, похоже, urllib.request.urlopen(..) - самый простой кандидат. Это вверху страницы документации.

К сожалению, альтернативы, такие как urlopen(..) - , не предоставляют аргумент reporthook. Этот аргумент может быть вызван функцией urlretrieve(..). В свою очередь, urlretrieve(..) регулярно вызывает его со следующими аргументами:

  • номер блока.
  • размер блока
  • общий размер файла

Я использую его для обновления индикатора выполнения. Вот почему я пропускаю аргумент reporthook в альтернативах.


3. urlretrieve (..) и urlopen (..)

Я обнаружил, что urlretrieve(..) просто использует urlopen(..). См. Файл кода request.py в установке Python 3.7 (Python37 / Lib / urllib / request.py):

_url_tempfiles = []
def urlretrieve(url, filename=None, reporthook=None, data=None):
    """
    Retrieve a URL into a temporary location on disk.

    Requires a URL argument. If a filename is passed, it is used as
    the temporary file location. The reporthook argument should be
    a callable that accepts a block number, a read size, and the
    total file size of the URL target. The data argument should be
    valid URL encoded data.

    If a filename is passed and the URL points to a local resource,
    the result is a copy from local file to new file.

    Returns a tuple containing the path to the newly created
    data file as well as the resulting HTTPMessage object.
    """
    url_type, path = splittype(url)

    with contextlib.closing(urlopen(url, data)) as fp:
        headers = fp.info()

        # Just return the local path and the "headers" for file://
        # URLs. No sense in performing a copy unless requested.
        if url_type == "file" and not filename:
            return os.path.normpath(path), headers

        # Handle temporary file setup.
        if filename:
            tfp = open(filename, 'wb')
        else:
            tfp = tempfile.NamedTemporaryFile(delete=False)
            filename = tfp.name
            _url_tempfiles.append(filename)

        with tfp:
            result = filename, headers
            bs = 1024*8
            size = -1
            read = 0
            blocknum = 0
            if "content-length" in headers:
                size = int(headers["Content-Length"])

            if reporthook:
                reporthook(blocknum, bs, size)

            while True:
                block = fp.read(bs)
                if not block:
                    break
                read += len(block)
                tfp.write(block)
                blocknum += 1
                if reporthook:
                    reporthook(blocknum, bs, size)

    if size >= 0 and read < size:
        raise ContentTooShortError(
            "retrieval incomplete: got only %i out of %i bytes"
            % (read, size), result)

    return result

4. Заключение

Из всего этого я вижу три возможных решения:

  1. Я сохраняю свой код без изменений . Будем надеяться, что функция urlretrieve(..) не устареет в ближайшее время.

  2. Я пишу себе замещающую функцию , которая ведет себя как urlretrieve(..) снаружи и использует urlopen(..) внутри. На самом деле, такая функция была бы копией кода выше. Это кажется нечистым - по сравнению с использованием официального urlretrieve(..).

  3. Я пишу себе функцию замены , которая ведет себя как urlretrieve(..) снаружи и использует что-то совершенно иное внутри. Но эй, зачем мне это делать? urlopen(..) не считается устаревшим, так почему бы не использовать его?

Какое решение вы бы приняли?

...