aboutsummaryrefslogtreecommitdiff
path: root/requests/utils.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests/utils.py')
-rw-r--r--requests/utils.py137
1 files changed, 132 insertions, 5 deletions
diff --git a/requests/utils.py b/requests/utils.py
index 2e16163..f31cad8 100644
--- a/requests/utils.py
+++ b/requests/utils.py
@@ -12,8 +12,109 @@ that are also useful for external consumption.
import cgi
import codecs
import cookielib
+import os
+import random
import re
import zlib
+import urllib
+
+from urllib2 import parse_http_list as _parse_list_header
+
+
+def guess_filename(obj):
+ """Tries to guess the filename of the given object."""
+ name = getattr(obj, 'name', None)
+ if name and name[0] != '<' and name[-1] != '>':
+ return name
+
+# From mitsuhiko/werkzeug (used with permission).
+def parse_list_header(value):
+ """Parse lists as described by RFC 2068 Section 2.
+
+ In particular, parse comma-separated lists where the elements of
+ the list may include quoted-strings. A quoted-string could
+ contain a comma. A non-quoted string could have quotes in the
+ middle. Quotes are removed automatically after parsing.
+
+ It basically works like :func:`parse_set_header` just that items
+ may appear multiple times and case sensitivity is preserved.
+
+ The return value is a standard :class:`list`:
+
+ >>> parse_list_header('token, "quoted value"')
+ ['token', 'quoted value']
+
+ To create a header from the :class:`list` again, use the
+ :func:`dump_header` function.
+
+ :param value: a string with a list header.
+ :return: :class:`list`
+ """
+ result = []
+ for item in _parse_list_header(value):
+ if item[:1] == item[-1:] == '"':
+ item = unquote_header_value(item[1:-1])
+ result.append(item)
+ return result
+
+
+# From mitsuhiko/werkzeug (used with permission).
+def parse_dict_header(value):
+ """Parse lists of key, value pairs as described by RFC 2068 Section 2 and
+ convert them into a python dict:
+
+ >>> d = parse_dict_header('foo="is a fish", bar="as well"')
+ >>> type(d) is dict
+ True
+ >>> sorted(d.items())
+ [('bar', 'as well'), ('foo', 'is a fish')]
+
+ If there is no value for a key it will be `None`:
+
+ >>> parse_dict_header('key_without_value')
+ {'key_without_value': None}
+
+ To create a header from the :class:`dict` again, use the
+ :func:`dump_header` function.
+
+ :param value: a string with a dict header.
+ :return: :class:`dict`
+ """
+ result = {}
+ for item in _parse_list_header(value):
+ if '=' not in item:
+ result[item] = None
+ continue
+ name, value = item.split('=', 1)
+ if value[:1] == value[-1:] == '"':
+ value = unquote_header_value(value[1:-1])
+ result[name] = value
+ return result
+
+
+# From mitsuhiko/werkzeug (used with permission).
+def unquote_header_value(value, is_filename=False):
+ r"""Unquotes a header value. (Reversal of :func:`quote_header_value`).
+ This does not use the real unquoting but what browsers are actually
+ using for quoting.
+
+ :param value: the header value to unquote.
+ """
+ if value and value[0] == value[-1] == '"':
+ # this is not the real unquoting, but fixing this so that the
+ # RFC is met will result in bugs with internet explorer and
+ # probably some other browsers as well. IE for example is
+ # uploading files with "C:\foo\bar.txt" as filename
+ value = value[1:-1]
+
+ # if this is a filename and the starting characters look like
+ # a UNC path, then just return the value without quotes. Using the
+ # replace sequence below on a UNC path has the effect of turning
+ # the leading double slash into a single slash and then
+ # _fix_ie_filename() doesn't work correctly. See #458.
+ if not is_filename or value[:2] != '\\\\':
+ return value.replace('\\\\', '\\').replace('\\"', '"')
+ return value
def header_expand(headers):
@@ -55,7 +156,7 @@ def header_expand(headers):
collector.append(', ')
- # Remove trailing seperators.
+ # Remove trailing separators.
if collector[-1] in (', ', '; '):
del collector[-1]
@@ -63,6 +164,21 @@ def header_expand(headers):
+def randombytes(n):
+ """Return n random bytes."""
+ # Use /dev/urandom if it is available. Fall back to random module
+ # if not. It might be worthwhile to extend this function to use
+ # other platform-specific mechanisms for getting random bytes.
+ if os.path.exists("/dev/urandom"):
+ f = open("/dev/urandom")
+ s = f.read(n)
+ f.close()
+ return s
+ else:
+ L = [chr(random.randrange(0, 256)) for i in range(n)]
+ return "".join(L)
+
+
def dict_from_cookiejar(cj):
"""Returns a key/value dictionary from a CookieJar.
@@ -180,13 +296,13 @@ def unicode_from_html(content):
def stream_decode_response_unicode(iterator, r):
"""Stream decodes a iterator."""
- encoding = get_encoding_from_headers(r.headers)
- if encoding is None:
+
+ if r.encoding is None:
for item in iterator:
yield item
return
- decoder = codecs.getincrementaldecoder(encoding)(errors='replace')
+ decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace')
for chunk in iterator:
rv = decoder.decode(chunk)
if rv:
@@ -199,7 +315,7 @@ def stream_decode_response_unicode(iterator, r):
def get_unicode_from_response(r):
"""Returns the requested content back in unicode.
- :param r: Reponse object to get unicode content from.
+ :param r: Response object to get unicode content from.
Tried:
@@ -252,3 +368,14 @@ def stream_decode_gzip(iterator):
yield rv
except zlib.error:
pass
+
+
+def requote_path(path):
+ """Re-quote the given URL path component.
+
+ This function passes the given path through an unquote/quote cycle to
+ ensure that it is fully and consistenty quoted.
+ """
+ parts = path.split("/")
+ parts = (urllib.quote(urllib.unquote(part), safe="") for part in parts)
+ return "/".join(parts)