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() — проверяем, существует ли имя в словаре заголовков.
Связанные с URL:
• 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, однако нижнее подчеркивание, с которого начинается это имя, намекает на то, что атрибут не предназначен для использования за пределами класса, не запрещая при этом доступ (мы все — ответственные пользователи).
Этот выбор позволяет пользователю изменять объекты, но они становятся гораздо читабельнее, а дополнительная работа, выполняемая внутри PreparedRequest, позволяет исправить проблемы с регистром и использовать словарь вместо CookieJar (ищите оператор if isinstance()/else):
#
#… из файла models.py…
#
class PreparedRequest():
····#
····#…пропускаем все остальное…
····#
····def prepare_cookies(self, cookies):
········"""Подготавливает данные заданного HTTP cookie.
········Эта функция в итоге генерирует заголовок ''Cookie'' на основе
········предоставленных cookies с использованием cookielib. Из-за особенностей
········дизайна cookielib заголовок не будет сгенерирован повторно,
········если он уже существует. Это значит, что эта функция может быть
········вызвана всего один раз во время жизни объекта
········:class:'PreparedRequest <PreparedRequest>'. Любые последующие вызовы
········''prepare_cookies'' не возымеют эффекта, если только заголовок "Cookie"
········не будет удален заранее."""
········if isinstance(cookies, cookielib.CookieJar):
············self._cookies = cookies
········else:
············self._cookies = cookiejar_from_dict(cookies)
········cookie_header = get_cookie_header(self._cookies, self)
········if cookie_header is not None:
············self.headers['Cookie'] = cookie_header
Эти детали могут показаться незначительными, но они позволяют создать интуитивно понятный API.
66
Модуль http.cookiejar ранее назывался cookielib в Python 2, urllib.requests.Request ранее назывался urllib2.Request в Python 2.
67
Этот метод позволяет обрабатывать запросы из разных источников (вроде получения библиотеки JavaScript, размещенной на стороннем сайте). Он должен возвращать изначальный хост запроса, определенный в IETF RFC 2965.