From 8081d7b15cf893435c19ea1c0d5e4392a60fe18f Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sun, 2 Sep 2012 23:09:43 -0400 Subject: [PATCH] Fixes #817. Use dicts and lists where necessary but accept both dicts and lists of 2-tuples everywhere. --- requests/compat.py | 2 ++ requests/models.py | 2 +- requests/sessions.py | 47 ++++++++++++++++------------------------- requests/structures.py | 1 + requests/utils.py | 57 ++++++++++++++++++++++++++++++++++---------------- 5 files changed, 61 insertions(+), 48 deletions(-) diff --git a/requests/compat.py b/requests/compat.py index 1d4f4a9..351b7c6 100644 --- a/requests/compat.py +++ b/requests/compat.py @@ -93,6 +93,7 @@ if is_py2: import cchardet as chardet except ImportError: from .packages import chardet + from .packages.urllib3.packages.ordered_dict import OrderedDict builtin_str = str bytes = str @@ -109,6 +110,7 @@ elif is_py3: from http.cookies import Morsel from io import StringIO from .packages import chardet2 as chardet + from collections import OrderedDict builtin_str = str str = str diff --git a/requests/models.py b/requests/models.py index 1159ad5..b02739f 100644 --- a/requests/models.py +++ b/requests/models.py @@ -34,7 +34,7 @@ from .utils import ( to_key_val_list, DEFAULT_CA_BUNDLE_PATH, parse_header_links, iter_slices) from .compat import ( cookielib, urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes, - StringIO, is_py2, chardet, json, builtin_str, numeric_types) + StringIO, is_py2, chardet, json, builtin_str) REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved) CONTENT_CHUNK_SIZE = 10 * 1024 diff --git a/requests/sessions.py b/requests/sessions.py index 1f46688..f0d4f3c 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -15,7 +15,7 @@ from .cookies import cookiejar_from_dict, remove_cookie_by_name from .defaults import defaults from .models import Request from .hooks import dispatch_hook -from .utils import header_expand, to_key_val_list +from .utils import header_expand, from_key_val_list from .packages.urllib3.poolmanager import PoolManager @@ -34,27 +34,19 @@ def merge_kwargs(local_kwarg, default_kwarg): if local_kwarg is None: return default_kwarg - kwargs = default_kwarg - # If default_kwargs is a list rather than a dictionary attempt to convert - # to dictionary. If the check fails, return local_kwargs. - if isinstance(default_kwarg, list): - try: - kwargs = dict(kwargs) - except ValueError: - return local_kwarg - # Bypass if not a dictionary (e.g. timeout) - if not hasattr(kwargs, 'items'): + if not hasattr(default_kwarg, 'items'): return local_kwarg - local_kwarg = to_key_val_list(local_kwarg) + default_kwarg = from_key_val_list(default_kwarg) + local_kwarg = from_key_val_list(local_kwarg) # Update new values. - kwargs = kwargs.copy() + kwargs = default_kwarg.copy() kwargs.update(local_kwarg) # Remove keys that are set to None. - for (k, v) in local_kwarg: + for (k, v) in local_kwarg.items(): if v is None: del kwargs[k] @@ -81,14 +73,13 @@ class Session(object): verify=True, cert=None): - #self.headers = to_key_val_list(headers or []) - self.headers = headers or {} + self.headers = from_key_val_list(headers or []) self.auth = auth self.timeout = timeout - self.proxies = to_key_val_list(proxies or []) - self.hooks = hooks or {} - self.params = to_key_val_list(params or []) - self.config = config or {} + self.proxies = from_key_val_list(proxies or []) + self.hooks = from_key_val_list(hooks or {}) + self.params = from_key_val_list(params or []) + self.config = from_key_val_list(config or {}) self.prefetch = prefetch self.verify = verify self.cert = cert @@ -171,7 +162,7 @@ class Session(object): data = [] if data is None else data files = [] if files is None else files headers = {} if headers is None else headers - params = [] if params is None else params + params = {} if params is None else params hooks = {} if hooks is None else hooks prefetch = prefetch if prefetch is not None else self.prefetch @@ -181,25 +172,23 @@ class Session(object): # Expand header values. if headers: - #e = [(k, header_expand(v)) for k, v in to_key_val_list(headers)] - #headers = e - for k, v in list(headers.items()) or {}: + for k, v in list(headers.items() or {}): headers[k] = header_expand(v) args = dict( method=method, url=url, data=data, - params=params, - headers=headers, + params=from_key_val_list(params), + headers=from_key_val_list(headers), cookies=cookies, files=files, auth=auth, - hooks=hooks, + hooks=from_key_val_list(hooks), timeout=timeout, allow_redirects=allow_redirects, - proxies=to_key_val_list(proxies), - config=config, + proxies=from_key_val_list(proxies), + config=from_key_val_list(config), prefetch=prefetch, verify=verify, cert=cert, diff --git a/requests/structures.py b/requests/structures.py index 7969c7e..3fda984 100644 --- a/requests/structures.py +++ b/requests/structures.py @@ -8,6 +8,7 @@ Data structures that power Requests. """ + class CaseInsensitiveDict(dict): """Case-insensitive Dictionary diff --git a/requests/utils.py b/requests/utils.py index 3639b8f..eb14600 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -20,7 +20,7 @@ from netrc import netrc, NetrcParseError from . import __version__ from .compat import parse_http_list as _parse_list_header -from .compat import quote, urlparse, basestring, bytes, str +from .compat import quote, urlparse, basestring, bytes, str, OrderedDict from .cookies import RequestsCookieJar, cookiejar_from_dict _hush_pyflakes = (RequestsCookieJar,) @@ -114,28 +114,49 @@ def guess_filename(obj): return name +def from_key_val_list(value): + """Take an object and test to see if it can be represented as a + dictionary. Unless it can not be represented as such, return an + OrderedDict, e.g., + + :: + + >>> from_key_val_list([('key', 'val')]) + OrderedDict([('key', 'val')]) + >>> from_key_val_list('string') + ValueError: need more than 1 value to unpack + >>> from_key_val_list({'key': 'val'}) + OrderedDict([('key', 'val')]) + """ + if value is None: + return None + + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') + + return OrderedDict(value) + + def to_key_val_list(value): """Take an object and test to see if it can be represented as a - dictionary. Unless it can not be represented as such, return a list of - tuples, e.g.,: - - >>> to_key_val_list([('key', 'val')]) - [('key', 'val')] - >>> to_key_val_list('string') - ValueError: ... - >>> to_key_val_list({'key': 'val'}) - [('key', 'val')] + dictionary. If it can be, return a list of tuples, e.g., + + :: + + >>> to_key_val_list([('key', 'val')]) + [('key', 'val')] + >>> to_key_val_list({'key': 'val'}) + [('key', 'val')] + >>> to_key_val_list('string') + ValueError: cannot encode objects that are not 2-tuples. """ if value is None: return None - try: - dict(value) - except ValueError: - raise ValueError('Unable to encode lists with elements that are not ' - '2-tuples.') + if isinstance(value, (str, bytes, bool, int)): + raise ValueError('cannot encode objects that are not 2-tuples') - if isinstance(value, dict) or hasattr(value, 'items'): + if isinstance(value, dict): value = value.items() return list(value) @@ -531,7 +552,7 @@ def parse_header_links(value): i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" """ - + links = [] replace_chars = " '\"" @@ -551,7 +572,7 @@ def parse_header_links(value): key,value = param.split("=") except ValueError: break - + link[key.strip(replace_chars)] = value.strip(replace_chars) links.append(link) -- 2.7.4