Кеннет Рейтц – Автостопом по Python (страница 31)
• sessions.py — предоставляет объект Session для управления настройками и их сохранения между запросами (cookies, авторизация, прокси).
• auth.py — содержит дескрипторы для аутентификации в Requests.
• status_codes.py — таблица, в которой соотносятся заголовки состояний и их коды.
• cookies.py — код для совместимости, который позволяет использовать cookielib.CookieJar с запросами.
• adapters.py — содержит транспортные адаптеры, которые Requests применяет для определения и поддержания соединений.
• exceptions.py — все исключения Requests.
• structures.py — структуры данных, которыми пользуется Requests.
• certs.py — возвращает предпочтительный набор сертификатов CA по умолчанию, в котором перечислены доверенные сертификаты SSL.
• utils.py — предоставляет вспомогательные функции, которые используются внутри Requests и могут применяться внешними пользователями.
Что мы узнали из заголовков:
• существует система функций перехвата (hooks.py) — это подразумевает, что пользователь может модифицировать способ работы библиотеки Requests. Мы не будем обсуждать этот вопрос подробно, чтобы не отвлекаться от темы;
• основным модулем является models.py, поскольку в нем содержатся «основные объекты, которыми пользуется Requests»;
• основная причина существования sessions.Session — сохранение cookies между несколькими запросами (например, это может понадобиться во время аутентификации);
• соединение HTTP создается с помощью объектов из модуля adapters.py;
• остальная часть проекта довольно очевидна: auth.py нужен для аутентификации, status_codes.py содержит коды состояний, cookies.py нужен для добавления и удаления cookies, exceptions.py — для исключений, structures.py содержит структуры данных (например, не зависящий от регистра словарь), utils.py — вспомогательные функции.
Идея поместить модуль коммуникации в отдельный файл adapters.py инновационна (во всяком случае для этого разработчика). Это означает, что models.Request, models.PreparedRequest и models.Response на самом деле ничего не делают — просто сохраняют данные, возможно несколько изменяя их в угоду представлению, сериализации или кодировке. Действия обрабатываются отдельными классами, которые существуют только для того, чтобы выполнить, например, аутентификацию или коммуникацию. Каждый класс делает что-то одно, и каждый модуль содержит классы, выполняющие похожие задачи, — в этом и проявляется питонский подход, который многие из нас используют для определений функций.
Если вы начинаете новый проект и используете Sphinx и его расширения autodoc, вам нужно отформатировать строки документации таким образом, чтобы Sphinx смог проанализировать их.
В документации Sphinx не всегда получится легко найти нужные ключевые слова. Многие рекомендуют копировать строки документации в Requests, если вы хотите, чтобы формат был правильным, а не искать инструкции в документации Sphinx. Например, рассмотрим определение функции delete() в файле requests/api.py:
def delete(url, **kwargs):
····"""Отправляет запрос DELETE.
····:param url: URL для нового объекта: class:'Request'.
····:param \*\*kwargs: Необязательные аргументы, которые принимает
·······················''request''.
····:return: объект класса: class:'Response <Response>'
····:rtype: requests.Response
····"""
····return request('delete', url, **kwargs)
Представление этого определения в Sphinx autodoc находится в онлайн-документации к API (http://docs.python-requests.org/en/master/api/#requests.delete).
Примеры из структуры Requests
Все любят API Requests — его просто запомнить, он помогает пользователям писать простой и красивый код. В этом разделе сначала рассматривается предпочтительный дизайн для более полных сообщений об ошибках и запоминающийся API, который, как мы думаем, привел к созданию модуля requests.api. Затем мы рассмотрим разницу между объектами requests.Request и urllib.request.Request и расскажем, для чего существует объект requests.Request.
Функции, определенные в файле api.py (за исключением request()), названы в честь методов запросов HTTP[65]. Все методы запроса практически одинаковы, за исключением имени метода и возможных параметров с ключевыми словами, поэтому мы удалим из этого фрагмента весь код, расположенный в файле requests/api.py после функции get().
В файле requests/api.py нет новой функциональности; она существует для того, чтобы предоставить пользователю простой API. Плюс размещение строковых методов HTTP непосредственно в API в качестве имен функций означает, что любая опечатка в имени метода будет найдена на ранних этапах, например:
>>> requests.foo('http://www.python.org')
Traceback (most recent call last):
····File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'foo'
>>>
>>> requests.request('foo', 'http://www.python.org')
<Response [403]>
Файл __init__.py предоставляет классы Request, PreparedRequest и Response из файла models.py как часть основного API. Зачем вообще нужен файл models.Request? В стандартной библиотеке уже существует urllib.requests.Request, и в файле cookies.py находится объект MockRequest, который оборачивает models.Request, чтобы он работал как urllib.requests.Request для http.cookiejar[66]. Это означает, что любые методы, необходимые для взаимодействия объекта типа Request с библиотекой cookies, намеренно исключены из requests.Request. Для чего эти лишние усилия?
Дополнительные методы в MockRequest (нужен для эмуляции urllib.request.Request для библиотеки cookies) используются библиотекой cookies для управления cookies. За исключением функции get_type() (которая обычно возвращает http или https при использовании) и непроверяемого свойства (в нашем случае True), они связаны с URL или заголовками запросов.
• add_unredirected_header() — добавить в заголовок новую пару ключ-значение;
• get_header() — получить определенное имя из словаря заголовков;
• get_new_headers() — получить словарь, содержащий новые заголовки (которые добавлены с помощью cookielib);
• has_header() — проверяем, существует ли имя в словаре заголовков.
• get_full_url() — соответствует своему имени;
• host и origin_req_host — свойства, чьи значения устанавливаются путем вызова методов get_host() и get_origin_req_host() соответственно;
• get_host() — извлекает хост из URL (например, www.python.org из https://www.python.org/dev/peps/pep-0008/);
• get_origin_req_host() — вызывает get_host()[67].
Все они являются функциями доступа, за исключением MockRequest.add_unredirected_header().
В строке документации к объекту MockRequest указывается, что «оригинальный объект запроса доступен только для чтения».
В requests.Request вместо этого непосредственно доступны атрибуты данных. Это делает все функции доступа ненужными: для получения или установки заголовков требуется лишь получить доступ к словарю request-instance.headers. Аналогично пользователь может получить или изменить строку URL: request-instance.url.
Объект PreparedRequest инициализируется пустым и заполняется при вызове метода prepared-request-instance.prepare(), что наполняет его релевантными данными (обычно получаемыми путем вызова объекта Request). В этот момент применяются исправления регистра символов, кодировки и пр. Содержимое объекта после подготовки можно будет отправить на сервер, но к каждому атрибуту все еще можно получить доступ непосредственно. Доступен даже PreparedRequest._cookies, однако нижнее подчеркивание, с которого начинается это имя, намекает на то, что атрибут не предназначен для использования за пределами класса, не запрещая при этом доступ (мы все — ответственные пользователи).