mirror of
https://github.com/kennethreitz/requests3.git
synced 2026-06-05 23:10:16 +00:00
185 lines
5.8 KiB
Python
185 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
requests.hooks
|
|
~~~~~~~~~~~~~~
|
|
|
|
This module provides the capabilities for the Requests hooks system.
|
|
|
|
Available hooks:
|
|
|
|
``args``:
|
|
A dictionary of the arguments being sent to Request().
|
|
|
|
``pre_request``:
|
|
The Request object, directly before being sent.
|
|
|
|
``post_request``:
|
|
The Request object, directly after being sent.
|
|
|
|
``response``:
|
|
The response generated from a Request.
|
|
|
|
"""
|
|
|
|
import warnings
|
|
from collections import Iterable
|
|
import config
|
|
import zlib
|
|
import bz2
|
|
from cgi import parse_header
|
|
|
|
def setup_hooks(supplied):
|
|
"""Setup a hooks mapping, based on the supplied argument. Eache mapping
|
|
value will be list of hooks that will extend the **default_hooks**.
|
|
|
|
:param supplied: a dictionary of hooks. Each value can either be a callable
|
|
or a list of callables.
|
|
:type supplied: dict
|
|
:returns: a dictionary of hooks that extends the **default_hooks** dictionary.
|
|
:rtype: dict
|
|
"""
|
|
|
|
# Copy the default hooks settings.
|
|
default = config.settings.default_hooks
|
|
dispatching = dict([(k, v[:]) for k, v in default.items()])
|
|
|
|
# I abandoned the idea of a dictionary of sets because sets may not keep
|
|
# insertion order, while it may be important. Also, there is no real reason
|
|
# to force hooks to run once.
|
|
for hooks, values in supplied.items():
|
|
hook_list = values if isinstance(values, Iterable) else [values]
|
|
dispatching[hooks].extend(hook_list)
|
|
|
|
# If header is set by config, maybe response is encoded.
|
|
if config.settings.base_headers.get('Accept-Encoding', ''):
|
|
if not decode_encoding in dispatching['response']:
|
|
# It's safer to put decoding as first hook.
|
|
dispatching['response'].insert(0, decode_encoding)
|
|
|
|
if config.settings.decode_unicode:
|
|
try:
|
|
# Try unicode encoding just after content decoding...
|
|
index = dispatching['response'].index(decode_encoding) + 1
|
|
except ValueError:
|
|
# ... Or as first hook
|
|
index = 0
|
|
dispatching['response'].insert(index, decode_unicode)
|
|
|
|
return dispatching
|
|
|
|
def dispatch_hooks(hooks, data):
|
|
"""Dispatches multiple hooks on a given piece of data.
|
|
|
|
:param key: the hooks group to lookup
|
|
:type key: str
|
|
:param hooks: the hooks dictionary. The value of each key can be a callable
|
|
object, or a list of callable objects.
|
|
:type hooks: dict
|
|
:param data: the object on witch the hooks should be applied
|
|
:type data: object
|
|
"""
|
|
for hook in hooks:
|
|
try:
|
|
# hook must be a callable.
|
|
data = hook(data)
|
|
|
|
except Exception, why:
|
|
|
|
# Letting users to choose a policy may be an idea. It can be as
|
|
# simple as "be gracefull, or not":
|
|
#
|
|
# config.settings.gracefull_hooks = True | False
|
|
if not config.settings.gracefull_hooks: raise
|
|
|
|
warnings.warn(str(why))
|
|
|
|
return data
|
|
|
|
#: Example response hook that turns a JSON formatted
|
|
#: :py:class:`requests.models.Response.content` into a dumped data structure::
|
|
#:
|
|
#: try:
|
|
#: import json
|
|
#: except ImportError:
|
|
#: try:
|
|
#: import simplejson as json
|
|
#: except ImportError:
|
|
#: json = False
|
|
#:
|
|
#: if json:
|
|
#: def json_content(r):
|
|
#: """Turns content into a dumped JSON structure."""
|
|
#: r._content = json.dumps(r.content)
|
|
#: return r
|
|
#:
|
|
#: Example response hook that turns an XML formatted
|
|
#: :py:class:`requests.models.Response.content` into an ElementTree::
|
|
#:
|
|
#: try:
|
|
#: from lxml import etree
|
|
#: except ImportError:
|
|
#: try:
|
|
#: import xml.etree.cElementTree as etree
|
|
#: except ImportError:
|
|
#: try:
|
|
#: import xml.etree.ElementTree as etree
|
|
#: except ImportError:
|
|
#: try:
|
|
#: import cElementTree as etree
|
|
#: except ImportError:
|
|
#: try:
|
|
#: import elementtree.ElementTree as etree
|
|
#: except ImportError:
|
|
#: etree = False
|
|
#:
|
|
#: if etree:
|
|
#: def etree_content(r):
|
|
#: """Turns content into an ElementTree structure."""
|
|
#: r._content = etree.fromstring(r.content)
|
|
#: return r
|
|
|
|
def decode_unicode(r):
|
|
"""Encode content into unicode string.
|
|
|
|
:param r: response object
|
|
:type r: :py:class:`requests.models.Response`
|
|
:returns: the same input object.
|
|
:rtype: :py:class:`requests.models.Response`
|
|
"""
|
|
content_type, params = parse_header(r.headers['content-type'])
|
|
charset = params.get('charset', '').strip("'\"")
|
|
r._content = unicode(r.content, charset) if charset else unicode(r.content)
|
|
return r
|
|
|
|
def decode_encoding(r):
|
|
"""Decode content using Contetn-Encoding header.
|
|
|
|
:param r: response object
|
|
:type r: :py:class:`requests.models.Response`
|
|
:returns: the same input object.
|
|
:rtype: :py:class:`requests.models.Response`
|
|
"""
|
|
|
|
# Dictionary of content decoders.
|
|
decode = {
|
|
# No decoding applied.
|
|
'identity': lambda content: content,
|
|
# Decode Response content compressed with deflate.
|
|
'deflate': lambda content: zlib.decompress(content),
|
|
# Decode Response content compressed with gzip.
|
|
'gzip': lambda content: zlib.decompress(content, 16+zlib.MAX_WBITS),
|
|
# Decode Response content compressed with bz2.
|
|
# Not a standard Content-Encoding value, but..
|
|
'bzip2': lambda content: bz2.decompress(content),
|
|
}
|
|
# Decode Response content compressed with compress.
|
|
# If I understood zlib...
|
|
decode['compress'] = decode['deflate']
|
|
|
|
# Apply decoding only if the header is set.
|
|
encoding = r.headers['content-encoding']
|
|
if encoding:
|
|
r._content = decode[encoding](r.content)
|
|
return r
|