aboutsummaryrefslogtreecommitdiff
path: root/requests
diff options
context:
space:
mode:
Diffstat (limited to 'requests')
-rw-r--r--requests/__init__.py33
-rw-r--r--requests/api.py117
-rw-r--r--requests/async.py106
-rw-r--r--requests/auth.py150
-rw-r--r--requests/compat.py105
-rw-r--r--requests/defaults.py51
-rw-r--r--requests/exceptions.py41
-rw-r--r--requests/hooks.py48
-rw-r--r--requests/models.py844
-rw-r--r--requests/packages/__init__.py3
-rw-r--r--requests/packages/oreos/__init__.py3
-rw-r--r--requests/packages/oreos/core.py24
-rw-r--r--requests/packages/oreos/monkeys.py773
-rw-r--r--requests/packages/oreos/structures.py399
-rw-r--r--requests/packages/urllib3/__init__.py42
-rw-r--r--requests/packages/urllib3/_collections.py131
-rw-r--r--requests/packages/urllib3/connectionpool.py522
-rw-r--r--requests/packages/urllib3/exceptions.py67
-rw-r--r--requests/packages/urllib3/filepost.py88
-rw-r--r--requests/packages/urllib3/packages/__init__.py4
-rw-r--r--requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py47
-rw-r--r--requests/packages/urllib3/packages/six.py372
-rw-r--r--requests/packages/urllib3/packages/ssl_match_hostname/__init__.py61
-rw-r--r--requests/packages/urllib3/poolmanager.py138
-rw-r--r--requests/packages/urllib3/request.py128
-rw-r--r--requests/packages/urllib3/response.py202
-rw-r--r--requests/packages/urllib3/util.py136
-rw-r--r--requests/sessions.py306
-rw-r--r--requests/status_codes.py86
-rw-r--r--requests/structures.py66
-rw-r--r--requests/utils.py504
31 files changed, 5597 insertions, 0 deletions
diff --git a/requests/__init__.py b/requests/__init__.py
new file mode 100644
index 0000000..96ef774
--- /dev/null
+++ b/requests/__init__.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+# __
+# /__) _ _ _ _ _/ _
+# / ( (- (/ (/ (- _) / _)
+# /
+
+"""
+requests
+~~~~~~~~
+
+:copyright: (c) 2012 by Kenneth Reitz.
+:license: ISC, see LICENSE for more details.
+
+"""
+
+__title__ = 'requests'
+__version__ = '0.11.2'
+__build__ = 0x001102
+__author__ = 'Kenneth Reitz'
+__license__ = 'ISC'
+__copyright__ = 'Copyright 2012 Kenneth Reitz'
+
+
+from . import utils
+from .models import Request, Response
+from .api import request, get, head, post, patch, put, delete, options
+from .sessions import session, Session
+from .status_codes import codes
+from .exceptions import (
+ RequestException, Timeout, URLRequired,
+ TooManyRedirects, HTTPError, ConnectionError
+)
diff --git a/requests/api.py b/requests/api.py
new file mode 100644
index 0000000..e0bf346
--- /dev/null
+++ b/requests/api.py
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.api
+~~~~~~~~~~~~
+
+This module implements the Requests API.
+
+:copyright: (c) 2012 by Kenneth Reitz.
+:license: ISC, see LICENSE for more details.
+
+"""
+
+from . import sessions
+
+def request(method, url, **kwargs):
+ """Constructs and sends a :class:`Request <Request>`.
+ Returns :class:`Response <Response>` object.
+
+ :param method: method for the new :class:`Request` object.
+ :param url: URL for the new :class:`Request` object.
+ :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
+ :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
+ :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload.
+ :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
+ :param timeout: (optional) Float describing the timeout of the request.
+ :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
+ :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
+ :param return_response: (optional) If False, an un-sent Request object will returned.
+ :param session: (optional) A :class:`Session` object to be used for the request.
+ :param config: (optional) A configuration dictionary.
+ :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
+ :param prefetch: (optional) if ``True``, the response content will be immediately downloaded.
+ :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
+ """
+
+ s = kwargs.pop('session') if 'session' in kwargs else sessions.session()
+ return s.request(method=method, url=url, **kwargs)
+
+
+
+def get(url, **kwargs):
+ """Sends a GET request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return request('get', url, **kwargs)
+
+
+def options(url, **kwargs):
+ """Sends a OPTIONS request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return request('options', url, **kwargs)
+
+
+def head(url, **kwargs):
+ """Sends a HEAD request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', False)
+ return request('head', url, **kwargs)
+
+
+def post(url, data=None, **kwargs):
+ """Sends a POST request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('post', url, data=data, **kwargs)
+
+
+def put(url, data=None, **kwargs):
+ """Sends a PUT request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('put', url, data=data, **kwargs)
+
+
+def patch(url, data=None, **kwargs):
+ """Sends a PATCH request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('patch', url, data=data, **kwargs)
+
+
+def delete(url, **kwargs):
+ """Sends a DELETE request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('delete', url, **kwargs)
diff --git a/requests/async.py b/requests/async.py
new file mode 100644
index 0000000..f12cf26
--- /dev/null
+++ b/requests/async.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.async
+~~~~~~~~~~~~~~
+
+This module contains an asynchronous replica of ``requests.api``, powered
+by gevent. All API methods return a ``Request`` instance (as opposed to
+``Response``). A list of requests can be sent with ``map()``.
+"""
+
+try:
+ import gevent
+ from gevent import monkey as curious_george
+ from gevent.pool import Pool
+except ImportError:
+ raise RuntimeError('Gevent is required for requests.async.')
+
+# Monkey-patch.
+curious_george.patch_all(thread=False, select=False)
+
+from . import api
+
+
+__all__ = (
+ 'map', 'imap',
+ 'get', 'options', 'head', 'post', 'put', 'patch', 'delete', 'request'
+)
+
+
+def patched(f):
+ """Patches a given API function to not send."""
+
+ def wrapped(*args, **kwargs):
+
+ kwargs['return_response'] = False
+ kwargs['prefetch'] = True
+
+ config = kwargs.get('config', {})
+ config.update(safe_mode=True)
+
+ kwargs['config'] = config
+
+ return f(*args, **kwargs)
+
+ return wrapped
+
+
+def send(r, pool=None, prefetch=False):
+ """Sends the request object using the specified pool. If a pool isn't
+ specified this method blocks. Pools are useful because you can specify size
+ and can hence limit concurrency."""
+
+ if pool != None:
+ return pool.spawn(r.send, prefetch=prefetch)
+
+ return gevent.spawn(r.send, prefetch=prefetch)
+
+
+# Patched requests.api functions.
+get = patched(api.get)
+options = patched(api.options)
+head = patched(api.head)
+post = patched(api.post)
+put = patched(api.put)
+patch = patched(api.patch)
+delete = patched(api.delete)
+request = patched(api.request)
+
+
+def map(requests, prefetch=True, size=None):
+ """Concurrently converts a list of Requests to Responses.
+
+ :param requests: a collection of Request objects.
+ :param prefetch: If False, the content will not be downloaded immediately.
+ :param size: Specifies the number of requests to make at a time. If None, no throttling occurs.
+ """
+
+ requests = list(requests)
+
+ pool = Pool(size) if size else None
+ jobs = [send(r, pool, prefetch=prefetch) for r in requests]
+ gevent.joinall(jobs)
+
+ return [r.response for r in requests]
+
+
+def imap(requests, prefetch=True, size=2):
+ """Concurrently converts a generator object of Requests to
+ a generator of Responses.
+
+ :param requests: a generator of Request objects.
+ :param prefetch: If False, the content will not be downloaded immediately.
+ :param size: Specifies the number of requests to make at a time. default is 2
+ """
+
+ pool = Pool(size)
+
+ def send(r):
+ r.send(prefetch)
+ return r.response
+
+ for r in pool.imap_unordered(send, requests):
+ yield r
+
+ pool.join() \ No newline at end of file
diff --git a/requests/auth.py b/requests/auth.py
new file mode 100644
index 0000000..353180a
--- /dev/null
+++ b/requests/auth.py
@@ -0,0 +1,150 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.auth
+~~~~~~~~~~~~~
+
+This module contains the authentication handlers for Requests.
+"""
+
+import time
+import hashlib
+
+from base64 import b64encode
+from .compat import urlparse, str
+from .utils import randombytes, parse_dict_header
+
+
+
+def _basic_auth_str(username, password):
+ """Returns a Basic Auth string."""
+
+ return 'Basic ' + b64encode(('%s:%s' % (username, password)).encode('latin1')).strip().decode('latin1')
+
+
+class AuthBase(object):
+ """Base class that all auth implementations derive from"""
+
+ def __call__(self, r):
+ raise NotImplementedError('Auth hooks must be callable.')
+
+
+class HTTPBasicAuth(AuthBase):
+ """Attaches HTTP Basic Authentication to the given Request object."""
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+
+ def __call__(self, r):
+ r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
+ return r
+
+
+class HTTPProxyAuth(HTTPBasicAuth):
+ """Attaches HTTP Proxy Authenetication to a given Request object."""
+ def __call__(self, r):
+ r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)
+ return r
+
+
+class HTTPDigestAuth(AuthBase):
+ """Attaches HTTP Digest Authentication to the given Request object."""
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+
+ def handle_401(self, r):
+ """Takes the given response and tries digest-auth, if needed."""
+
+ r.request.deregister_hook('response', self.handle_401)
+
+ s_auth = r.headers.get('www-authenticate', '')
+
+ if 'digest' in s_auth.lower():
+
+ last_nonce = ''
+ nonce_count = 0
+
+ chal = parse_dict_header(s_auth.replace('Digest ', ''))
+
+ realm = chal['realm']
+ nonce = chal['nonce']
+ qop = chal.get('qop')
+ algorithm = chal.get('algorithm', 'MD5')
+ opaque = chal.get('opaque', None)
+
+ algorithm = algorithm.upper()
+ # lambdas assume digest modules are imported at the top level
+ if algorithm == 'MD5':
+ def md5_utf8(x):
+ if isinstance(x, str):
+ x = x.encode('utf-8')
+ return hashlib.md5(x).hexdigest()
+ hash_utf8 = md5_utf8
+ elif algorithm == 'SHA':
+ def sha_utf8(x):
+ if isinstance(x, str):
+ x = x.encode('utf-8')
+ return hashlib.sha1(x).hexdigest()
+ hash_utf8 = sha_utf8
+ # XXX MD5-sess
+ KD = lambda s, d: hash_utf8("%s:%s" % (s, d))
+
+ if hash_utf8 is None:
+ return None
+
+ # XXX not implemented yet
+ entdig = None
+ p_parsed = urlparse(r.request.url)
+ path = p_parsed.path
+ if p_parsed.query:
+ path += '?' + p_parsed.query
+
+ A1 = '%s:%s:%s' % (self.username, realm, self.password)
+ A2 = '%s:%s' % (r.request.method, path)
+
+ if qop == 'auth':
+ if nonce == last_nonce:
+ nonce_count += 1
+ else:
+ nonce_count = 1
+ last_nonce = nonce
+
+ ncvalue = '%08x' % nonce_count
+ s = str(nonce_count).encode('utf-8')
+ s += nonce.encode('utf-8')
+ s += time.ctime().encode('utf-8')
+ s += randombytes(8)
+
+ cnonce = (hashlib.sha1(s).hexdigest()[:16])
+ noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, hash_utf8(A2))
+ respdig = KD(hash_utf8(A1), noncebit)
+ elif qop is None:
+ respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2)))
+ else:
+ # XXX handle auth-int.
+ return None
+
+ # XXX should the partial digests be encoded too?
+ base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
+ 'response="%s"' % (self.username, realm, nonce, path, respdig)
+ if opaque:
+ base += ', opaque="%s"' % opaque
+ if entdig:
+ base += ', digest="%s"' % entdig
+ base += ', algorithm="%s"' % algorithm
+ if qop:
+ base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
+
+ r.request.headers['Authorization'] = 'Digest %s' % (base)
+ r.request.send(anyway=True)
+ _r = r.request.response
+ _r.history.append(r)
+
+ return _r
+
+ return r
+
+ def __call__(self, r):
+ r.register_hook('response', self.handle_401)
+ return r
diff --git a/requests/compat.py b/requests/compat.py
new file mode 100644
index 0000000..fec7a01
--- /dev/null
+++ b/requests/compat.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+"""
+pythoncompat
+"""
+
+
+import sys
+
+# -------
+# Pythons
+# -------
+
+# Syntax sugar.
+_ver = sys.version_info
+
+#: Python 2.x?
+is_py2 = (_ver[0] == 2)
+
+#: Python 3.x?
+is_py3 = (_ver[0] == 3)
+
+#: Python 3.0.x
+is_py30 = (is_py3 and _ver[1] == 0)
+
+#: Python 3.1.x
+is_py31 = (is_py3 and _ver[1] == 1)
+
+#: Python 3.2.x
+is_py32 = (is_py3 and _ver[1] == 2)
+
+#: Python 3.3.x
+is_py33 = (is_py3 and _ver[1] == 3)
+
+#: Python 3.4.x
+is_py34 = (is_py3 and _ver[1] == 4)
+
+#: Python 2.7.x
+is_py27 = (is_py2 and _ver[1] == 7)
+
+#: Python 2.6.x
+is_py26 = (is_py2 and _ver[1] == 6)
+
+#: Python 2.5.x
+is_py25 = (is_py2 and _ver[1] == 5)
+
+#: Python 2.4.x
+is_py24 = (is_py2 and _ver[1] == 4) # I'm assuming this is not by choice.
+
+
+# ---------
+# Platforms
+# ---------
+
+
+# Syntax sugar.
+_ver = sys.version.lower()
+
+is_pypy = ('pypy' in _ver)
+is_jython = ('jython' in _ver)
+is_ironpython = ('iron' in _ver)
+
+# Assume CPython, if nothing else.
+is_cpython = not any((is_pypy, is_jython, is_ironpython))
+
+# Windows-based system.
+is_windows = 'win32' in str(sys.platform).lower()
+
+# Standard Linux 2+ system.
+is_linux = ('linux' in str(sys.platform).lower())
+is_osx = ('darwin' in str(sys.platform).lower())
+is_hpux = ('hpux' in str(sys.platform).lower()) # Complete guess.
+is_solaris = ('solar==' in str(sys.platform).lower()) # Complete guess.
+
+
+# ---------
+# Specifics
+# ---------
+
+
+if is_py2:
+ from urllib import quote, unquote, urlencode
+ from urlparse import urlparse, urlunparse, urljoin, urlsplit
+ from urllib2 import parse_http_list
+ import cookielib
+ from .packages.oreos.monkeys import SimpleCookie
+ from StringIO import StringIO
+
+ bytes = str
+ str = unicode
+ basestring = basestring
+
+
+
+elif is_py3:
+ from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote
+ from urllib.request import parse_http_list
+ from http import cookiejar as cookielib
+ from http.cookies import SimpleCookie
+ from io import StringIO
+
+ str = str
+ bytes = bytes
+ basestring = (str,bytes)
+
diff --git a/requests/defaults.py b/requests/defaults.py
new file mode 100644
index 0000000..6a7ea27
--- /dev/null
+++ b/requests/defaults.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.defaults
+~~~~~~~~~~~~~~~~~
+
+This module provides the Requests configuration defaults.
+
+Configurations:
+
+:base_headers: Default HTTP headers.
+:verbose: Stream to write request logging to.
+:max_redirects: Maximum number of redirects allowed within a request.s
+:keep_alive: Reuse HTTP Connections?
+:max_retries: The number of times a request should be retried in the event of a connection failure.
+:danger_mode: If true, Requests will raise errors immediately.
+:safe_mode: If true, Requests will catch all errors.
+:strict_mode: If true, Requests will do its best to follow RFCs (e.g. POST redirects).
+:pool_maxsize: The maximium size of an HTTP connection pool.
+:pool_connections: The number of active HTTP connection pools to use.
+:encode_uri: If true, URIs will automatically be percent-encoded.
+:trust_env: If true, the surrouding environment will be trusted (environ, netrc).
+
+"""
+
+SCHEMAS = ['http', 'https']
+
+from . import __version__
+
+defaults = dict()
+
+
+defaults['base_headers'] = {
+ 'User-Agent': 'python-requests/%s' % __version__,
+ 'Accept-Encoding': ', '.join(('identity', 'deflate', 'compress', 'gzip')),
+ 'Accept': '*/*'
+}
+
+defaults['verbose'] = None
+defaults['max_redirects'] = 30
+defaults['pool_connections'] = 10
+defaults['pool_maxsize'] = 10
+defaults['max_retries'] = 0
+defaults['danger_mode'] = False
+defaults['safe_mode'] = False
+defaults['strict_mode'] = False
+defaults['keep_alive'] = True
+defaults['encode_uri'] = True
+defaults['trust_env'] = True
+
+
diff --git a/requests/exceptions.py b/requests/exceptions.py
new file mode 100644
index 0000000..1cffa80
--- /dev/null
+++ b/requests/exceptions.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.exceptions
+~~~~~~~~~~~~~~~~~~~
+
+This module contains the set of Requests' exceptions.
+
+"""
+
+class RequestException(RuntimeError):
+ """There was an ambiguous exception that occurred while handling your
+ request."""
+
+class HTTPError(RequestException):
+ """An HTTP error occurred."""
+ response = None
+
+class ConnectionError(RequestException):
+ """A Connection error occurred."""
+
+class SSLError(ConnectionError):
+ """An SSL error occurred."""
+
+class Timeout(RequestException):
+ """The request timed out."""
+
+class URLRequired(RequestException):
+ """A valid URL is required to make a request."""
+
+class TooManyRedirects(RequestException):
+ """Too many redirects."""
+
+class MissingSchema(RequestException, ValueError):
+ """The URL schema (e.g. http or https) is missing."""
+
+class InvalidSchema(RequestException, ValueError):
+ """See defaults.py for valid schemas."""
+
+class InvalidURL(RequestException, ValueError):
+ """ The URL provided was somehow invalid. """ \ No newline at end of file
diff --git a/requests/hooks.py b/requests/hooks.py
new file mode 100644
index 0000000..3560b89
--- /dev/null
+++ b/requests/hooks.py
@@ -0,0 +1,48 @@
+# -*- 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 traceback
+
+
+HOOKS = ('args', 'pre_request', 'post_request', 'response')
+
+
+def dispatch_hook(key, hooks, hook_data):
+ """Dispatches a hook dictionary on a given piece of data."""
+
+ hooks = hooks or dict()
+
+ if key in hooks:
+ hooks = hooks.get(key)
+
+ if hasattr(hooks, '__call__'):
+ hooks = [hooks]
+
+ for hook in hooks:
+ try:
+ hook_data = hook(hook_data) or hook_data
+ except Exception:
+ traceback.print_exc()
+
+ return hook_data
diff --git a/requests/models.py b/requests/models.py
new file mode 100644
index 0000000..60f58d2
--- /dev/null
+++ b/requests/models.py
@@ -0,0 +1,844 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.models
+~~~~~~~~~~~~~~~
+
+This module contains the primary objects that power Requests.
+"""
+
+import os
+from datetime import datetime
+
+from .hooks import dispatch_hook, HOOKS
+from .structures import CaseInsensitiveDict
+from .status_codes import codes
+
+from .auth import HTTPBasicAuth, HTTPProxyAuth
+from .packages.urllib3.response import HTTPResponse
+from .packages.urllib3.exceptions import MaxRetryError, LocationParseError
+from .packages.urllib3.exceptions import SSLError as _SSLError
+from .packages.urllib3.exceptions import HTTPError as _HTTPError
+from .packages.urllib3 import connectionpool, poolmanager
+from .packages.urllib3.filepost import encode_multipart_formdata
+from .defaults import SCHEMAS
+from .exceptions import (
+ ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects,
+ URLRequired, SSLError, MissingSchema, InvalidSchema, InvalidURL)
+from .utils import (
+ get_encoding_from_headers, stream_untransfer, guess_filename, requote_uri,
+ dict_from_string, stream_decode_response_unicode, get_netrc_auth,
+ DEFAULT_CA_BUNDLE_PATH)
+from .compat import (
+ urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes,
+ SimpleCookie, is_py2)
+
+# Import chardet if it is available.
+try:
+ import chardet
+except ImportError:
+ pass
+
+REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
+
+
+class Request(object):
+ """The :class:`Request <Request>` object. It carries out all functionality of
+ Requests. Recommended interface is with the Requests functions.
+ """
+
+ def __init__(self,
+ url=None,
+ headers=dict(),
+ files=None,
+ method=None,
+ data=dict(),
+ params=dict(),
+ auth=None,
+ cookies=None,
+ timeout=None,
+ redirect=False,
+ allow_redirects=False,
+ proxies=None,
+ hooks=None,
+ config=None,
+ prefetch=False,
+ _poolmanager=None,
+ verify=None,
+ session=None,
+ cert=None):
+
+ #: Dictionary of configurations for this request.
+ self.config = dict(config or [])
+
+ #: Float describes the timeout of the request.
+ # (Use socket.setdefaulttimeout() as fallback)
+ self.timeout = timeout
+
+ #: Request URL.
+ self.url = url
+
+ #: Dictionary of HTTP Headers to attach to the :class:`Request <Request>`.
+ self.headers = dict(headers or [])
+
+ #: Dictionary of files to multipart upload (``{filename: content}``).
+ self.files = None
+
+ #: HTTP Method to use.
+ self.method = method
+
+ #: Dictionary or byte of request body data to attach to the
+ #: :class:`Request <Request>`.
+ self.data = None
+
+ #: Dictionary or byte of querystring data to attach to the
+ #: :class:`Request <Request>`. The dictionary values can be lists for representing
+ #: multivalued query parameters.
+ self.params = None
+
+ #: True if :class:`Request <Request>` is part of a redirect chain (disables history
+ #: and HTTPError storage).
+ self.redirect = redirect
+
+ #: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
+ self.allow_redirects = allow_redirects
+
+ # Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
+ self.proxies = dict(proxies or [])
+
+ # If no proxies are given, allow configuration by environment variables
+ # HTTP_PROXY and HTTPS_PROXY.
+ if not self.proxies and self.config.get('trust_env'):
+ if 'HTTP_PROXY' in os.environ:
+ self.proxies['http'] = os.environ['HTTP_PROXY']
+ if 'HTTPS_PROXY' in os.environ:
+ self.proxies['https'] = os.environ['HTTPS_PROXY']
+
+ self.data, self._enc_data = self._encode_params(data)
+ self.params, self._enc_params = self._encode_params(params)
+ self.files, self._enc_files = self._encode_files(files)
+
+ #: :class:`Response <Response>` instance, containing
+ #: content and metadata of HTTP Response, once :attr:`sent <send>`.
+ self.response = Response()
+
+ #: Authentication tuple or object to attach to :class:`Request <Request>`.
+ self.auth = auth
+
+ #: CookieJar to attach to :class:`Request <Request>`.
+ self.cookies = dict(cookies or [])
+
+ #: True if Request has been sent.
+ self.sent = False
+
+ #: Event-handling hooks.
+ self.hooks = {}
+
+ for event in HOOKS:
+ self.hooks[event] = []
+
+ hooks = hooks or {}
+
+ for (k, v) in list(hooks.items()):
+ self.register_hook(event=k, hook=v)
+
+ #: Session.
+ self.session = session
+
+ #: SSL Verification.
+ self.verify = verify
+
+ #: SSL Certificate
+ self.cert = cert
+
+ #: Prefetch response content
+ self.prefetch = prefetch
+
+ if headers:
+ headers = CaseInsensitiveDict(self.headers)
+ else:
+ headers = CaseInsensitiveDict()
+
+ # Add configured base headers.
+ for (k, v) in list(self.config.get('base_headers', {}).items()):
+ if k not in headers:
+ headers[k] = v
+
+ self.headers = headers
+ self._poolmanager = _poolmanager
+
+ def __repr__(self):
+ return '<Request [%s]>' % (self.method)
+
+ def _build_response(self, resp):
+ """Build internal :class:`Response <Response>` object
+ from given response.
+ """
+
+ def build(resp):
+
+ response = Response()
+
+ # Pass settings over.
+ response.config = self.config
+
+ if resp:
+
+ # Fallback to None if there's no status_code, for whatever reason.
+ response.status_code = getattr(resp, 'status', None)
+
+ # Make headers case-insensitive.
+ response.headers = CaseInsensitiveDict(getattr(resp, 'headers', None))
+
+ # Set encoding.
+ response.encoding = get_encoding_from_headers(response.headers)
+
+ # Start off with our local cookies.
+ cookies = self.cookies or dict()
+
+ # Add new cookies from the server.
+ if 'set-cookie' in response.headers:
+ cookie_header = response.headers['set-cookie']
+ cookies = dict_from_string(cookie_header)
+
+ # Save cookies in Response.
+ response.cookies = cookies
+
+ # No exceptions were harmed in the making of this request.
+ response.error = getattr(resp, 'error', None)
+
+ # Save original response for later.
+ response.raw = resp
+ if isinstance(self.full_url, bytes):
+ response.url = self.full_url.decode('utf-8')
+ else:
+ response.url = self.full_url
+
+ return response
+
+ history = []
+
+ r = build(resp)
+
+ self.cookies.update(r.cookies)
+
+ if r.status_code in REDIRECT_STATI and not self.redirect:
+
+ while (('location' in r.headers) and
+ ((r.status_code is codes.see_other) or (self.allow_redirects))):
+
+ r.content # Consume socket so it can be released
+
+ if not len(history) < self.config.get('max_redirects'):
+ raise TooManyRedirects()
+
+ # Release the connection back into the pool.
+ r.raw.release_conn()
+
+ history.append(r)
+
+ url = r.headers['location']
+ data = self.data
+
+ # Handle redirection without scheme (see: RFC 1808 Section 4)
+ if url.startswith('//'):
+ parsed_rurl = urlparse(r.url)
+ url = '%s:%s' % (parsed_rurl.scheme, url)
+
+ # Facilitate non-RFC2616-compliant 'location' headers
+ # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
+ if not urlparse(url).netloc:
+ url = urljoin(r.url,
+ # Compliant with RFC3986, we percent
+ # encode the url.
+ requote_uri(url))
+
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
+ if r.status_code is codes.see_other:
+ method = 'GET'
+ data = None
+ else:
+ method = self.method
+
+ # Do what the browsers do if strict_mode is off...
+ if (not self.config.get('strict_mode')):
+
+ if r.status_code in (codes.moved, codes.found) and self.method == 'POST':
+ method = 'GET'
+ data = None
+
+ if (r.status_code == 303) and self.method != 'HEAD':
+ method = 'GET'
+ data = None
+
+ # Remove the cookie headers that were sent.
+ headers = self.headers
+ try:
+ del headers['Cookie']
+ except KeyError:
+ pass
+
+ request = Request(
+ url=url,
+ headers=headers,
+ files=self.files,
+ method=method,
+ params=self.session.params,
+ auth=self.auth,
+ cookies=self.cookies,
+ redirect=True,
+ data=data,
+ config=self.config,
+ timeout=self.timeout,
+ _poolmanager=self._poolmanager,
+ proxies=self.proxies,
+ verify=self.verify,
+ session=self.session,
+ cert=self.cert
+ )
+
+ request.send()
+ r = request.response
+ self.cookies.update(r.cookies)
+
+ r.history = history
+
+ self.response = r
+ self.response.request = self
+ self.response.cookies.update(self.cookies)
+
+ @staticmethod
+ def _encode_params(data):
+ """Encode parameters in a piece of data.
+
+ If the data supplied is a dictionary, encodes each parameter in it, and
+ returns a list of tuples containing the encoded parameters, and a urlencoded
+ version of that.
+
+ Otherwise, assumes the data is already encoded appropriately, and
+ returns it twice.
+ """
+
+ if isinstance(data, bytes):
+ return data, data
+
+ if hasattr(data, '__iter__') and not isinstance(data, str):
+ data = dict(data)
+
+ if hasattr(data, 'items'):
+ result = []
+ for k, vs in list(data.items()):
+ for v in isinstance(vs, list) and vs or [vs]:
+ result.append((k.encode('utf-8') if isinstance(k, str) else k,
+ v.encode('utf-8') if isinstance(v, str) else v))
+ return result, urlencode(result, doseq=True)
+ else:
+ return data, data
+
+ def _encode_files(self,files):
+
+ if (not files) or isinstance(self.data, str):
+ return None, None
+
+ try:
+ fields = self.data.copy()
+ except AttributeError:
+ fields = dict(self.data)
+
+ for (k, v) in list(files.items()):
+ # support for explicit filename
+ if isinstance(v, (tuple, list)):
+ fn, fp = v
+ else:
+ fn = guess_filename(v) or k
+ fp = v
+ fields.update({k: (fn, fp.read())})
+
+ (body, content_type) = encode_multipart_formdata(fields)
+
+ return files, (body, content_type)
+
+ @property
+ def full_url(self):
+ """Build the actual URL to use."""
+
+ if not self.url:
+ raise URLRequired()
+
+ url = self.url
+
+ # Support for unicode domain names and paths.
+ scheme, netloc, path, params, query, fragment = urlparse(url)
+
+ if not scheme:
+ raise MissingSchema("Invalid URL %r: No schema supplied" % url)
+
+ if not scheme in SCHEMAS:
+ raise InvalidSchema("Invalid scheme %r" % scheme)
+
+ netloc = netloc.encode('idna').decode('utf-8')
+
+ if not path:
+ path = '/'
+
+
+ if is_py2:
+ if isinstance(scheme, str):
+ scheme = scheme.encode('utf-8')
+ if isinstance(netloc, str):
+ netloc = netloc.encode('utf-8')
+ if isinstance(path, str):
+ path = path.encode('utf-8')
+ if isinstance(params, str):
+ params = params.encode('utf-8')
+ if isinstance(query, str):
+ query = query.encode('utf-8')
+ if isinstance(fragment, str):
+ fragment = fragment.encode('utf-8')
+
+ url = (urlunparse([scheme, netloc, path, params, query, fragment]))
+
+ if self._enc_params:
+ if urlparse(url).query:
+ url = '%s&%s' % (url, self._enc_params)
+ else:
+ url = '%s?%s' % (url, self._enc_params)
+
+ if self.config.get('encode_uri', True):
+ url = requote_uri(url)
+
+ return url
+
+ @property
+ def path_url(self):
+ """Build the path URL to use."""
+
+ url = []
+
+ p = urlsplit(self.full_url)
+
+ # Proxies use full URLs.
+ if p.scheme in self.proxies:
+ return self.full_url
+
+ path = p.path
+ if not path:
+ path = '/'
+
+ url.append(path)
+
+ query = p.query
+ if query:
+ url.append('?')
+ url.append(query)
+
+ return ''.join(url)
+
+ def register_hook(self, event, hook):
+ """Properly register a hook."""
+
+ self.hooks[event].append(hook)
+
+ def deregister_hook(self,event,hook):
+ """Deregister a previously registered hook.
+ Returns True if the hook existed, False if not.
+ """
+
+ try:
+ self.hooks[event].remove(hook)
+ return True
+ except ValueError:
+ return False
+
+ def send(self, anyway=False, prefetch=False):
+ """Sends the request. Returns True of successful, False if not.
+ If there was an HTTPError during transmission,
+ self.response.status_code will contain the HTTPError code.
+
+ Once a request is successfully sent, `sent` will equal True.
+
+ :param anyway: If True, request will be sent, even if it has
+ already been sent.
+ """
+
+ # Build the URL
+ url = self.full_url
+
+ # Logging
+ if self.config.get('verbose'):
+ self.config.get('verbose').write('%s %s %s\n' % (
+ datetime.now().isoformat(), self.method, url
+ ))
+
+ # Nottin' on you.
+ body = None
+ content_type = None
+
+ # Multi-part file uploads.
+ if self.files:
+ (body, content_type) = self._enc_files
+ else:
+ if self.data:
+
+ body = self._enc_data
+ if isinstance(self.data, str):
+ content_type = None
+ else:
+ content_type = 'application/x-www-form-urlencoded'
+
+ # Add content-type if it wasn't explicitly provided.
+ if (content_type) and (not 'content-type' in self.headers):
+ self.headers['Content-Type'] = content_type
+
+ # Use .netrc auth if none was provided.
+ if not self.auth and self.config.get('trust_env'):
+ self.auth = get_netrc_auth(url)
+
+ if self.auth:
+ if isinstance(self.auth, tuple) and len(self.auth) == 2:
+ # special-case basic HTTP auth
+ self.auth = HTTPBasicAuth(*self.auth)
+
+ # Allow auth to make its changes.
+ r = self.auth(self)
+
+ # Update self to reflect the auth changes.
+ self.__dict__.update(r.__dict__)
+
+ _p = urlparse(url)
+ proxy = self.proxies.get(_p.scheme)
+
+ if proxy:
+ conn = poolmanager.proxy_from_url(proxy)
+ _proxy = urlparse(proxy)
+ if '@' in _proxy.netloc:
+ auth, url = _proxy.netloc.split('@', 1)
+ self.proxy_auth = HTTPProxyAuth(*auth.split(':', 1))
+ r = self.proxy_auth(self)
+ self.__dict__.update(r.__dict__)
+ else:
+ # Check to see if keep_alive is allowed.
+ try:
+ if self.config.get('keep_alive'):
+ conn = self._poolmanager.connection_from_url(url)
+ else:
+ conn = connectionpool.connection_from_url(url)
+ except LocationParseError as e:
+ raise InvalidURL(e)
+
+ if url.startswith('https') and self.verify:
+
+ cert_loc = None
+
+ # Allow self-specified cert location.
+ if self.verify is not True:
+ cert_loc = self.verify
+
+ # Look for configuration.
+ if not cert_loc and self.config.get('trust_env'):
+ cert_loc = os.environ.get('REQUESTS_CA_BUNDLE')
+
+ # Curl compatibility.
+ if not cert_loc and self.config.get('trust_env'):
+ cert_loc = os.environ.get('CURL_CA_BUNDLE')
+
+ if not cert_loc:
+ cert_loc = DEFAULT_CA_BUNDLE_PATH
+
+ if not cert_loc:
+ raise Exception("Could not find a suitable SSL CA certificate bundle.")
+
+ conn.cert_reqs = 'CERT_REQUIRED'
+ conn.ca_certs = cert_loc
+ else:
+ conn.cert_reqs = 'CERT_NONE'
+ conn.ca_certs = None
+
+ if self.cert and self.verify:
+ if len(self.cert) == 2:
+ conn.cert_file = self.cert[0]
+ conn.key_file = self.cert[1]
+ else:
+ conn.cert_file = self.cert
+
+ if not self.sent or anyway:
+
+ if self.cookies:
+
+ # Skip if 'cookie' header is explicitly set.
+ if 'cookie' not in self.headers:
+
+ # Simple cookie with our dict.
+ c = SimpleCookie()
+ for (k, v) in list(self.cookies.items()):
+ c[k] = v
+
+ # Turn it into a header.
+ cookie_header = c.output(header='', sep='; ').strip()
+
+ # Attach Cookie header to request.
+ self.headers['Cookie'] = cookie_header
+
+ # Pre-request hook.
+ r = dispatch_hook('pre_request', self.hooks, self)
+ self.__dict__.update(r.__dict__)
+
+ try:
+ # The inner try .. except re-raises certain exceptions as
+ # internal exception types; the outer suppresses exceptions
+ # when safe mode is set.
+ try:
+ # Send the request.
+ r = conn.urlopen(
+ method=self.method,
+ url=self.path_url,
+ body=body,
+ headers=self.headers,
+ redirect=False,
+ assert_same_host=False,
+ preload_content=False,
+ decode_content=False,
+ retries=self.config.get('max_retries', 0),
+ timeout=self.timeout,
+ )
+ self.sent = True
+
+ except MaxRetryError as e:
+ raise ConnectionError(e)
+
+ except (_SSLError, _HTTPError) as e:
+ if self.verify and isinstance(e, _SSLError):
+ raise SSLError(e)
+
+ raise Timeout('Request timed out.')
+
+ except RequestException as e:
+ if self.config.get('safe_mode', False):
+ # In safe mode, catch the exception and attach it to
+ # a blank urllib3.HTTPResponse object.
+ r = HTTPResponse()
+ r.error = e
+ else:
+ raise
+
+ self._build_response(r)
+
+ # Response manipulation hook.
+ self.response = dispatch_hook('response', self.hooks, self.response)
+
+ # Post-request hook.
+ r = dispatch_hook('post_request', self.hooks, self)
+ self.__dict__.update(r.__dict__)
+
+ # If prefetch is True, mark content as consumed.
+ if prefetch or self.prefetch:
+ # Save the response.
+ self.response.content
+
+ if self.config.get('danger_mode'):
+ self.response.raise_for_status()
+
+ return self.sent
+
+
+class Response(object):
+ """The core :class:`Response <Response>` object. All
+ :class:`Request <Request>` objects contain a
+ :class:`response <Response>` attribute, which is an instance
+ of this class.
+ """
+
+ def __init__(self):
+
+ self._content = False
+ self._content_consumed = False
+
+ #: Integer Code of responded HTTP Status.
+ self.status_code = None
+
+ #: Case-insensitive Dictionary of Response Headers.
+ #: For example, ``headers['content-encoding']`` will return the
+ #: value of a ``'Content-Encoding'`` response header.
+ self.headers = CaseInsensitiveDict()
+
+ #: File-like object representation of response (for advanced usage).
+ self.raw = None
+
+ #: Final URL location of Response.
+ self.url = None
+
+ #: Resulting :class:`HTTPError` of request, if one occurred.
+ self.error = None
+
+ #: Encoding to decode with when accessing r.content.
+ self.encoding = None
+
+ #: A list of :class:`Response <Response>` objects from
+ #: the history of the Request. Any redirect responses will end
+ #: up here.
+ self.history = []
+
+ #: The :class:`Request <Request>` that created the Response.
+ self.request = None
+
+ #: A dictionary of Cookies the server sent back.
+ self.cookies = {}
+
+ #: Dictionary of configurations for this request.
+ self.config = {}
+
+ def __repr__(self):
+ return '<Response [%s]>' % (self.status_code)
+
+ def __bool__(self):
+ """Returns true if :attr:`status_code` is 'OK'."""
+ return self.ok
+
+ def __nonzero__(self):
+ """Returns true if :attr:`status_code` is 'OK'."""
+ return self.ok
+
+ @property
+ def ok(self):
+ try:
+ self.raise_for_status()
+ except RequestException:
+ return False
+ return True
+
+ def iter_content(self, chunk_size=1, decode_unicode=False):
+ """Iterates over the response data. This avoids reading the content
+ at once into memory for large responses. The chunk size is the number
+ of bytes it should read into memory. This is not necessarily the
+ length of each item returned as decoding can take place.
+ """
+ if self._content_consumed:
+ raise RuntimeError(
+ 'The content for this response was already consumed'
+ )
+
+ def generate():
+ while 1:
+ chunk = self.raw.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+ self._content_consumed = True
+
+ gen = stream_untransfer(generate(), self)
+
+ if decode_unicode:
+ gen = stream_decode_response_unicode(gen, self)
+
+ return gen
+
+ def iter_lines(self, chunk_size=10 * 1024, decode_unicode=None):
+ """Iterates over the response data, one line at a time. This
+ avoids reading the content at once into memory for large
+ responses.
+ """
+
+ pending = None
+
+ for chunk in self.iter_content(
+ chunk_size=chunk_size,
+ decode_unicode=decode_unicode):
+
+ if pending is not None:
+ chunk = pending + chunk
+ lines = chunk.splitlines()
+
+ if lines[-1][-1] == chunk[-1]:
+ pending = lines.pop()
+ else:
+ pending = None
+
+ for line in lines:
+ yield line
+
+ if pending is not None:
+ yield pending
+
+ @property
+ def content(self):
+ """Content of the response, in bytes."""
+
+ if self._content is False:
+ # Read the contents.
+ try:
+ if self._content_consumed:
+ raise RuntimeError(
+ 'The content for this response was already consumed')
+
+ if self.status_code is 0:
+ self._content = None
+ else:
+ self._content = bytes().join(self.iter_content()) or bytes()
+
+ except AttributeError:
+ self._content = None
+
+ self._content_consumed = True
+ return self._content
+
+ def _detected_encoding(self):
+ try:
+ detected = chardet.detect(self.content) or {}
+ return detected.get('encoding')
+
+ # Trust that chardet isn't available or something went terribly wrong.
+ except Exception:
+ pass
+
+
+ @property
+ def text(self):
+ """Content of the response, in unicode.
+
+ if Response.encoding is None and chardet module is available, encoding
+ will be guessed.
+ """
+
+ # Try charset from content-type
+ content = None
+ encoding = self.encoding
+
+ # Fallback to auto-detected encoding if chardet is available.
+ if self.encoding is None:
+ encoding = self._detected_encoding()
+
+ # Decode unicode from given encoding.
+ try:
+ content = str(self.content, encoding, errors='replace')
+ except LookupError:
+ # A LookupError is raised if the encoding was not found which could
+ # indicate a misspelling or similar mistake.
+ #
+ # So we try blindly encoding.
+ content = str(self.content, errors='replace')
+ except (UnicodeError, TypeError):
+ pass
+
+ return content
+
+ def raise_for_status(self, allow_redirects=True):
+ """Raises stored :class:`HTTPError` or :class:`URLError`, if one occurred."""
+
+ if self.error:
+ raise self.error
+
+ if (self.status_code >= 300) and (self.status_code < 400) and not allow_redirects:
+ http_error = HTTPError('%s Redirection' % self.status_code)
+ http_error.response = self
+ raise http_error
+
+ elif (self.status_code >= 400) and (self.status_code < 500):
+ http_error = HTTPError('%s Client Error' % self.status_code)
+ http_error.response = self
+ raise http_error
+
+
+ elif (self.status_code >= 500) and (self.status_code < 600):
+ http_error = HTTPError('%s Server Error' % self.status_code)
+ http_error.response = self
+ raise http_error
diff --git a/requests/packages/__init__.py b/requests/packages/__init__.py
new file mode 100644
index 0000000..d62c4b7
--- /dev/null
+++ b/requests/packages/__init__.py
@@ -0,0 +1,3 @@
+from __future__ import absolute_import
+
+from . import urllib3
diff --git a/requests/packages/oreos/__init__.py b/requests/packages/oreos/__init__.py
new file mode 100644
index 0000000..d01340f
--- /dev/null
+++ b/requests/packages/oreos/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from .core import dict_from_string \ No newline at end of file
diff --git a/requests/packages/oreos/core.py b/requests/packages/oreos/core.py
new file mode 100644
index 0000000..359d744
--- /dev/null
+++ b/requests/packages/oreos/core.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+"""
+oreos.core
+~~~~~~~~~~
+
+The creamy white center.
+"""
+
+from .monkeys import SimpleCookie
+
+
+def dict_from_string(s):
+ """Returns a MultiDict with Cookies."""
+
+ cookies = dict()
+
+ c = SimpleCookie()
+ c.load(s)
+
+ for k,v in c.items():
+ cookies.update({k: v.value})
+
+ return cookies \ No newline at end of file
diff --git a/requests/packages/oreos/monkeys.py b/requests/packages/oreos/monkeys.py
new file mode 100644
index 0000000..2cf9016
--- /dev/null
+++ b/requests/packages/oreos/monkeys.py
@@ -0,0 +1,773 @@
+# -*- coding: utf-8 -*-
+
+"""
+oreos.monkeys
+~~~~~~~~~~~~~
+
+Monkeypatches.
+"""
+#!/usr/bin/env python
+#
+
+####
+# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
+#
+# All Rights Reserved
+#
+# Permission to use, copy, modify, and distribute this software
+# and its documentation for any purpose and without fee is hereby
+# granted, provided that the above copyright notice appear in all
+# copies and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Timothy O'Malley not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
+# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+####
+#
+# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
+# by Timothy O'Malley <timo@alum.mit.edu>
+#
+# Cookie.py is a Python module for the handling of HTTP
+# cookies as a Python dictionary. See RFC 2109 for more
+# information on cookies.
+#
+# The original idea to treat Cookies as a dictionary came from
+# Dave Mitchell (davem@magnet.com) in 1995, when he released the
+# first version of nscookie.py.
+#
+####
+
+r"""
+Here's a sample session to show how to use this module.
+At the moment, this is the only documentation.
+
+The Basics
+----------
+
+Importing is easy..
+
+ >>> import Cookie
+
+Most of the time you start by creating a cookie. Cookies come in
+three flavors, each with slightly different encoding semantics, but
+more on that later.
+
+ >>> C = Cookie.SimpleCookie()
+ >>> C = Cookie.SerialCookie()
+ >>> C = Cookie.SmartCookie()
+
+[Note: Long-time users of Cookie.py will remember using
+Cookie.Cookie() to create an Cookie object. Although deprecated, it
+is still supported by the code. See the Backward Compatibility notes
+for more information.]
+
+Once you've created your Cookie, you can add values just as if it were
+a dictionary.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["fig"] = "newton"
+ >>> C["sugar"] = "wafer"
+ >>> C.output()
+ 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
+
+Notice that the printable representation of a Cookie is the
+appropriate format for a Set-Cookie: header. This is the
+default behavior. You can change the header and printed
+attributes by using the .output() function
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["rocky"] = "road"
+ >>> C["rocky"]["path"] = "/cookie"
+ >>> print C.output(header="Cookie:")
+ Cookie: rocky=road; Path=/cookie
+ >>> print C.output(attrs=[], header="Cookie:")
+ Cookie: rocky=road
+
+The load() method of a Cookie extracts cookies from a string. In a
+CGI script, you would use this method to extract the cookies from the
+HTTP_COOKIE environment variable.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C.load("chips=ahoy; vienna=finger")
+ >>> C.output()
+ 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
+
+The load() method is darn-tootin smart about identifying cookies
+within a string. Escaped quotation marks, nested semicolons, and other
+such trickeries do not confuse it.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
+ >>> print C
+ Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
+
+Each element of the Cookie also supports all of the RFC 2109
+Cookie attributes. Here's an example which sets the Path
+attribute.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["oreo"] = "doublestuff"
+ >>> C["oreo"]["path"] = "/"
+ >>> print C
+ Set-Cookie: oreo=doublestuff; Path=/
+
+Each dictionary element has a 'value' attribute, which gives you
+back the value associated with the key.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["twix"] = "none for you"
+ >>> C["twix"].value
+ 'none for you'
+
+
+A Bit More Advanced
+-------------------
+
+As mentioned before, there are three different flavors of Cookie
+objects, each with different encoding/decoding semantics. This
+section briefly discusses the differences.
+
+SimpleCookie
+
+The SimpleCookie expects that all values should be standard strings.
+Just to be sure, SimpleCookie invokes the str() builtin to convert
+the value to a string, when the values are set dictionary-style.
+
+ >>> C = Cookie.SimpleCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ '7'
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
+
+
+SerialCookie
+
+The SerialCookie expects that all values should be serialized using
+cPickle (or pickle, if cPickle isn't available). As a result of
+serializing, SerialCookie can save almost any Python object to a
+value, and recover the exact same object when the cookie has been
+returned. (SerialCookie can yield some strange-looking cookie
+values, however.)
+
+ >>> C = Cookie.SerialCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ 7
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
+
+Be warned, however, if SerialCookie cannot de-serialize a value (because
+it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
+
+
+SmartCookie
+
+The SmartCookie combines aspects of each of the other two flavors.
+When setting a value in a dictionary-fashion, the SmartCookie will
+serialize (ala cPickle) the value *if and only if* it isn't a
+Python string. String objects are *not* serialized. Similarly,
+when the load() method parses out values, it attempts to de-serialize
+the value. If it fails, then it fallsback to treating the value
+as a string.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ 7
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
+
+
+Backwards Compatibility
+-----------------------
+
+In order to keep compatibilty with earlier versions of Cookie.py,
+it is still possible to use Cookie.Cookie() to create a Cookie. In
+fact, this simply returns a SmartCookie.
+
+ >>> C = Cookie.Cookie()
+ >>> print C.__class__.__name__
+ SmartCookie
+
+
+Finis.
+""" #"
+# ^
+# |----helps out font-lock
+
+#
+# Import our required modules
+#
+import string
+
+try:
+ from cPickle import dumps, loads
+except ImportError:
+ from pickle import dumps, loads
+
+import re, warnings
+
+__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
+ "SmartCookie","Cookie"]
+
+_nulljoin = ''.join
+_semispacejoin = '; '.join
+_spacejoin = ' '.join
+
+#
+# Define an exception visible to External modules
+#
+class CookieError(Exception):
+ pass
+
+
+# These quoting routines conform to the RFC2109 specification, which in
+# turn references the character definitions from RFC2068. They provide
+# a two-way quoting algorithm. Any non-text character is translated
+# into a 4 character sequence: a forward-slash followed by the
+# three-digit octal equivalent of the character. Any '\' or '"' is
+# quoted with a preceeding '\' slash.
+#
+# These are taken from RFC2068 and RFC2109.
+# _RFC2965Forbidden is the list of forbidden chars we accept anyway
+# _LegalChars is the list of chars which don't require "'s
+# _Translator hash-table for fast quoting
+#
+_RFC2965Forbidden = "[]:{}="
+_LegalChars = ( string.ascii_letters + string.digits +
+ "!#$%&'*+-.^_`|~_@" + _RFC2965Forbidden )
+_Translator = {
+ '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
+ '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
+ '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
+ '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
+ '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
+ '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
+ '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
+ '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
+ '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
+ '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
+ '\036' : '\\036', '\037' : '\\037',
+
+ # Because of the way browsers really handle cookies (as opposed
+ # to what the RFC says) we also encode , and ;
+
+ ',' : '\\054', ';' : '\\073',
+
+ '"' : '\\"', '\\' : '\\\\',
+
+ '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
+ '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
+ '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
+ '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
+ '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
+ '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
+ '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
+ '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
+ '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
+ '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
+ '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
+ '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
+ '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
+ '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
+ '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
+ '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
+ '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
+ '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
+ '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
+ '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
+ '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
+ '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
+ '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
+ '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
+ '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
+ '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
+ '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
+ '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
+ '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
+ '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
+ '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
+ '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
+ '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
+ '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
+ '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
+ '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
+ '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
+ '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
+ '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
+ '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
+ '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
+ '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
+ '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
+ }
+
+_idmap = ''.join(chr(x) for x in range(256))
+
+def _quote(str, LegalChars=_LegalChars,
+ idmap=_idmap, translate=string.translate):
+ #
+ # If the string does not need to be double-quoted,
+ # then just return the string. Otherwise, surround
+ # the string in doublequotes and precede quote (with a \)
+ # special characters.
+ #
+ if "" == translate(str, idmap, LegalChars):
+ return str
+ else:
+ return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
+# end _quote
+
+
+_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
+_QuotePatt = re.compile(r"[\\].")
+
+def _unquote(str):
+ # If there aren't any doublequotes,
+ # then there can't be any special characters. See RFC 2109.
+ if len(str) < 2:
+ return str
+ if str[0] != '"' or str[-1] != '"':
+ return str
+
+ # We have to assume that we must decode this string.
+ # Down to work.
+
+ # Remove the "s
+ str = str[1:-1]
+
+ # Check for special sequences. Examples:
+ # \012 --> \n
+ # \" --> "
+ #
+ i = 0
+ n = len(str)
+ res = []
+ while 0 <= i < n:
+ Omatch = _OctalPatt.search(str, i)
+ Qmatch = _QuotePatt.search(str, i)
+ if not Omatch and not Qmatch: # Neither matched
+ res.append(str[i:])
+ break
+ # else:
+ j = k = -1
+ if Omatch: j = Omatch.start(0)
+ if Qmatch: k = Qmatch.start(0)
+ if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
+ res.append(str[i:k])
+ res.append(str[k+1])
+ i = k+2
+ else: # OctalPatt matched
+ res.append(str[i:j])
+ res.append( chr( int(str[j+1:j+4], 8) ) )
+ i = j+4
+ return _nulljoin(res)
+# end _unquote
+
+# The _getdate() routine is used to set the expiration time in
+# the cookie's HTTP header. By default, _getdate() returns the
+# current time in the appropriate "expires" format for a
+# Set-Cookie header. The one optional argument is an offset from
+# now, in seconds. For example, an offset of -3600 means "one hour ago".
+# The offset may be a floating point number.
+#
+
+_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+_monthname = [None,
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
+ from time import gmtime, time
+ now = time()
+ year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
+ return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
+ (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
+
+
+#
+# A class to hold ONE key,value pair.
+# In a cookie, each such pair may have several attributes.
+# so this class is used to keep the attributes associated
+# with the appropriate key,value pair.
+# This class also includes a coded_value attribute, which
+# is used to hold the network representation of the
+# value. This is most useful when Python objects are
+# pickled for network transit.
+#
+
+class Morsel(dict):
+ # RFC 2109 lists these attributes as reserved:
+ # path comment domain
+ # max-age secure version
+ #
+ # For historical reasons, these attributes are also reserved:
+ # expires
+ #
+ # This is an extension from Microsoft:
+ # httponly
+ #
+ # This dictionary provides a mapping from the lowercase
+ # variant on the left to the appropriate traditional
+ # formatting on the right.
+ _reserved = { "expires" : "expires",
+ "path" : "Path",
+ "comment" : "Comment",
+ "domain" : "Domain",
+ "max-age" : "Max-Age",
+ "secure" : "secure",
+ "httponly" : "httponly",
+ "version" : "Version",
+ }
+
+ def __init__(self):
+ # Set defaults
+ self.key = self.value = self.coded_value = None
+
+ # Set default attributes
+ for K in self._reserved:
+ dict.__setitem__(self, K, "")
+ # end __init__
+
+ def __setitem__(self, K, V):
+ K = K.lower()
+ if not K in self._reserved:
+ raise CookieError("Invalid Attribute %s" % K)
+ dict.__setitem__(self, K, V)
+ # end __setitem__
+
+ def isReservedKey(self, K):
+ return K.lower() in self._reserved
+ # end isReservedKey
+
+ def set(self, key, val, coded_val,
+ LegalChars=_LegalChars,
+ idmap=_idmap, translate=string.translate):
+ # First we verify that the key isn't a reserved word
+ # Second we make sure it only contains legal characters
+ if key.lower() in self._reserved:
+ raise CookieError("Attempt to set a reserved key: %s" % key)
+ if "" != translate(key, idmap, LegalChars):
+ raise CookieError("Illegal key value: %s" % key)
+
+ # It's a good key, so save it.
+ self.key = key
+ self.value = val
+ self.coded_value = coded_val
+ # end set
+
+ def output(self, attrs=None, header = "Set-Cookie:"):
+ return "%s %s" % ( header, self.OutputString(attrs) )
+
+ __str__ = output
+
+ def __repr__(self):
+ return '<%s: %s=%s>' % (self.__class__.__name__,
+ self.key, repr(self.value) )
+
+ def js_output(self, attrs=None):
+ # Print javascript
+ return """
+ <script type="text/javascript">
+ <!-- begin hiding
+ document.cookie = \"%s\";
+ // end hiding -->
+ </script>
+ """ % ( self.OutputString(attrs).replace('"',r'\"'), )
+ # end js_output()
+
+ def OutputString(self, attrs=None):
+ # Build up our result
+ #
+ result = []
+ RA = result.append
+
+ # First, the key=value pair
+ RA("%s=%s" % (self.key, self.coded_value))
+
+ # Now add any defined attributes
+ if attrs is None:
+ attrs = self._reserved
+ items = self.items()
+ items.sort()
+ for K,V in items:
+ if V == "": continue
+ if K not in attrs: continue
+ if K == "expires" and type(V) == type(1):
+ RA("%s=%s" % (self._reserved[K], _getdate(V)))
+ elif K == "max-age" and type(V) == type(1):
+ RA("%s=%d" % (self._reserved[K], V))
+ elif K == "secure":
+ RA(str(self._reserved[K]))
+ elif K == "httponly":
+ RA(str(self._reserved[K]))
+ else:
+ RA("%s=%s" % (self._reserved[K], V))
+
+ # Return the result
+ return _semispacejoin(result)
+ # end OutputString
+# end Morsel class
+
+
+
+#
+# Pattern for finding cookie
+#
+# This used to be strict parsing based on the RFC2109 and RFC2068
+# specifications. I have since discovered that MSIE 3.0x doesn't
+# follow the character rules outlined in those specs. As a
+# result, the parsing rules here are less strict.
+#
+
+_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=\[\]\_]"
+
+_CookiePattern = re.compile(
+ r"(?x)" # This is a Verbose pattern
+ r"(?P<key>" # Start of group 'key'
+ ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
+ r")" # End of group 'key'
+ r"\s*=\s*" # Equal Sign
+ r"(?P<val>" # Start of group 'val'
+ r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
+ r"|" # or
+ r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
+ r"|" # or
+ ""+ _LegalCharsPatt +"*" # Any word or empty string
+ r")" # End of group 'val'
+ r"\s*;?" # Probably ending in a semi-colon
+ )
+
+
+# At long last, here is the cookie class.
+# Using this class is almost just like using a dictionary.
+# See this module's docstring for example usage.
+#
+class BaseCookie(dict):
+ # A container class for a set of Morsels
+ #
+
+ def value_decode(self, val):
+ """real_value, coded_value = value_decode(STRING)
+ Called prior to setting a cookie's value from the network
+ representation. The VALUE is the value read from HTTP
+ header.
+ Override this function to modify the behavior of cookies.
+ """
+ return val, val
+ # end value_encode
+
+ def value_encode(self, val):
+ """real_value, coded_value = value_encode(VALUE)
+ Called prior to setting a cookie's value from the dictionary
+ representation. The VALUE is the value being assigned.
+ Override this function to modify the behavior of cookies.
+ """
+ strval = str(val)
+ return strval, strval
+ # end value_encode
+
+ def __init__(self, input=None):
+ if input: self.load(input)
+ # end __init__
+
+ def __set(self, key, real_value, coded_value):
+ """Private method for setting a cookie's value"""
+ M = self.get(key, Morsel())
+ M.set(key, real_value, coded_value)
+ dict.__setitem__(self, key, M)
+ # end __set
+
+ def __setitem__(self, key, value):
+ """Dictionary style assignment."""
+ rval, cval = self.value_encode(value)
+ self.__set(key, rval, cval)
+ # end __setitem__
+
+ def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
+ """Return a string suitable for HTTP."""
+ result = []
+ items = self.items()
+ items.sort()
+ for K,V in items:
+ result.append( V.output(attrs, header) )
+ return sep.join(result)
+ # end output
+
+ __str__ = output
+
+ def __repr__(self):
+ L = []
+ items = self.items()
+ items.sort()
+ for K,V in items:
+ L.append( '%s=%s' % (K,repr(V.value) ) )
+ return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
+
+ def js_output(self, attrs=None):
+ """Return a string suitable for JavaScript."""
+ result = []
+ items = self.items()
+ items.sort()
+ for K,V in items:
+ result.append( V.js_output(attrs) )
+ return _nulljoin(result)
+ # end js_output
+
+ def load(self, rawdata):
+ """Load cookies from a string (presumably HTTP_COOKIE) or
+ from a dictionary. Loading cookies from a dictionary 'd'
+ is equivalent to calling:
+ map(Cookie.__setitem__, d.keys(), d.values())
+ """
+ if type(rawdata) == type(""):
+ self.__ParseString(rawdata)
+ else:
+ # self.update() wouldn't call our custom __setitem__
+ for k, v in rawdata.items():
+ self[k] = v
+ return
+ # end load()
+
+ def __ParseString(self, str, patt=_CookiePattern):
+ i = 0 # Our starting point
+ n = len(str) # Length of string
+ M = None # current morsel
+
+ while 0 <= i < n:
+ # Start looking for a cookie
+ match = patt.search(str, i)
+ if not match: break # No more cookies
+
+ K,V = match.group("key"), match.group("val")
+ i = match.end(0)
+
+ # Parse the key, value in case it's metainfo
+ if K[0] == "$":
+ # We ignore attributes which pertain to the cookie
+ # mechanism as a whole. See RFC 2109.
+ # (Does anyone care?)
+ if M:
+ M[ K[1:] ] = V
+ elif K.lower() in Morsel._reserved:
+ if M:
+ M[ K ] = _unquote(V)
+ else:
+ rval, cval = self.value_decode(V)
+ self.__set(K, rval, cval)
+ M = self[K]
+ # end __ParseString
+# end BaseCookie class
+
+class SimpleCookie(BaseCookie):
+ """SimpleCookie
+ SimpleCookie supports strings as cookie values. When setting
+ the value using the dictionary assignment notation, SimpleCookie
+ calls the builtin str() to convert the value to a string. Values
+ received from HTTP are kept as strings.
+ """
+ def value_decode(self, val):
+ return _unquote( val ), val
+ def value_encode(self, val):
+ strval = str(val)
+ return strval, _quote( strval )
+# end SimpleCookie
+
+class SerialCookie(BaseCookie):
+ """SerialCookie
+ SerialCookie supports arbitrary objects as cookie values. All
+ values are serialized (using cPickle) before being sent to the
+ client. All incoming values are assumed to be valid Pickle
+ representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
+ FORMAT, THEN AN EXCEPTION WILL BE RAISED.
+
+ Note: Large cookie values add overhead because they must be
+ retransmitted on every HTTP transaction.
+
+ Note: HTTP has a 2k limit on the size of a cookie. This class
+ does not check for this limit, so be careful!!!
+ """
+ def __init__(self, input=None):
+ warnings.warn("SerialCookie class is insecure; do not use it",
+ DeprecationWarning)
+ BaseCookie.__init__(self, input)
+ # end __init__
+ def value_decode(self, val):
+ # This could raise an exception!
+ return loads( _unquote(val) ), val
+ def value_encode(self, val):
+ return val, _quote( dumps(val) )
+# end SerialCookie
+
+class SmartCookie(BaseCookie):
+ """SmartCookie
+ SmartCookie supports arbitrary objects as cookie values. If the
+ object is a string, then it is quoted. If the object is not a
+ string, however, then SmartCookie will use cPickle to serialize
+ the object into a string representation.
+
+ Note: Large cookie values add overhead because they must be
+ retransmitted on every HTTP transaction.
+
+ Note: HTTP has a 2k limit on the size of a cookie. This class
+ does not check for this limit, so be careful!!!
+ """
+ def __init__(self, input=None):
+ warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
+ DeprecationWarning)
+ BaseCookie.__init__(self, input)
+ # end __init__
+ def value_decode(self, val):
+ strval = _unquote(val)
+ try:
+ return loads(strval), val
+ except:
+ return strval, val
+ def value_encode(self, val):
+ if type(val) == type(""):
+ return val, _quote(val)
+ else:
+ return val, _quote( dumps(val) )
+# end SmartCookie
+
+
+###########################################################
+# Backwards Compatibility: Don't break any existing code!
+
+# We provide Cookie() as an alias for SmartCookie()
+Cookie = SmartCookie
+
+#
+###########################################################
+
+def _test():
+ import doctest, Cookie
+ return doctest.testmod(Cookie)
+
+if __name__ == "__main__":
+ _test()
+
+
+#Local Variables:
+#tab-width: 4
+#end:
diff --git a/requests/packages/oreos/structures.py b/requests/packages/oreos/structures.py
new file mode 100644
index 0000000..8329277
--- /dev/null
+++ b/requests/packages/oreos/structures.py
@@ -0,0 +1,399 @@
+# -*- coding: utf-8 -*-
+
+"""
+oreos.structures
+~~~~~~~~~~~~~~~~
+
+The plastic blue packaging.
+
+This is mostly directly stolen from mitsuhiko/werkzeug.
+"""
+
+__all__ = ('MultiDict',)
+
+class _Missing(object):
+
+ def __repr__(self):
+ return 'no value'
+
+ def __reduce__(self):
+ return '_missing'
+
+_missing = _Missing()
+
+
+
+def iter_multi_items(mapping):
+ """Iterates over the items of a mapping yielding keys and values
+ without dropping any from more complex structures.
+ """
+ if isinstance(mapping, MultiDict):
+ for item in mapping.iteritems(multi=True):
+ yield item
+ elif isinstance(mapping, dict):
+ for key, value in mapping.iteritems():
+ if isinstance(value, (tuple, list)):
+ for value in value:
+ yield key, value
+ else:
+ yield key, value
+ else:
+ for item in mapping:
+ yield item
+
+
+
+class TypeConversionDict(dict):
+ """Works like a regular dict but the :meth:`get` method can perform
+ type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
+ are subclasses of this class and provide the same feature.
+
+ .. versionadded:: 0.5
+ """
+
+ def get(self, key, default=None, type=None):
+ """Return the default value if the requested data doesn't exist.
+ If `type` is provided and is a callable it should convert the value,
+ return it or raise a :exc:`ValueError` if that is not possible. In
+ this case the function will return the default as if the value was not
+ found:
+
+ >>> d = TypeConversionDict(foo='42', bar='blub')
+ >>> d.get('foo', type=int)
+ 42
+ >>> d.get('bar', -1, type=int)
+ -1
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key can't
+ be looked up. If not further specified `None` is
+ returned.
+ :param type: A callable that is used to cast the value in the
+ :class:`MultiDict`. If a :exc:`ValueError` is raised
+ by this callable the default value is returned.
+ """
+ try:
+ rv = self[key]
+ if type is not None:
+ rv = type(rv)
+ except (KeyError, ValueError):
+ rv = default
+ return rv
+
+
+class MultiDict(TypeConversionDict):
+ """A :class:`MultiDict` is a dictionary subclass customized to deal with
+ multiple values for the same key which is for example used by the parsing
+ functions in the wrappers. This is necessary because some HTML form
+ elements pass multiple values for the same key.
+
+ :class:`MultiDict` implements all standard dictionary methods.
+ Internally, it saves all values for a key as a list, but the standard dict
+ access methods will only return the first value for a key. If you want to
+ gain access to the other values, too, you have to use the `list` methods as
+ explained below.
+
+ Basic Usage:
+
+ >>> d = MultiDict([('a', 'b'), ('a', 'c')])
+ >>> d
+ MultiDict([('a', 'b'), ('a', 'c')])
+ >>> d['a']
+ 'b'
+ >>> d.getlist('a')
+ ['b', 'c']
+ >>> 'a' in d
+ True
+
+ It behaves like a normal dict thus all dict functions will only return the
+ first value when multiple values for one key are found.
+
+ From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+ subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+ render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
+ exceptions.
+
+ A :class:`MultiDict` can be constructed from an iterable of
+ ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
+ onwards some keyword parameters.
+
+ :param mapping: the initial value for the :class:`MultiDict`. Either a
+ regular dict, an iterable of ``(key, value)`` tuples
+ or `None`.
+ """
+
+ def __init__(self, mapping=None):
+ if isinstance(mapping, MultiDict):
+ dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
+ elif isinstance(mapping, dict):
+ tmp = {}
+ for key, value in mapping.iteritems():
+ if isinstance(value, (tuple, list)):
+ value = list(value)
+ else:
+ value = [value]
+ tmp[key] = value
+ dict.__init__(self, tmp)
+ else:
+ tmp = {}
+ for key, value in mapping or ():
+ tmp.setdefault(key, []).append(value)
+ dict.__init__(self, tmp)
+
+ def __getstate__(self):
+ return dict(self.lists())
+
+ def __setstate__(self, value):
+ dict.clear(self)
+ dict.update(self, value)
+
+ def __iter__(self):
+ return self.iterkeys()
+
+ def __getitem__(self, key):
+ """Return the first data value for this key;
+ raises KeyError if not found.
+
+ :param key: The key to be looked up.
+ :raise KeyError: if the key does not exist.
+ """
+ if key in self:
+ return dict.__getitem__(self, key)[0]
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ """Like :meth:`add` but removes an existing key first.
+
+ :param key: the key for the value.
+ :param value: the value to set.
+ """
+ dict.__setitem__(self, key, [value])
+
+ def add(self, key, value):
+ """Adds a new value for the key.
+
+ .. versionadded:: 0.6
+
+ :param key: the key for the value.
+ :param value: the value to add.
+ """
+ dict.setdefault(self, key, []).append(value)
+
+ def getlist(self, key, type=None):
+ """Return the list of items for a given key. If that key is not in the
+ `MultiDict`, the return value will be an empty list. Just as `get`
+ `getlist` accepts a `type` parameter. All items will be converted
+ with the callable defined there.
+
+ :param key: The key to be looked up.
+ :param type: A callable that is used to cast the value in the
+ :class:`MultiDict`. If a :exc:`ValueError` is raised
+ by this callable the value will be removed from the list.
+ :return: a :class:`list` of all the values for the key.
+ """
+ try:
+ rv = dict.__getitem__(self, key)
+ except KeyError:
+ return []
+ if type is None:
+ return list(rv)
+ result = []
+ for item in rv:
+ try:
+ result.append(type(item))
+ except ValueError:
+ pass
+ return result
+
+ def setlist(self, key, new_list):
+ """Remove the old values for a key and add new ones. Note that the list
+ you pass the values in will be shallow-copied before it is inserted in
+ the dictionary.
+
+ >>> d = MultiDict()
+ >>> d.setlist('foo', ['1', '2'])
+ >>> d['foo']
+ '1'
+ >>> d.getlist('foo')
+ ['1', '2']
+
+ :param key: The key for which the values are set.
+ :param new_list: An iterable with the new values for the key. Old values
+ are removed first.
+ """
+ dict.__setitem__(self, key, list(new_list))
+
+ def setdefault(self, key, default=None):
+ """Returns the value for the key if it is in the dict, otherwise it
+ returns `default` and sets that value for `key`.
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key is not
+ in the dict. If not further specified it's `None`.
+ """
+ if key not in self:
+ self[key] = default
+ else:
+ default = self[key]
+ return default
+
+ def setlistdefault(self, key, default_list=None):
+ """Like `setdefault` but sets multiple values. The list returned
+ is not a copy, but the list that is actually used internally. This
+ means that you can put new values into the dict by appending items
+ to the list:
+
+ >>> d = MultiDict({"foo": 1})
+ >>> d.setlistdefault("foo").extend([2, 3])
+ >>> d.getlist("foo")
+ [1, 2, 3]
+
+ :param key: The key to be looked up.
+ :param default: An iterable of default values. It is either copied
+ (in case it was a list) or converted into a list
+ before returned.
+ :return: a :class:`list`
+ """
+ if key not in self:
+ default_list = list(default_list or ())
+ dict.__setitem__(self, key, default_list)
+ else:
+ default_list = dict.__getitem__(self, key)
+ return default_list
+
+ def items(self, multi=False):
+ """Return a list of ``(key, value)`` pairs.
+
+ :param multi: If set to `True` the list returned will have a
+ pair for each value of each key. Otherwise it
+ will only contain pairs for the first value of
+ each key.
+
+ :return: a :class:`list`
+ """
+ return list(self.iteritems(multi))
+
+ def lists(self):
+ """Return a list of ``(key, values)`` pairs, where values is the list of
+ all values associated with the key.
+
+ :return: a :class:`list`
+ """
+ return list(self.iterlists())
+
+ def values(self):
+ """Returns a list of the first value on every key's value list.
+
+ :return: a :class:`list`.
+ """
+ return [self[key] for key in self.iterkeys()]
+
+ def listvalues(self):
+ """Return a list of all values associated with a key. Zipping
+ :meth:`keys` and this is the same as calling :meth:`lists`:
+
+ >>> d = MultiDict({"foo": [1, 2, 3]})
+ >>> zip(d.keys(), d.listvalues()) == d.lists()
+ True
+
+ :return: a :class:`list`
+ """
+ return list(self.iterlistvalues())
+
+ def iteritems(self, multi=False):
+ """Like :meth:`items` but returns an iterator."""
+ for key, values in dict.iteritems(self):
+ if multi:
+ for value in values:
+ yield key, value
+ else:
+ yield key, values[0]
+
+ def iterlists(self):
+ """Like :meth:`items` but returns an iterator."""
+ for key, values in dict.iteritems(self):
+ yield key, list(values)
+
+ def itervalues(self):
+ """Like :meth:`values` but returns an iterator."""
+ for values in dict.itervalues(self):
+ yield values[0]
+
+ def iterlistvalues(self):
+ """Like :meth:`listvalues` but returns an iterator."""
+ return dict.itervalues(self)
+
+ def copy(self):
+ """Return a shallow copy of this object."""
+ return self.__class__(self)
+
+ def to_dict(self, flat=True):
+ """Return the contents as regular dict. If `flat` is `True` the
+ returned dict will only have the first item present, if `flat` is
+ `False` all values will be returned as lists.
+
+ :param flat: If set to `False` the dict returned will have lists
+ with all the values in it. Otherwise it will only
+ contain the first value for each key.
+ :return: a :class:`dict`
+ """
+ if flat:
+ return dict(self.iteritems())
+ return dict(self.lists())
+
+ def update(self, other_dict):
+ """update() extends rather than replaces existing key lists."""
+ for key, value in iter_multi_items(other_dict):
+ MultiDict.add(self, key, value)
+
+ def pop(self, key, default=_missing):
+ """Pop the first item for a list on the dict. Afterwards the
+ key is removed from the dict, so additional values are discarded:
+
+ >>> d = MultiDict({"foo": [1, 2, 3]})
+ >>> d.pop("foo")
+ 1
+ >>> "foo" in d
+ False
+
+ :param key: the key to pop.
+ :param default: if provided the value to return if the key was
+ not in the dictionary.
+ """
+ try:
+ return dict.pop(self, key)[0]
+ except KeyError as e:
+ if default is not _missing:
+ return default
+ raise KeyError(str(e))
+
+ def popitem(self):
+ """Pop an item from the dict."""
+ try:
+ item = dict.popitem(self)
+ return (item[0], item[1][0])
+ except KeyError as e:
+ raise KeyError(str(e))
+
+ def poplist(self, key):
+ """Pop the list for a key from the dict. If the key is not in the dict
+ an empty list is returned.
+
+ .. versionchanged:: 0.5
+ If the key does no longer exist a list is returned instead of
+ raising an error.
+ """
+ return dict.pop(self, key, [])
+
+ def popitemlist(self):
+ """Pop a ``(key, list)`` tuple from the dict."""
+ try:
+ return dict.popitem(self)
+ except KeyError as e:
+ raise KeyError(str(e))
+
+ def __copy__(self):
+ return self.copy()
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
diff --git a/requests/packages/urllib3/__init__.py b/requests/packages/urllib3/__init__.py
new file mode 100644
index 0000000..81e76f5
--- /dev/null
+++ b/requests/packages/urllib3/__init__.py
@@ -0,0 +1,42 @@
+# urllib3/__init__.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""
+urllib3 - Thread-safe connection pooling and re-using.
+"""
+
+__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
+__license__ = 'MIT'
+__version__ = 'dev'
+
+
+from .connectionpool import (
+ HTTPConnectionPool,
+ HTTPSConnectionPool,
+ connection_from_url
+)
+
+from . import exceptions
+from .filepost import encode_multipart_formdata
+from .poolmanager import PoolManager, ProxyManager, proxy_from_url
+from .response import HTTPResponse
+from .util import make_headers, get_host
+
+
+# Set default logging handler to avoid "No handler found" warnings.
+import logging
+try:
+ from logging import NullHandler
+except ImportError:
+ class NullHandler(logging.Handler):
+ def emit(self, record):
+ pass
+
+logging.getLogger(__name__).addHandler(NullHandler())
+
+# ... Clean up.
+del logging
+del NullHandler
diff --git a/requests/packages/urllib3/_collections.py b/requests/packages/urllib3/_collections.py
new file mode 100644
index 0000000..3cef081
--- /dev/null
+++ b/requests/packages/urllib3/_collections.py
@@ -0,0 +1,131 @@
+# urllib3/_collections.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from collections import deque
+
+from threading import RLock
+
+__all__ = ['RecentlyUsedContainer']
+
+
+class AccessEntry(object):
+ __slots__ = ('key', 'is_valid')
+
+ def __init__(self, key, is_valid=True):
+ self.key = key
+ self.is_valid = is_valid
+
+
+class RecentlyUsedContainer(dict):
+ """
+ Provides a dict-like that maintains up to ``maxsize`` keys while throwing
+ away the least-recently-used keys beyond ``maxsize``.
+ """
+
+ # If len(self.access_log) exceeds self._maxsize * CLEANUP_FACTOR, then we
+ # will attempt to cleanup the invalidated entries in the access_log
+ # datastructure during the next 'get' operation.
+ CLEANUP_FACTOR = 10
+
+ def __init__(self, maxsize=10):
+ self._maxsize = maxsize
+
+ self._container = {}
+
+ # We use a deque to to store our keys ordered by the last access.
+ self.access_log = deque()
+ self.access_log_lock = RLock()
+
+ # We look up the access log entry by the key to invalidate it so we can
+ # insert a new authorative entry at the head without having to dig and
+ # find the old entry for removal immediately.
+ self.access_lookup = {}
+
+ # Trigger a heap cleanup when we get past this size
+ self.access_log_limit = maxsize * self.CLEANUP_FACTOR
+
+ def _invalidate_entry(self, key):
+ "If exists: Invalidate old entry and return it."
+ old_entry = self.access_lookup.get(key)
+ if old_entry:
+ old_entry.is_valid = False
+
+ return old_entry
+
+ def _push_entry(self, key):
+ "Push entry onto our access log, invalidate the old entry if exists."
+ self._invalidate_entry(key)
+
+ new_entry = AccessEntry(key)
+ self.access_lookup[key] = new_entry
+
+ self.access_log_lock.acquire()
+ self.access_log.appendleft(new_entry)
+ self.access_log_lock.release()
+
+ def _prune_entries(self, num):
+ "Pop entries from our access log until we popped ``num`` valid ones."
+ while num > 0:
+ self.access_log_lock.acquire()
+ p = self.access_log.pop()
+ self.access_log_lock.release()
+
+ if not p.is_valid:
+ continue # Invalidated entry, skip
+
+ dict.pop(self, p.key, None)
+ self.access_lookup.pop(p.key, None)
+ num -= 1
+
+ def _prune_invalidated_entries(self):
+ "Rebuild our access_log without the invalidated entries."
+ self.access_log_lock.acquire()
+ self.access_log = deque(e for e in self.access_log if e.is_valid)
+ self.access_log_lock.release()
+
+ def _get_ordered_access_keys(self):
+ "Return ordered access keys for inspection. Used for testing."
+ self.access_log_lock.acquire()
+ r = [e.key for e in self.access_log if e.is_valid]
+ self.access_log_lock.release()
+
+ return r
+
+ def __getitem__(self, key):
+ item = dict.get(self, key)
+
+ if not item:
+ raise KeyError(key)
+
+ # Insert new entry with new high priority, also implicitly invalidates
+ # the old entry.
+ self._push_entry(key)
+
+ if len(self.access_log) > self.access_log_limit:
+ # Heap is getting too big, try to clean up any tailing invalidated
+ # entries.
+ self._prune_invalidated_entries()
+
+ return item
+
+ def __setitem__(self, key, item):
+ # Add item to our container and access log
+ dict.__setitem__(self, key, item)
+ self._push_entry(key)
+
+ # Discard invalid and excess entries
+ self._prune_entries(len(self) - self._maxsize)
+
+ def __delitem__(self, key):
+ self._invalidate_entry(key)
+ self.access_lookup.pop(key, None)
+ dict.__delitem__(self, key)
+
+ def get(self, key, default=None):
+ try:
+ return self[key]
+ except KeyError:
+ return default
diff --git a/requests/packages/urllib3/connectionpool.py b/requests/packages/urllib3/connectionpool.py
new file mode 100644
index 0000000..336aa77
--- /dev/null
+++ b/requests/packages/urllib3/connectionpool.py
@@ -0,0 +1,522 @@
+# urllib3/connectionpool.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import logging
+import socket
+
+from socket import error as SocketError, timeout as SocketTimeout
+
+try: # Python 3
+ from http.client import HTTPConnection, HTTPException
+ from http.client import HTTP_PORT, HTTPS_PORT
+except ImportError:
+ from httplib import HTTPConnection, HTTPException
+ from httplib import HTTP_PORT, HTTPS_PORT
+
+try: # Python 3
+ from queue import LifoQueue, Empty, Full
+except ImportError:
+ from Queue import LifoQueue, Empty, Full
+
+
+try: # Compiled with SSL?
+ HTTPSConnection = object
+ BaseSSLError = None
+ ssl = None
+
+ try: # Python 3
+ from http.client import HTTPSConnection
+ except ImportError:
+ from httplib import HTTPSConnection
+
+ import ssl
+ BaseSSLError = ssl.SSLError
+
+except (ImportError, AttributeError):
+ pass
+
+
+from .request import RequestMethods
+from .response import HTTPResponse
+from .util import get_host, is_connection_dropped
+from .exceptions import (
+ EmptyPoolError,
+ HostChangedError,
+ MaxRetryError,
+ SSLError,
+ TimeoutError,
+)
+
+from .packages.ssl_match_hostname import match_hostname, CertificateError
+from .packages import six
+
+
+xrange = six.moves.xrange
+
+log = logging.getLogger(__name__)
+
+_Default = object()
+
+port_by_scheme = {
+ 'http': HTTP_PORT,
+ 'https': HTTPS_PORT,
+}
+
+
+## Connection objects (extension of httplib)
+
+class VerifiedHTTPSConnection(HTTPSConnection):
+ """
+ Based on httplib.HTTPSConnection but wraps the socket with
+ SSL certification.
+ """
+ cert_reqs = None
+ ca_certs = None
+
+ def set_cert(self, key_file=None, cert_file=None,
+ cert_reqs='CERT_NONE', ca_certs=None):
+ ssl_req_scheme = {
+ 'CERT_NONE': ssl.CERT_NONE,
+ 'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
+ 'CERT_REQUIRED': ssl.CERT_REQUIRED
+ }
+
+ self.key_file = key_file
+ self.cert_file = cert_file
+ self.cert_reqs = ssl_req_scheme.get(cert_reqs) or ssl.CERT_NONE
+ self.ca_certs = ca_certs
+
+ def connect(self):
+ # Add certificate verification
+ sock = socket.create_connection((self.host, self.port), self.timeout)
+
+ # Wrap socket using verification with the root certs in
+ # trusted_root_certs
+ self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
+ cert_reqs=self.cert_reqs,
+ ca_certs=self.ca_certs)
+ if self.ca_certs:
+ match_hostname(self.sock.getpeercert(), self.host)
+
+
+## Pool objects
+
+class ConnectionPool(object):
+ """
+ Base class for all connection pools, such as
+ :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`.
+ """
+
+ scheme = None
+ QueueCls = LifoQueue
+
+ def __init__(self, host, port=None):
+ self.host = host
+ self.port = port
+
+ def __str__(self):
+ return '%s(host=%r, port=%r)' % (type(self).__name__,
+ self.host, self.port)
+
+
+class HTTPConnectionPool(ConnectionPool, RequestMethods):
+ """
+ Thread-safe connection pool for one host.
+
+ :param host:
+ Host used for this HTTP Connection (e.g. "localhost"), passed into
+ :class:`httplib.HTTPConnection`.
+
+ :param port:
+ Port used for this HTTP Connection (None is equivalent to 80), passed
+ into :class:`httplib.HTTPConnection`.
+
+ :param strict:
+ Causes BadStatusLine to be raised if the status line can't be parsed
+ as a valid HTTP/1.0 or 1.1 status line, passed into
+ :class:`httplib.HTTPConnection`.
+
+ :param timeout:
+ Socket timeout for each individual connection, can be a float. None
+ disables timeout.
+
+ :param maxsize:
+ Number of connections to save that can be reused. More than 1 is useful
+ in multithreaded situations. If ``block`` is set to false, more
+ connections will be created but they will not be saved once they've
+ been used.
+
+ :param block:
+ If set to True, no more than ``maxsize`` connections will be used at
+ a time. When no free connections are available, the call will block
+ until a connection has been released. This is a useful side effect for
+ particular multithreaded situations where one does not want to use more
+ than maxsize connections per host to prevent flooding.
+
+ :param headers:
+ Headers to include with all requests, unless other headers are given
+ explicitly.
+ """
+
+ scheme = 'http'
+
+ def __init__(self, host, port=None, strict=False, timeout=None, maxsize=1,
+ block=False, headers=None):
+ super(HTTPConnectionPool, self).__init__(host, port)
+
+ self.strict = strict
+ self.timeout = timeout
+ self.pool = self.QueueCls(maxsize)
+ self.block = block
+ self.headers = headers or {}
+
+ # Fill the queue up so that doing get() on it will block properly
+ for _ in xrange(maxsize):
+ self.pool.put(None)
+
+ # These are mostly for testing and debugging purposes.
+ self.num_connections = 0
+ self.num_requests = 0
+
+ def _new_conn(self):
+ """
+ Return a fresh :class:`httplib.HTTPConnection`.
+ """
+ self.num_connections += 1
+ log.info("Starting new HTTP connection (%d): %s" %
+ (self.num_connections, self.host))
+ return HTTPConnection(host=self.host, port=self.port)
+
+ def _get_conn(self, timeout=None):
+ """
+ Get a connection. Will return a pooled connection if one is available.
+
+ If no connections are available and :prop:`.block` is ``False``, then a
+ fresh connection is returned.
+
+ :param timeout:
+ Seconds to wait before giving up and raising
+ :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and
+ :prop:`.block` is ``True``.
+ """
+ conn = None
+ try:
+ conn = self.pool.get(block=self.block, timeout=timeout)
+
+ # If this is a persistent connection, check if it got disconnected
+ if conn and is_connection_dropped(conn):
+ log.info("Resetting dropped connection: %s" % self.host)
+ conn.close()
+
+ except Empty:
+ if self.block:
+ raise EmptyPoolError(self,
+ "Pool reached maximum size and no more "
+ "connections are allowed.")
+ pass # Oh well, we'll create a new connection then
+
+ return conn or self._new_conn()
+
+ def _put_conn(self, conn):
+ """
+ Put a connection back into the pool.
+
+ :param conn:
+ Connection object for the current host and port as returned by
+ :meth:`._new_conn` or :meth:`._get_conn`.
+
+ If the pool is already full, the connection is discarded because we
+ exceeded maxsize. If connections are discarded frequently, then maxsize
+ should be increased.
+ """
+ try:
+ self.pool.put(conn, block=False)
+ except Full:
+ # This should never happen if self.block == True
+ log.warning("HttpConnectionPool is full, discarding connection: %s"
+ % self.host)
+
+ def _make_request(self, conn, method, url, timeout=_Default,
+ **httplib_request_kw):
+ """
+ Perform a request on a given httplib connection object taken from our
+ pool.
+ """
+ self.num_requests += 1
+
+ if timeout is _Default:
+ timeout = self.timeout
+
+ conn.timeout = timeout # This only does anything in Py26+
+ conn.request(method, url, **httplib_request_kw)
+
+ # Set timeout
+ sock = getattr(conn, 'sock', False) # AppEngine doesn't have sock attr.
+ if sock:
+ sock.settimeout(timeout)
+
+ httplib_response = conn.getresponse()
+
+ # AppEngine doesn't have a version attr.
+ http_version = getattr(conn, '_http_vsn_str', 'HTTP/?'),
+ log.debug("\"%s %s %s\" %s %s" % (method, url, http_version,
+ httplib_response.status,
+ httplib_response.length))
+
+ return httplib_response
+
+
+ def is_same_host(self, url):
+ """
+ Check if the given ``url`` is a member of the same host as this
+ connection pool.
+ """
+ # TODO: Add optional support for socket.gethostbyname checking.
+ scheme, host, port = get_host(url)
+
+ if self.port and not port:
+ # Use explicit default port for comparison when none is given.
+ port = port_by_scheme.get(scheme)
+
+ return (url.startswith('/') or
+ (scheme, host, port) == (self.scheme, self.host, self.port))
+
+ def urlopen(self, method, url, body=None, headers=None, retries=3,
+ redirect=True, assert_same_host=True, timeout=_Default,
+ pool_timeout=None, release_conn=None, **response_kw):
+ """
+ Get a connection from the pool and perform an HTTP request. This is the
+ lowest level call for making a request, so you'll need to specify all
+ the raw details.
+
+ .. note::
+
+ More commonly, it's appropriate to use a convenience method provided
+ by :class:`.RequestMethods`, such as :meth:`request`.
+
+ .. note::
+
+ `release_conn` will only behave as expected if
+ `preload_content=False` because we want to make
+ `preload_content=False` the default behaviour someday soon without
+ breaking backwards compatibility.
+
+ :param method:
+ HTTP request method (such as GET, POST, PUT, etc.)
+
+ :param body:
+ Data to send in the request body (useful for creating
+ POST requests, see HTTPConnectionPool.post_url for
+ more convenience).
+
+ :param headers:
+ Dictionary of custom headers to send, such as User-Agent,
+ If-None-Match, etc. If None, pool headers are used. If provided,
+ these headers completely replace any pool-specific headers.
+
+ :param retries:
+ Number of retries to allow before raising a MaxRetryError exception.
+
+ :param redirect:
+ Automatically handle redirects (status codes 301, 302, 303, 307),
+ each redirect counts as a retry.
+
+ :param assert_same_host:
+ If ``True``, will make sure that the host of the pool requests is
+ consistent else will raise HostChangedError. When False, you can
+ use the pool on an HTTP proxy and request foreign hosts.
+
+ :param timeout:
+ If specified, overrides the default timeout for this one request.
+
+ :param pool_timeout:
+ If set and the pool is set to block=True, then this method will
+ block for ``pool_timeout`` seconds and raise EmptyPoolError if no
+ connection is available within the time period.
+
+ :param release_conn:
+ If False, then the urlopen call will not release the connection
+ back into the pool once a response is received (but will release if
+ you read the entire contents of the response such as when
+ `preload_content=True`). This is useful if you're not preloading
+ the response's content immediately. You will need to call
+ ``r.release_conn()`` on the response ``r`` to return the connection
+ back into the pool. If None, it takes the value of
+ ``response_kw.get('preload_content', True)``.
+
+ :param \**response_kw:
+ Additional parameters are passed to
+ :meth:`urllib3.response.HTTPResponse.from_httplib`
+ """
+ if headers is None:
+ headers = self.headers
+
+ if retries < 0:
+ raise MaxRetryError(self, url)
+
+ if timeout is _Default:
+ timeout = self.timeout
+
+ if release_conn is None:
+ release_conn = response_kw.get('preload_content', True)
+
+ # Check host
+ if assert_same_host and not self.is_same_host(url):
+ host = "%s://%s" % (self.scheme, self.host)
+ if self.port:
+ host = "%s:%d" % (host, self.port)
+
+ raise HostChangedError(self, url, retries - 1)
+
+ conn = None
+
+ try:
+ # Request a connection from the queue
+ # (Could raise SocketError: Bad file descriptor)
+ conn = self._get_conn(timeout=pool_timeout)
+
+ # Make the request on the httplib connection object
+ httplib_response = self._make_request(conn, method, url,
+ timeout=timeout,
+ body=body, headers=headers)
+
+ # If we're going to release the connection in ``finally:``, then
+ # the request doesn't need to know about the connection. Otherwise
+ # it will also try to release it and we'll have a double-release
+ # mess.
+ response_conn = not release_conn and conn
+
+ # Import httplib's response into our own wrapper object
+ response = HTTPResponse.from_httplib(httplib_response,
+ pool=self,
+ connection=response_conn,
+ **response_kw)
+
+ # else:
+ # The connection will be put back into the pool when
+ # ``response.release_conn()`` is called (implicitly by
+ # ``response.read()``)
+
+ except Empty as e:
+ # Timed out by queue
+ raise TimeoutError(self, "Request timed out. (pool_timeout=%s)" %
+ pool_timeout)
+
+ except SocketTimeout as e:
+ # Timed out by socket
+ raise TimeoutError(self, "Request timed out. (timeout=%s)" %
+ timeout)
+
+ except BaseSSLError as e:
+ # SSL certificate error
+ raise SSLError(e)
+
+ except CertificateError as e:
+ # Name mismatch
+ raise SSLError(e)
+
+ except (HTTPException, SocketError) as e:
+ # Connection broken, discard. It will be replaced next _get_conn().
+ conn = None
+ # This is necessary so we can access e below
+ err = e
+
+ finally:
+ if conn and release_conn:
+ # Put the connection back to be reused
+ self._put_conn(conn)
+
+ if not conn:
+ log.warn("Retrying (%d attempts remain) after connection "
+ "broken by '%r': %s" % (retries, err, url))
+ return self.urlopen(method, url, body, headers, retries - 1,
+ redirect, assert_same_host) # Try again
+
+ # Handle redirect?
+ redirect_location = redirect and response.get_redirect_location()
+ if redirect_location:
+ log.info("Redirecting %s -> %s" % (url, redirect_location))
+ return self.urlopen(method, redirect_location, body, headers,
+ retries - 1, redirect, assert_same_host)
+
+ return response
+
+
+class HTTPSConnectionPool(HTTPConnectionPool):
+ """
+ Same as :class:`.HTTPConnectionPool`, but HTTPS.
+
+ When Python is compiled with the :mod:`ssl` module, then
+ :class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates,
+ instead of :class:httplib.HTTPSConnection`.
+
+ The ``key_file``, ``cert_file``, ``cert_reqs``, and ``ca_certs`` parameters
+ are only used if :mod:`ssl` is available and are fed into
+ :meth:`ssl.wrap_socket` to upgrade the connection socket into an SSL socket.
+ """
+
+ scheme = 'https'
+
+ def __init__(self, host, port=None,
+ strict=False, timeout=None, maxsize=1,
+ block=False, headers=None,
+ key_file=None, cert_file=None,
+ cert_reqs='CERT_NONE', ca_certs=None):
+
+ super(HTTPSConnectionPool, self).__init__(host, port,
+ strict, timeout, maxsize,
+ block, headers)
+ self.key_file = key_file
+ self.cert_file = cert_file
+ self.cert_reqs = cert_reqs
+ self.ca_certs = ca_certs
+
+ def _new_conn(self):
+ """
+ Return a fresh :class:`httplib.HTTPSConnection`.
+ """
+ self.num_connections += 1
+ log.info("Starting new HTTPS connection (%d): %s"
+ % (self.num_connections, self.host))
+
+ if not ssl: # Platform-specific: Python compiled without +ssl
+ if not HTTPSConnection or HTTPSConnection is object:
+ raise SSLError("Can't connect to HTTPS URL because the SSL "
+ "module is not available.")
+
+ return HTTPSConnection(host=self.host, port=self.port)
+
+ connection = VerifiedHTTPSConnection(host=self.host, port=self.port)
+ connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
+ cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)
+ return connection
+
+
+def connection_from_url(url, **kw):
+ """
+ Given a url, return an :class:`.ConnectionPool` instance of its host.
+
+ This is a shortcut for not having to parse out the scheme, host, and port
+ of the url before creating an :class:`.ConnectionPool` instance.
+
+ :param url:
+ Absolute URL string that must include the scheme. Port is optional.
+
+ :param \**kw:
+ Passes additional parameters to the constructor of the appropriate
+ :class:`.ConnectionPool`. Useful for specifying things like
+ timeout, maxsize, headers, etc.
+
+ Example: ::
+
+ >>> conn = connection_from_url('http://google.com/')
+ >>> r = conn.request('GET', '/')
+ """
+ scheme, host, port = get_host(url)
+ if scheme == 'https':
+ return HTTPSConnectionPool(host, port=port, **kw)
+ else:
+ return HTTPConnectionPool(host, port=port, **kw)
diff --git a/requests/packages/urllib3/exceptions.py b/requests/packages/urllib3/exceptions.py
new file mode 100644
index 0000000..15c9699
--- /dev/null
+++ b/requests/packages/urllib3/exceptions.py
@@ -0,0 +1,67 @@
+# urllib3/exceptions.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+## Base Exceptions
+
+class HTTPError(Exception):
+ "Base exception used by this module."
+ pass
+
+
+class PoolError(HTTPError):
+ "Base exception for errors caused within a pool."
+ def __init__(self, pool, message):
+ self.pool = pool
+ HTTPError.__init__(self, "%s: %s" % (pool, message))
+
+
+class SSLError(HTTPError):
+ "Raised when SSL certificate fails in an HTTPS connection."
+ pass
+
+
+## Leaf Exceptions
+
+class MaxRetryError(PoolError):
+ "Raised when the maximum number of retries is exceeded."
+
+ def __init__(self, pool, url):
+ message = "Max retries exceeded with url: %s" % url
+ PoolError.__init__(self, pool, message)
+
+ self.url = url
+
+
+class HostChangedError(PoolError):
+ "Raised when an existing pool gets a request for a foreign host."
+
+ def __init__(self, pool, url, retries=3):
+ message = "Tried to open a foreign host with url: %s" % url
+ PoolError.__init__(self, pool, message)
+
+ self.url = url
+ self.retries = retries
+
+
+class TimeoutError(PoolError):
+ "Raised when a socket timeout occurs."
+ pass
+
+
+class EmptyPoolError(PoolError):
+ "Raised when a pool runs out of connections and no more are allowed."
+ pass
+
+
+class LocationParseError(ValueError, HTTPError):
+ "Raised when get_host or similar fails to parse the URL input."
+
+ def __init__(self, location):
+ message = "Failed to parse: %s" % location
+ super(LocationParseError, self).__init__(self, message)
+
+ self.location = location
diff --git a/requests/packages/urllib3/filepost.py b/requests/packages/urllib3/filepost.py
new file mode 100644
index 0000000..344a103
--- /dev/null
+++ b/requests/packages/urllib3/filepost.py
@@ -0,0 +1,88 @@
+# urllib3/filepost.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import codecs
+import mimetypes
+
+try:
+ from mimetools import choose_boundary
+except ImportError:
+ from .packages.mimetools_choose_boundary import choose_boundary
+
+from io import BytesIO
+
+from .packages import six
+from .packages.six import b
+
+writer = codecs.lookup('utf-8')[3]
+
+
+def get_content_type(filename):
+ return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+
+
+def iter_fields(fields):
+ """
+ Iterate over fields.
+
+ Supports list of (k, v) tuples and dicts.
+ """
+ if isinstance(fields, dict):
+ return ((k, v) for k, v in six.iteritems(fields))
+
+ return ((k, v) for k, v in fields)
+
+
+def encode_multipart_formdata(fields, boundary=None):
+ """
+ Encode a dictionary of ``fields`` using the multipart/form-data mime format.
+
+ :param fields:
+ Dictionary of fields or list of (key, value) field tuples. The key is
+ treated as the field name, and the value as the body of the form-data
+ bytes. If the value is a tuple of two elements, then the first element
+ is treated as the filename of the form-data section.
+
+ Field names and filenames must be unicode.
+
+ :param boundary:
+ If not specified, then a random boundary will be generated using
+ :func:`mimetools.choose_boundary`.
+ """
+ body = BytesIO()
+ if boundary is None:
+ boundary = choose_boundary()
+
+ for fieldname, value in iter_fields(fields):
+ body.write(b('--%s\r\n' % (boundary)))
+
+ if isinstance(value, tuple):
+ filename, data = value
+ writer(body).write('Content-Disposition: form-data; name="%s"; '
+ 'filename="%s"\r\n' % (fieldname, filename))
+ body.write(b('Content-Type: %s\r\n\r\n' %
+ (get_content_type(filename))))
+ else:
+ data = value
+ writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
+ % (fieldname))
+ body.write(b'Content-Type: text/plain\r\n\r\n')
+
+ if isinstance(data, int):
+ data = str(data) # Backwards compatibility
+
+ if isinstance(data, six.text_type):
+ writer(body).write(data)
+ else:
+ body.write(data)
+
+ body.write(b'\r\n')
+
+ body.write(b('--%s--\r\n' % (boundary)))
+
+ content_type = b('multipart/form-data; boundary=%s' % boundary)
+
+ return body.getvalue(), content_type
diff --git a/requests/packages/urllib3/packages/__init__.py b/requests/packages/urllib3/packages/__init__.py
new file mode 100644
index 0000000..37e8351
--- /dev/null
+++ b/requests/packages/urllib3/packages/__init__.py
@@ -0,0 +1,4 @@
+from __future__ import absolute_import
+
+from . import ssl_match_hostname
+
diff --git a/requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py b/requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py
new file mode 100644
index 0000000..a0109ab
--- /dev/null
+++ b/requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py
@@ -0,0 +1,47 @@
+"""The function mimetools.choose_boundary() from Python 2.7, which seems to
+have disappeared in Python 3 (although email.generator._make_boundary() might
+work as a replacement?).
+
+Tweaked to use lock from threading rather than thread.
+"""
+import os
+from threading import Lock
+_counter_lock = Lock()
+
+_counter = 0
+def _get_next_counter():
+ global _counter
+ with _counter_lock:
+ _counter += 1
+ return _counter
+
+_prefix = None
+
+def choose_boundary():
+ """Return a string usable as a multipart boundary.
+
+ The string chosen is unique within a single program run, and
+ incorporates the user id (if available), process id (if available),
+ and current time. So it's very unlikely the returned string appears
+ in message text, but there's no guarantee.
+
+ The boundary contains dots so you have to quote it in the header."""
+
+ global _prefix
+ import time
+ if _prefix is None:
+ import socket
+ try:
+ hostid = socket.gethostbyname(socket.gethostname())
+ except socket.gaierror:
+ hostid = '127.0.0.1'
+ try:
+ uid = repr(os.getuid())
+ except AttributeError:
+ uid = '1'
+ try:
+ pid = repr(os.getpid())
+ except AttributeError:
+ pid = '1'
+ _prefix = hostid + '.' + uid + '.' + pid
+ return "%s.%.3f.%d" % (_prefix, time.time(), _get_next_counter())
diff --git a/requests/packages/urllib3/packages/six.py b/requests/packages/urllib3/packages/six.py
new file mode 100644
index 0000000..a64f6fb
--- /dev/null
+++ b/requests/packages/urllib3/packages/six.py
@@ -0,0 +1,372 @@
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+#Copyright (c) 2010-2011 Benjamin Peterson
+
+#Permission is hereby granted, free of charge, to any person obtaining a copy of
+#this software and associated documentation files (the "Software"), to deal in
+#the Software without restriction, including without limitation the rights to
+#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+#the Software, and to permit persons to whom the Software is furnished to do so,
+#subject to the following conditions:
+
+#The above copyright notice and this permission notice shall be included in all
+#copies or substantial portions of the Software.
+
+#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.1.0"
+
+
+# True if we are running on Python 3.
+PY3 = sys.version_info[0] == 3
+
+if PY3:
+ string_types = str,
+ integer_types = int,
+ class_types = type,
+ text_type = str
+ binary_type = bytes
+
+ MAXSIZE = sys.maxsize
+else:
+ string_types = basestring,
+ integer_types = (int, long)
+ class_types = (type, types.ClassType)
+ text_type = unicode
+ binary_type = str
+
+ # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+ class X(object):
+ def __len__(self):
+ return 1 << 31
+ try:
+ len(X())
+ except OverflowError:
+ # 32-bit
+ MAXSIZE = int((1 << 31) - 1)
+ else:
+ # 64-bit
+ MAXSIZE = int((1 << 63) - 1)
+ del X
+
+
+def _add_doc(func, doc):
+ """Add documentation to a function."""
+ func.__doc__ = doc
+
+
+def _import_module(name):
+ """Import module, returning the module after the last dot."""
+ __import__(name)
+ return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+ def __init__(self, name):
+ self.name = name
+
+ def __get__(self, obj, tp):
+ result = self._resolve()
+ setattr(obj, self.name, result)
+ # This is a bit ugly, but it avoids running this again.
+ delattr(tp, self.name)
+ return result
+
+
+class MovedModule(_LazyDescr):
+
+ def __init__(self, name, old, new=None):
+ super(MovedModule, self).__init__(name)
+ if PY3:
+ if new is None:
+ new = name
+ self.mod = new
+ else:
+ self.mod = old
+
+ def _resolve(self):
+ return _import_module(self.mod)
+
+
+class MovedAttribute(_LazyDescr):
+
+ def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+ super(MovedAttribute, self).__init__(name)
+ if PY3:
+ if new_mod is None:
+ new_mod = name
+ self.mod = new_mod
+ if new_attr is None:
+ if old_attr is None:
+ new_attr = name
+ else:
+ new_attr = old_attr
+ self.attr = new_attr
+ else:
+ self.mod = old_mod
+ if old_attr is None:
+ old_attr = name
+ self.attr = old_attr
+
+ def _resolve(self):
+ module = _import_module(self.mod)
+ return getattr(module, self.attr)
+
+
+
+class _MovedItems(types.ModuleType):
+ """Lazy loading of moved objects"""
+
+
+_moved_attributes = [
+ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+ MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+ MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+ MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
+ MovedAttribute("reduce", "__builtin__", "functools"),
+ MovedAttribute("StringIO", "StringIO", "io"),
+ MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+ MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+
+ MovedModule("builtins", "__builtin__"),
+ MovedModule("configparser", "ConfigParser"),
+ MovedModule("copyreg", "copy_reg"),
+ MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+ MovedModule("http_cookies", "Cookie", "http.cookies"),
+ MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+ MovedModule("html_parser", "HTMLParser", "html.parser"),
+ MovedModule("http_client", "httplib", "http.client"),
+ MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+ MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+ MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+ MovedModule("cPickle", "cPickle", "pickle"),
+ MovedModule("queue", "Queue"),
+ MovedModule("reprlib", "repr"),
+ MovedModule("socketserver", "SocketServer"),
+ MovedModule("tkinter", "Tkinter"),
+ MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+ MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+ MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+ MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+ MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+ MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+ MovedModule("tkinter_colorchooser", "tkColorChooser",
+ "tkinter.colorchooser"),
+ MovedModule("tkinter_commondialog", "tkCommonDialog",
+ "tkinter.commondialog"),
+ MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+ MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+ MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+ MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+ "tkinter.simpledialog"),
+ MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+ MovedModule("winreg", "_winreg"),
+]
+for attr in _moved_attributes:
+ setattr(_MovedItems, attr.name, attr)
+del attr
+
+moves = sys.modules["six.moves"] = _MovedItems("moves")
+
+
+def add_move(move):
+ """Add an item to six.moves."""
+ setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+ """Remove item from six.moves."""
+ try:
+ delattr(_MovedItems, name)
+ except AttributeError:
+ try:
+ del moves.__dict__[name]
+ except KeyError:
+ raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+ _meth_func = "__func__"
+ _meth_self = "__self__"
+
+ _func_code = "__code__"
+ _func_defaults = "__defaults__"
+
+ _iterkeys = "keys"
+ _itervalues = "values"
+ _iteritems = "items"
+else:
+ _meth_func = "im_func"
+ _meth_self = "im_self"
+
+ _func_code = "func_code"
+ _func_defaults = "func_defaults"
+
+ _iterkeys = "iterkeys"
+ _itervalues = "itervalues"
+ _iteritems = "iteritems"
+
+
+if PY3:
+ def get_unbound_function(unbound):
+ return unbound
+
+
+ advance_iterator = next
+
+ def callable(obj):
+ return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+else:
+ def get_unbound_function(unbound):
+ return unbound.im_func
+
+
+ def advance_iterator(it):
+ return it.next()
+
+ callable = callable
+_add_doc(get_unbound_function,
+ """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+
+
+def iterkeys(d):
+ """Return an iterator over the keys of a dictionary."""
+ return getattr(d, _iterkeys)()
+
+def itervalues(d):
+ """Return an iterator over the values of a dictionary."""
+ return getattr(d, _itervalues)()
+
+def iteritems(d):
+ """Return an iterator over the (key, value) pairs of a dictionary."""
+ return getattr(d, _iteritems)()
+
+
+if PY3:
+ def b(s):
+ return s.encode("latin-1")
+ def u(s):
+ return s
+ if sys.version_info[1] <= 1:
+ def int2byte(i):
+ return bytes((i,))
+ else:
+ # This is about 2x faster than the implementation above on 3.2+
+ int2byte = operator.methodcaller("to_bytes", 1, "big")
+ import io
+ StringIO = io.StringIO
+ BytesIO = io.BytesIO
+else:
+ def b(s):
+ return s
+ def u(s):
+ return unicode(s, "unicode_escape")
+ int2byte = chr
+ import StringIO
+ StringIO = BytesIO = StringIO.StringIO
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+if PY3:
+ import builtins
+ exec_ = getattr(builtins, "exec")
+
+
+ def reraise(tp, value, tb=None):
+ if value.__traceback__ is not tb:
+ raise value.with_traceback(tb)
+ raise value
+
+
+ print_ = getattr(builtins, "print")
+ del builtins
+
+else:
+ def exec_(code, globs=None, locs=None):
+ """Execute code in a namespace."""
+ if globs is None:
+ frame = sys._getframe(1)
+ globs = frame.f_globals
+ if locs is None:
+ locs = frame.f_locals
+ del frame
+ elif locs is None:
+ locs = globs
+ exec("""exec code in globs, locs""")
+
+
+ exec_("""def reraise(tp, value, tb=None):
+ raise tp, value, tb
+""")
+
+
+ def print_(*args, **kwargs):
+ """The new-style print function."""
+ fp = kwargs.pop("file", sys.stdout)
+ if fp is None:
+ return
+ def write(data):
+ if not isinstance(data, basestring):
+ data = str(data)
+ fp.write(data)
+ want_unicode = False
+ sep = kwargs.pop("sep", None)
+ if sep is not None:
+ if isinstance(sep, unicode):
+ want_unicode = True
+ elif not isinstance(sep, str):
+ raise TypeError("sep must be None or a string")
+ end = kwargs.pop("end", None)
+ if end is not None:
+ if isinstance(end, unicode):
+ want_unicode = True
+ elif not isinstance(end, str):
+ raise TypeError("end must be None or a string")
+ if kwargs:
+ raise TypeError("invalid keyword arguments to print()")
+ if not want_unicode:
+ for arg in args:
+ if isinstance(arg, unicode):
+ want_unicode = True
+ break
+ if want_unicode:
+ newline = unicode("\n")
+ space = unicode(" ")
+ else:
+ newline = "\n"
+ space = " "
+ if sep is None:
+ sep = space
+ if end is None:
+ end = newline
+ for i, arg in enumerate(args):
+ if i:
+ write(sep)
+ write(arg)
+ write(end)
+
+_add_doc(reraise, """Reraise an exception.""")
+
+
+def with_metaclass(meta, base=object):
+ """Create a base class with a metaclass."""
+ return meta("NewBase", (base,), {})
diff --git a/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py b/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
new file mode 100644
index 0000000..9560b04
--- /dev/null
+++ b/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
@@ -0,0 +1,61 @@
+"""The match_hostname() function from Python 3.2, essential when using SSL."""
+
+import re
+
+__version__ = '3.2.2'
+
+class CertificateError(ValueError):
+ pass
+
+def _dnsname_to_pat(dn):
+ pats = []
+ for frag in dn.split(r'.'):
+ if frag == '*':
+ # When '*' is a fragment by itself, it matches a non-empty dotless
+ # fragment.
+ pats.append('[^.]+')
+ else:
+ # Otherwise, '*' matches any dotless fragment.
+ frag = re.escape(frag)
+ pats.append(frag.replace(r'\*', '[^.]*'))
+ return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
+
+def match_hostname(cert, hostname):
+ """Verify that *cert* (in decoded format as returned by
+ SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules
+ are mostly followed, but IP addresses are not accepted for *hostname*.
+
+ CertificateError is raised on failure. On success, the function
+ returns nothing.
+ """
+ if not cert:
+ raise ValueError("empty or no certificate")
+ dnsnames = []
+ san = cert.get('subjectAltName', ())
+ for key, value in san:
+ if key == 'DNS':
+ if _dnsname_to_pat(value).match(hostname):
+ return
+ dnsnames.append(value)
+ if not dnsnames:
+ # The subject is only checked when there is no dNSName entry
+ # in subjectAltName
+ for sub in cert.get('subject', ()):
+ for key, value in sub:
+ # XXX according to RFC 2818, the most specific Common Name
+ # must be used.
+ if key == 'commonName':
+ if _dnsname_to_pat(value).match(hostname):
+ return
+ dnsnames.append(value)
+ if len(dnsnames) > 1:
+ raise CertificateError("hostname %r "
+ "doesn't match either of %s"
+ % (hostname, ', '.join(map(repr, dnsnames))))
+ elif len(dnsnames) == 1:
+ raise CertificateError("hostname %r "
+ "doesn't match %r"
+ % (hostname, dnsnames[0]))
+ else:
+ raise CertificateError("no appropriate commonName or "
+ "subjectAltName fields were found")
diff --git a/requests/packages/urllib3/poolmanager.py b/requests/packages/urllib3/poolmanager.py
new file mode 100644
index 0000000..310ea21
--- /dev/null
+++ b/requests/packages/urllib3/poolmanager.py
@@ -0,0 +1,138 @@
+# urllib3/poolmanager.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import logging
+
+from ._collections import RecentlyUsedContainer
+from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
+from .connectionpool import get_host, connection_from_url, port_by_scheme
+from .exceptions import HostChangedError
+from .request import RequestMethods
+
+
+__all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url']
+
+
+pool_classes_by_scheme = {
+ 'http': HTTPConnectionPool,
+ 'https': HTTPSConnectionPool,
+}
+
+log = logging.getLogger(__name__)
+
+
+class PoolManager(RequestMethods):
+ """
+ Allows for arbitrary requests while transparently keeping track of
+ necessary connection pools for you.
+
+ :param num_pools:
+ Number of connection pools to cache before discarding the least recently
+ used pool.
+
+ :param \**connection_pool_kw:
+ Additional parameters are used to create fresh
+ :class:`urllib3.connectionpool.ConnectionPool` instances.
+
+ Example: ::
+
+ >>> manager = PoolManager(num_pools=2)
+ >>> r = manager.urlopen("http://google.com/")
+ >>> r = manager.urlopen("http://google.com/mail")
+ >>> r = manager.urlopen("http://yahoo.com/")
+ >>> len(manager.pools)
+ 2
+
+ """
+
+ # TODO: Make sure there are no memory leaks here.
+
+ def __init__(self, num_pools=10, **connection_pool_kw):
+ self.connection_pool_kw = connection_pool_kw
+ self.pools = RecentlyUsedContainer(num_pools)
+
+ def connection_from_host(self, host, port=80, scheme='http'):
+ """
+ Get a :class:`ConnectionPool` based on the host, port, and scheme.
+
+ Note that an appropriate ``port`` value is required here to normalize
+ connection pools in our container most effectively.
+ """
+ pool_key = (scheme, host, port)
+
+ # If the scheme, host, or port doesn't match existing open connections,
+ # open a new ConnectionPool.
+ pool = self.pools.get(pool_key)
+ if pool:
+ return pool
+
+ # Make a fresh ConnectionPool of the desired type
+ pool_cls = pool_classes_by_scheme[scheme]
+ pool = pool_cls(host, port, **self.connection_pool_kw)
+
+ self.pools[pool_key] = pool
+
+ return pool
+
+ def connection_from_url(self, url):
+ """
+ Similar to :func:`urllib3.connectionpool.connection_from_url` but
+ doesn't pass any additional parameters to the
+ :class:`urllib3.connectionpool.ConnectionPool` constructor.
+
+ Additional parameters are taken from the :class:`.PoolManager`
+ constructor.
+ """
+ scheme, host, port = get_host(url)
+
+ port = port or port_by_scheme.get(scheme, 80)
+
+ return self.connection_from_host(host, port=port, scheme=scheme)
+
+ def urlopen(self, method, url, **kw):
+ """
+ Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen`.
+
+ ``url`` must be absolute, such that an appropriate
+ :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it.
+ """
+ conn = self.connection_from_url(url)
+ try:
+ return conn.urlopen(method, url, **kw)
+
+ except HostChangedError as e:
+ kw['retries'] = e.retries # Persist retries countdown
+ return self.urlopen(method, e.url, **kw)
+
+
+class ProxyManager(RequestMethods):
+ """
+ Given a ConnectionPool to a proxy, the ProxyManager's ``urlopen`` method
+ will make requests to any url through the defined proxy.
+ """
+
+ def __init__(self, proxy_pool):
+ self.proxy_pool = proxy_pool
+
+ def _set_proxy_headers(self, headers=None):
+ headers = headers or {}
+
+ # Same headers are curl passes for --proxy1.0
+ headers['Accept'] = '*/*'
+ headers['Proxy-Connection'] = 'Keep-Alive'
+
+ return headers
+
+ def urlopen(self, method, url, **kw):
+ "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute."
+ kw['assert_same_host'] = False
+ kw['headers'] = self._set_proxy_headers(kw.get('headers'))
+ return self.proxy_pool.urlopen(method, url, **kw)
+
+
+def proxy_from_url(url, **pool_kw):
+ proxy_pool = connection_from_url(url, **pool_kw)
+ return ProxyManager(proxy_pool)
diff --git a/requests/packages/urllib3/request.py b/requests/packages/urllib3/request.py
new file mode 100644
index 0000000..569ac96
--- /dev/null
+++ b/requests/packages/urllib3/request.py
@@ -0,0 +1,128 @@
+# urllib3/request.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+try:
+ from urllib.parse import urlencode
+except ImportError:
+ from urllib import urlencode
+
+from .filepost import encode_multipart_formdata
+
+
+__all__ = ['RequestMethods']
+
+
+class RequestMethods(object):
+ """
+ Convenience mixin for classes who implement a :meth:`urlopen` method, such
+ as :class:`~urllib3.connectionpool.HTTPConnectionPool` and
+ :class:`~urllib3.poolmanager.PoolManager`.
+
+ Provides behavior for making common types of HTTP request methods and
+ decides which type of request field encoding to use.
+
+ Specifically,
+
+ :meth:`.request_encode_url` is for sending requests whose fields are encoded
+ in the URL (such as GET, HEAD, DELETE).
+
+ :meth:`.request_encode_body` is for sending requests whose fields are
+ encoded in the *body* of the request using multipart or www-orm-urlencoded
+ (such as for POST, PUT, PATCH).
+
+ :meth:`.request` is for making any kind of request, it will look up the
+ appropriate encoding format and use one of the above two methods to make
+ the request.
+ """
+
+ _encode_url_methods = set(['DELETE', 'GET', 'HEAD', 'OPTIONS'])
+
+ _encode_body_methods = set(['PATCH', 'POST', 'PUT', 'TRACE'])
+
+ def urlopen(self, method, url, body=None, headers=None,
+ encode_multipart=True, multipart_boundary=None,
+ **kw): # Abstract
+ raise NotImplemented("Classes extending RequestMethods must implement "
+ "their own ``urlopen`` method.")
+
+ def request(self, method, url, fields=None, headers=None, **urlopen_kw):
+ """
+ Make a request using :meth:`urlopen` with the appropriate encoding of
+ ``fields`` based on the ``method`` used.
+
+ This is a convenience method that requires the least amount of manual
+ effort. It can be used in most situations, while still having the option
+ to drop down to more specific methods when necessary, such as
+ :meth:`request_encode_url`, :meth:`request_encode_body`,
+ or even the lowest level :meth:`urlopen`.
+ """
+ method = method.upper()
+
+ if method in self._encode_url_methods:
+ return self.request_encode_url(method, url, fields=fields,
+ headers=headers,
+ **urlopen_kw)
+ else:
+ return self.request_encode_body(method, url, fields=fields,
+ headers=headers,
+ **urlopen_kw)
+
+ def request_encode_url(self, method, url, fields=None, **urlopen_kw):
+ """
+ Make a request using :meth:`urlopen` with the ``fields`` encoded in
+ the url. This is useful for request methods like GET, HEAD, DELETE, etc.
+ """
+ if fields:
+ url += '?' + urlencode(fields)
+ return self.urlopen(method, url, **urlopen_kw)
+
+ def request_encode_body(self, method, url, fields=None, headers=None,
+ encode_multipart=True, multipart_boundary=None,
+ **urlopen_kw):
+ """
+ Make a request using :meth:`urlopen` with the ``fields`` encoded in
+ the body. This is useful for request methods like POST, PUT, PATCH, etc.
+
+ When ``encode_multipart=True`` (default), then
+ :meth:`urllib3.filepost.encode_multipart_formdata` is used to encode the
+ payload with the appropriate content type. Otherwise
+ :meth:`urllib.urlencode` is used with the
+ 'application/x-www-form-urlencoded' content type.
+
+ Multipart encoding must be used when posting files, and it's reasonably
+ safe to use it in other times too. However, it may break request signing,
+ such as with OAuth.
+
+ Supports an optional ``fields`` parameter of key/value strings AND
+ key/filetuple. A filetuple is a (filename, data) tuple. For example: ::
+
+ fields = {
+ 'foo': 'bar',
+ 'fakefile': ('foofile.txt', 'contents of foofile'),
+ 'realfile': ('barfile.txt', open('realfile').read()),
+ 'nonamefile': ('contents of nonamefile field'),
+ }
+
+ When uploading a file, providing a filename (the first parameter of the
+ tuple) is optional but recommended to best mimick behavior of browsers.
+
+ Note that if ``headers`` are supplied, the 'Content-Type' header will be
+ overwritten because it depends on the dynamic random boundary string
+ which is used to compose the body of the request. The random boundary
+ string can be explicitly set with the ``multipart_boundary`` parameter.
+ """
+ if encode_multipart:
+ body, content_type = encode_multipart_formdata(fields or {},
+ boundary=multipart_boundary)
+ else:
+ body, content_type = (urlencode(fields or {}),
+ 'application/x-www-form-urlencoded')
+
+ headers = headers or {}
+ headers.update({'Content-Type': content_type})
+
+ return self.urlopen(method, url, body=body, headers=headers,
+ **urlopen_kw)
diff --git a/requests/packages/urllib3/response.py b/requests/packages/urllib3/response.py
new file mode 100644
index 0000000..5fab824
--- /dev/null
+++ b/requests/packages/urllib3/response.py
@@ -0,0 +1,202 @@
+# urllib3/response.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+import gzip
+import logging
+import zlib
+
+from io import BytesIO
+
+from .exceptions import HTTPError
+from .packages.six import string_types as basestring
+
+
+log = logging.getLogger(__name__)
+
+
+def decode_gzip(data):
+ gzipper = gzip.GzipFile(fileobj=BytesIO(data))
+ return gzipper.read()
+
+
+def decode_deflate(data):
+ try:
+ return zlib.decompress(data)
+ except zlib.error:
+ return zlib.decompress(data, -zlib.MAX_WBITS)
+
+
+class HTTPResponse(object):
+ """
+ HTTP Response container.
+
+ Backwards-compatible to httplib's HTTPResponse but the response ``body`` is
+ loaded and decoded on-demand when the ``data`` property is accessed.
+
+ Extra parameters for behaviour not present in httplib.HTTPResponse:
+
+ :param preload_content:
+ If True, the response's body will be preloaded during construction.
+
+ :param decode_content:
+ If True, attempts to decode specific content-encoding's based on headers
+ (like 'gzip' and 'deflate') will be skipped and raw data will be used
+ instead.
+
+ :param original_response:
+ When this HTTPResponse wrapper is generated from an httplib.HTTPResponse
+ object, it's convenient to include the original for debug purposes. It's
+ otherwise unused.
+ """
+
+ CONTENT_DECODERS = {
+ 'gzip': decode_gzip,
+ 'deflate': decode_deflate,
+ }
+
+ def __init__(self, body='', headers=None, status=0, version=0, reason=None,
+ strict=0, preload_content=True, decode_content=True,
+ original_response=None, pool=None, connection=None):
+ self.headers = headers or {}
+ self.status = status
+ self.version = version
+ self.reason = reason
+ self.strict = strict
+
+ self._decode_content = decode_content
+ self._body = body if body and isinstance(body, basestring) else None
+ self._fp = None
+ self._original_response = original_response
+
+ self._pool = pool
+ self._connection = connection
+
+ if hasattr(body, 'read'):
+ self._fp = body
+
+ if preload_content and not self._body:
+ self._body = self.read(decode_content=decode_content)
+
+ def get_redirect_location(self):
+ """
+ Should we redirect and where to?
+
+ :returns: Truthy redirect location string if we got a redirect status
+ code and valid location. ``None`` if redirect status and no
+ location. ``False`` if not a redirect status code.
+ """
+ if self.status in [301, 302, 303, 307]:
+ return self.headers.get('location')
+
+ return False
+
+ def release_conn(self):
+ if not self._pool or not self._connection:
+ return
+
+ self._pool._put_conn(self._connection)
+ self._connection = None
+
+ @property
+ def data(self):
+ # For backwords-compat with earlier urllib3 0.4 and earlier.
+ if self._body:
+ return self._body
+
+ if self._fp:
+ return self.read(cache_content=True)
+
+ def read(self, amt=None, decode_content=None, cache_content=False):
+ """
+ Similar to :meth:`httplib.HTTPResponse.read`, but with two additional
+ parameters: ``decode_content`` and ``cache_content``.
+
+ :param amt:
+ How much of the content to read. If specified, decoding and caching
+ is skipped because we can't decode partial content nor does it make
+ sense to cache partial content as the full response.
+
+ :param decode_content:
+ If True, will attempt to decode the body based on the
+ 'content-encoding' header. (Overridden if ``amt`` is set.)
+
+ :param cache_content:
+ If True, will save the returned data such that the same result is
+ returned despite of the state of the underlying file object. This
+ is useful if you want the ``.data`` property to continue working
+ after having ``.read()`` the file object. (Overridden if ``amt`` is
+ set.)
+ """
+ content_encoding = self.headers.get('content-encoding')
+ decoder = self.CONTENT_DECODERS.get(content_encoding)
+ if decode_content is None:
+ decode_content = self._decode_content
+
+ if self._fp is None:
+ return
+
+ try:
+ if amt is None:
+ # cStringIO doesn't like amt=None
+ data = self._fp.read()
+ else:
+ return self._fp.read(amt)
+
+ try:
+ if decode_content and decoder:
+ data = decoder(data)
+ except IOError:
+ raise HTTPError("Received response with content-encoding: %s, but "
+ "failed to decode it." % content_encoding)
+
+ if cache_content:
+ self._body = data
+
+ return data
+
+ finally:
+ if self._original_response and self._original_response.isclosed():
+ self.release_conn()
+
+ @classmethod
+ def from_httplib(ResponseCls, r, **response_kw):
+ """
+ Given an :class:`httplib.HTTPResponse` instance ``r``, return a
+ corresponding :class:`urllib3.response.HTTPResponse` object.
+
+ Remaining parameters are passed to the HTTPResponse constructor, along
+ with ``original_response=r``.
+ """
+
+ # Normalize headers between different versions of Python
+ headers = {}
+ for k, v in r.getheaders():
+ # Python 3: Header keys are returned capitalised
+ k = k.lower()
+
+ has_value = headers.get(k)
+ if has_value: # Python 3: Repeating header keys are unmerged.
+ v = ', '.join([has_value, v])
+
+ headers[k] = v
+
+ # HTTPResponse objects in Python 3 don't have a .strict attribute
+ strict = getattr(r, 'strict', 0)
+ return ResponseCls(body=r,
+ headers=headers,
+ status=r.status,
+ version=r.version,
+ reason=r.reason,
+ strict=strict,
+ original_response=r,
+ **response_kw)
+
+ # Backwards-compatibility methods for httplib.HTTPResponse
+ def getheaders(self):
+ return self.headers
+
+ def getheader(self, name, default=None):
+ return self.headers.get(name, default)
diff --git a/requests/packages/urllib3/util.py b/requests/packages/urllib3/util.py
new file mode 100644
index 0000000..2684a2f
--- /dev/null
+++ b/requests/packages/urllib3/util.py
@@ -0,0 +1,136 @@
+# urllib3/util.py
+# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
+#
+# This module is part of urllib3 and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+from base64 import b64encode
+
+try:
+ from select import poll, POLLIN
+except ImportError: # `poll` doesn't exist on OSX and other platforms
+ poll = False
+ try:
+ from select import select
+ except ImportError: # `select` doesn't exist on AppEngine.
+ select = False
+
+from .packages import six
+from .exceptions import LocationParseError
+
+
+def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
+ basic_auth=None):
+ """
+ Shortcuts for generating request headers.
+
+ :param keep_alive:
+ If ``True``, adds 'connection: keep-alive' header.
+
+ :param accept_encoding:
+ Can be a boolean, list, or string.
+ ``True`` translates to 'gzip,deflate'.
+ List will get joined by comma.
+ String will be used as provided.
+
+ :param user_agent:
+ String representing the user-agent you want, such as
+ "python-urllib3/0.6"
+
+ :param basic_auth:
+ Colon-separated username:password string for 'authorization: basic ...'
+ auth header.
+
+ Example: ::
+
+ >>> make_headers(keep_alive=True, user_agent="Batman/1.0")
+ {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'}
+ >>> make_headers(accept_encoding=True)
+ {'accept-encoding': 'gzip,deflate'}
+ """
+ headers = {}
+ if accept_encoding:
+ if isinstance(accept_encoding, str):
+ pass
+ elif isinstance(accept_encoding, list):
+ accept_encoding = ','.join(accept_encoding)
+ else:
+ accept_encoding = 'gzip,deflate'
+ headers['accept-encoding'] = accept_encoding
+
+ if user_agent:
+ headers['user-agent'] = user_agent
+
+ if keep_alive:
+ headers['connection'] = 'keep-alive'
+
+ if basic_auth:
+ headers['authorization'] = 'Basic ' + \
+ b64encode(six.b(basic_auth)).decode('utf-8')
+
+ return headers
+
+
+def get_host(url):
+ """
+ Given a url, return its scheme, host and port (None if it's not there).
+
+ For example: ::
+
+ >>> get_host('http://google.com/mail/')
+ ('http', 'google.com', None)
+ >>> get_host('google.com:80')
+ ('http', 'google.com', 80)
+ """
+
+ # This code is actually similar to urlparse.urlsplit, but much
+ # simplified for our needs.
+ port = None
+ scheme = 'http'
+
+ if '://' in url:
+ scheme, url = url.split('://', 1)
+ if '/' in url:
+ url, _path = url.split('/', 1)
+ if '@' in url:
+ _auth, url = url.split('@', 1)
+ if ':' in url:
+ url, port = url.split(':', 1)
+
+ if not port.isdigit():
+ raise LocationParseError("Failed to parse: %s" % url)
+
+ port = int(port)
+
+ return scheme, url, port
+
+
+
+def is_connection_dropped(conn):
+ """
+ Returns True if the connection is dropped and should be closed.
+
+ :param conn:
+ ``HTTPConnection`` object.
+
+ Note: For platforms like AppEngine, this will always return ``False`` to
+ let the platform handle connection recycling transparently for us.
+ """
+ sock = getattr(conn, 'sock', False)
+ if not sock: #Platform-specific: AppEngine
+ return False
+
+ if not poll: # Platform-specific
+ if not select: #Platform-specific: AppEngine
+ return False
+
+ return select([sock], [], [], 0.0)[0]
+
+ # This version is better on platforms that support it.
+ p = poll()
+ p.register(sock, POLLIN)
+ for (fno, ev) in p.poll(0.0):
+ if fno == sock.fileno():
+ # Either data is buffered (bad), or the connection is dropped.
+ return True
diff --git a/requests/sessions.py b/requests/sessions.py
new file mode 100644
index 0000000..0e43030
--- /dev/null
+++ b/requests/sessions.py
@@ -0,0 +1,306 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.session
+~~~~~~~~~~~~~~~~
+
+This module provides a Session object to manage and persist settings across
+requests (cookies, auth, proxies).
+
+"""
+
+from .defaults import defaults
+from .models import Request
+from .hooks import dispatch_hook
+from .utils import header_expand
+from .packages.urllib3.poolmanager import PoolManager
+
+
+def merge_kwargs(local_kwarg, default_kwarg):
+ """Merges kwarg dictionaries.
+
+ If a local key in the dictionary is set to None, it will be removed.
+ """
+
+ if default_kwarg is None:
+ return local_kwarg
+
+ if isinstance(local_kwarg, str):
+ return local_kwarg
+
+ if local_kwarg is None:
+ return default_kwarg
+
+ # Bypass if not a dictionary (e.g. timeout)
+ if not hasattr(default_kwarg, 'items'):
+ return local_kwarg
+
+ # Update new values.
+ kwargs = default_kwarg.copy()
+ kwargs.update(local_kwarg)
+
+ # Remove keys that are set to None.
+ for (k, v) in list(local_kwarg.items()):
+ if v is None:
+ del kwargs[k]
+
+ return kwargs
+
+
+class Session(object):
+ """A Requests session."""
+
+ __attrs__ = [
+ 'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks',
+ 'params', 'config', 'verify', 'cert', 'prefetch']
+
+
+ def __init__(self,
+ headers=None,
+ cookies=None,
+ auth=None,
+ timeout=None,
+ proxies=None,
+ hooks=None,
+ params=None,
+ config=None,
+ prefetch=False,
+ verify=True,
+ cert=None):
+
+ self.headers = headers or {}
+ self.cookies = cookies or {}
+ self.auth = auth
+ self.timeout = timeout
+ self.proxies = proxies or {}
+ self.hooks = hooks or {}
+ self.params = params or {}
+ self.config = config or {}
+ self.prefetch = prefetch
+ self.verify = verify
+ self.cert = cert
+
+ for (k, v) in list(defaults.items()):
+ self.config.setdefault(k, v)
+
+ self.init_poolmanager()
+
+ # Set up a CookieJar to be used by default
+ self.cookies = {}
+
+ # Add passed cookies in.
+ if cookies is not None:
+ self.cookies.update(cookies)
+
+ def init_poolmanager(self):
+ self.poolmanager = PoolManager(
+ num_pools=self.config.get('pool_connections'),
+ maxsize=self.config.get('pool_maxsize')
+ )
+
+ def __repr__(self):
+ return '<requests-client at 0x%x>' % (id(self))
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ pass
+
+ def request(self, method, url,
+ params=None,
+ data=None,
+ headers=None,
+ cookies=None,
+ files=None,
+ auth=None,
+ timeout=None,
+ allow_redirects=True,
+ proxies=None,
+ hooks=None,
+ return_response=True,
+ config=None,
+ prefetch=False,
+ verify=None,
+ cert=None):
+
+ """Constructs and sends a :class:`Request <Request>`.
+ Returns :class:`Response <Response>` object.
+
+ :param method: method for the new :class:`Request` object.
+ :param url: URL for the new :class:`Request` object.
+ :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
+ :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
+ :param files: (optional) Dictionary of 'filename': file-like-objects for multipart encoding upload.
+ :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
+ :param timeout: (optional) Float describing the timeout of the request.
+ :param allow_redirects: (optional) Boolean. Set to True by default.
+ :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
+ :param return_response: (optional) If False, an un-sent Request object will returned.
+ :param config: (optional) A configuration dictionary.
+ :param prefetch: (optional) if ``True``, the response content will be immediately downloaded.
+ :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
+ :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
+ """
+
+ method = str(method).upper()
+
+ # Default empty dicts for dict params.
+ cookies = {} if cookies is None else cookies
+ 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
+ hooks = {} if hooks is None else hooks
+ prefetch = self.prefetch or prefetch
+
+ # use session's hooks as defaults
+ for key, cb in list(self.hooks.items()):
+ hooks.setdefault(key, cb)
+
+ # Expand header values.
+ if headers:
+ 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,
+ cookies=cookies,
+ files=files,
+ auth=auth,
+ hooks=hooks,
+ timeout=timeout,
+ allow_redirects=allow_redirects,
+ proxies=proxies,
+ config=config,
+ prefetch=prefetch,
+ verify=verify,
+ cert=cert,
+ _poolmanager=self.poolmanager
+ )
+
+ # Merge local kwargs with session kwargs.
+ for attr in self.__attrs__:
+ session_val = getattr(self, attr, None)
+ local_val = args.get(attr)
+
+ args[attr] = merge_kwargs(local_val, session_val)
+
+ # Arguments manipulation hook.
+ args = dispatch_hook('args', args['hooks'], args)
+
+ # Create the (empty) response.
+ r = Request(**args)
+
+ # Give the response some context.
+ r.session = self
+
+ # Don't send if asked nicely.
+ if not return_response:
+ return r
+
+ # Send the HTTP Request.
+ r.send(prefetch=prefetch)
+
+ # Send any cookies back up the to the session.
+ self.cookies.update(r.response.cookies)
+
+ # Return the response.
+ return r.response
+
+
+ def get(self, url, **kwargs):
+ """Sends a GET request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return self.request('get', url, **kwargs)
+
+
+ def options(self, url, **kwargs):
+ """Sends a OPTIONS request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return self.request('options', url, **kwargs)
+
+
+ def head(self, url, **kwargs):
+ """Sends a HEAD request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', False)
+ return self.request('head', url, **kwargs)
+
+
+ def post(self, url, data=None, **kwargs):
+ """Sends a POST request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return self.request('post', url, data=data, **kwargs)
+
+
+ def put(self, url, data=None, **kwargs):
+ """Sends a PUT request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return self.request('put', url, data=data, **kwargs)
+
+
+ def patch(self, url, data=None, **kwargs):
+ """Sends a PATCH request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return self.request('patch', url, data=data, **kwargs)
+
+
+ def delete(self, url, **kwargs):
+ """Sends a DELETE request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param \*\*kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return self.request('delete', url, **kwargs)
+
+ def __getstate__(self):
+ return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__)
+
+ def __setstate__(self, state):
+ for attr, value in state.items():
+ setattr(self, attr, value)
+
+ self.init_poolmanager()
+
+
+def session(**kwargs):
+ """Returns a :class:`Session` for context-management."""
+
+ return Session(**kwargs)
diff --git a/requests/status_codes.py b/requests/status_codes.py
new file mode 100644
index 0000000..da74286
--- /dev/null
+++ b/requests/status_codes.py
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+
+from .structures import LookupDict
+
+_codes = {
+
+ # Informational.
+ 100: ('continue',),
+ 101: ('switching_protocols',),
+ 102: ('processing',),
+ 103: ('checkpoint',),
+ 122: ('uri_too_long', 'request_uri_too_long'),
+ 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/'),
+ 201: ('created',),
+ 202: ('accepted',),
+ 203: ('non_authoritative_info', 'non_authoritative_information'),
+ 204: ('no_content',),
+ 205: ('reset_content', 'reset'),
+ 206: ('partial_content', 'partial'),
+ 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'),
+ 208: ('im_used',),
+
+ # Redirection.
+ 300: ('multiple_choices',),
+ 301: ('moved_permanently', 'moved', '\\o-'),
+ 302: ('found',),
+ 303: ('see_other', 'other'),
+ 304: ('not_modified',),
+ 305: ('use_proxy',),
+ 306: ('switch_proxy',),
+ 307: ('temporary_redirect', 'temporary_moved', 'temporary'),
+ 308: ('resume_incomplete', 'resume'),
+
+ # Client Error.
+ 400: ('bad_request', 'bad'),
+ 401: ('unauthorized',),
+ 402: ('payment_required', 'payment'),
+ 403: ('forbidden',),
+ 404: ('not_found', '-o-'),
+ 405: ('method_not_allowed', 'not_allowed'),
+ 406: ('not_acceptable',),
+ 407: ('proxy_authentication_required', 'proxy_auth', 'proxy_authentication'),
+ 408: ('request_timeout', 'timeout'),
+ 409: ('conflict',),
+ 410: ('gone',),
+ 411: ('length_required',),
+ 412: ('precondition_failed', 'precondition'),
+ 413: ('request_entity_too_large',),
+ 414: ('request_uri_too_large',),
+ 415: ('unsupported_media_type', 'unsupported_media', 'media_type'),
+ 416: ('requested_range_not_satisfiable', 'requested_range', 'range_not_satisfiable'),
+ 417: ('expectation_failed',),
+ 418: ('im_a_teapot', 'teapot', 'i_am_a_teapot'),
+ 422: ('unprocessable_entity', 'unprocessable'),
+ 423: ('locked',),
+ 424: ('failed_dependency', 'dependency'),
+ 425: ('unordered_collection', 'unordered'),
+ 426: ('upgrade_required', 'upgrade'),
+ 428: ('precondition_required', 'precondition'),
+ 429: ('too_many_requests', 'too_many'),
+ 431: ('header_fields_too_large', 'fields_too_large'),
+ 444: ('no_response', 'none'),
+ 449: ('retry_with', 'retry'),
+ 450: ('blocked_by_windows_parental_controls', 'parental_controls'),
+ 499: ('client_closed_request',),
+
+ # Server Error.
+ 500: ('internal_server_error', 'server_error', '/o\\'),
+ 501: ('not_implemented',),
+ 502: ('bad_gateway',),
+ 503: ('service_unavailable', 'unavailable'),
+ 504: ('gateway_timeout',),
+ 505: ('http_version_not_supported', 'http_version'),
+ 506: ('variant_also_negotiates',),
+ 507: ('insufficient_storage',),
+ 509: ('bandwidth_limit_exceeded', 'bandwidth'),
+ 510: ('not_extended',),
+}
+
+codes = LookupDict(name='status_codes')
+
+for (code, titles) in list(_codes.items()):
+ for title in titles:
+ setattr(codes, title, code)
+ if not title.startswith('\\'):
+ setattr(codes, title.upper(), code) \ No newline at end of file
diff --git a/requests/structures.py b/requests/structures.py
new file mode 100644
index 0000000..3746754
--- /dev/null
+++ b/requests/structures.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.structures
+~~~~~~~~~~~~~~~~~~~
+
+Data structures that power Requests.
+
+"""
+
+
+class CaseInsensitiveDict(dict):
+ """Case-insensitive Dictionary
+
+ For example, ``headers['content-encoding']`` will return the
+ value of a ``'Content-Encoding'`` response header."""
+
+ @property
+ def lower_keys(self):
+ if not hasattr(self, '_lower_keys') or not self._lower_keys:
+ self._lower_keys = dict((k.lower(), k) for k in list(self.keys()))
+ return self._lower_keys
+
+ def _clear_lower_keys(self):
+ if hasattr(self, '_lower_keys'):
+ self._lower_keys.clear()
+
+ def __setitem__(self, key, value):
+ dict.__setitem__(self, key, value)
+ self._clear_lower_keys()
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self._lower_keys.clear()
+
+ def __contains__(self, key):
+ return key.lower() in self.lower_keys
+
+ def __getitem__(self, key):
+ # We allow fall-through here, so values default to None
+ if key in self:
+ return dict.__getitem__(self, self.lower_keys[key.lower()])
+
+ def get(self, key, default=None):
+ if key in self:
+ return self[key]
+ else:
+ return default
+
+class LookupDict(dict):
+ """Dictionary lookup object."""
+
+ def __init__(self, name=None):
+ self.name = name
+ super(LookupDict, self).__init__()
+
+ def __repr__(self):
+ return '<lookup \'%s\'>' % (self.name)
+
+ def __getitem__(self, key):
+ # We allow fall-through here, so values default to None
+
+ return self.__dict__.get(key, None)
+
+ def get(self, key, default=None):
+ return self.__dict__.get(key, default)
diff --git a/requests/utils.py b/requests/utils.py
new file mode 100644
index 0000000..925547a
--- /dev/null
+++ b/requests/utils.py
@@ -0,0 +1,504 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.utils
+~~~~~~~~~~~~~~
+
+This module provides utility functions that are used within Requests
+that are also useful for external consumption.
+
+"""
+
+import cgi
+import codecs
+import os
+import random
+import re
+import zlib
+from netrc import netrc, NetrcParseError
+
+from .compat import parse_http_list as _parse_list_header
+from .compat import quote, cookielib, SimpleCookie, is_py2, urlparse
+from .compat import basestring, bytes, str
+
+CERTIFI_BUNDLE_PATH = None
+try:
+ # see if requests's own CA certificate bundle is installed
+ import certifi
+ CERTIFI_BUNDLE_PATH = certifi.where()
+except ImportError:
+ pass
+
+NETRC_FILES = ('.netrc', '_netrc')
+
+# common paths for the OS's CA certificate bundle
+POSSIBLE_CA_BUNDLE_PATHS = [
+ # Red Hat, CentOS, Fedora and friends (provided by the ca-certificates package):
+ '/etc/pki/tls/certs/ca-bundle.crt',
+ # Ubuntu, Debian, and friends (provided by the ca-certificates package):
+ '/etc/ssl/certs/ca-certificates.crt',
+ # FreeBSD (provided by the ca_root_nss package):
+ '/usr/local/share/certs/ca-root-nss.crt',
+]
+
+def get_os_ca_bundle_path():
+ """Try to pick an available CA certificate bundle provided by the OS."""
+ for path in POSSIBLE_CA_BUNDLE_PATHS:
+ if os.path.exists(path):
+ return path
+ return None
+
+# if certifi is installed, use its CA bundle;
+# otherwise, try and use the OS bundle
+DEFAULT_CA_BUNDLE_PATH = CERTIFI_BUNDLE_PATH or get_os_ca_bundle_path()
+
+def dict_to_sequence(d):
+ """Returns an internal sequence dictionary update."""
+
+ if hasattr(d, 'items'):
+ d = d.items()
+
+ return d
+
+
+def get_netrc_auth(url):
+ """Returns the Requests tuple auth for a given url from netrc."""
+
+ try:
+ locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES)
+ netrc_path = None
+
+ for loc in locations:
+ if os.path.exists(loc) and not netrc_path:
+ netrc_path = loc
+
+ # Abort early if there isn't one.
+ if netrc_path is None:
+ return netrc_path
+
+ ri = urlparse(url)
+
+ # Strip port numbers from netloc
+ host = ri.netloc.split(':')[0]
+
+ try:
+ _netrc = netrc(netrc_path).authenticators(host)
+ if _netrc:
+ # Return with login / password
+ login_i = (0 if _netrc[0] else 1)
+ return (_netrc[login_i], _netrc[2])
+ except (NetrcParseError, IOError):
+ # If there was a parsing error or a permissions issue reading the file,
+ # we'll just skip netrc auth
+ pass
+
+ # AppEngine hackiness.
+ except AttributeError:
+ pass
+
+
+
+def dict_from_string(s):
+ """Returns a MultiDict with Cookies."""
+
+ cookies = dict()
+
+ try:
+ c = SimpleCookie()
+ c.load(s)
+
+ for k, v in list(c.items()):
+ cookies.update({k: v.value})
+ # This stuff is not to be trusted.
+ except Exception:
+ pass
+
+ return cookies
+
+
+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):
+ """Returns an HTTP Header value string from a dictionary.
+
+ Example expansion::
+
+ {'text/x-dvi': {'q': '.8', 'mxb': '100000', 'mxt': '5.0'}, 'text/x-c': {}}
+ # Accept: text/x-dvi; q=.8; mxb=100000; mxt=5.0, text/x-c
+
+ (('text/x-dvi', {'q': '.8', 'mxb': '100000', 'mxt': '5.0'}), ('text/x-c', {}))
+ # Accept: text/x-dvi; q=.8; mxb=100000; mxt=5.0, text/x-c
+ """
+
+ collector = []
+
+ if isinstance(headers, dict):
+ headers = list(headers.items())
+ elif isinstance(headers, basestring):
+ return headers
+ elif isinstance(headers, str):
+ # As discussed in https://github.com/kennethreitz/requests/issues/400
+ # latin-1 is the most conservative encoding used on the web. Anyone
+ # who needs more can encode to a byte-string before calling
+ return headers.encode("latin-1")
+ elif headers is None:
+ return headers
+
+ for i, (value, params) in enumerate(headers):
+
+ _params = []
+
+ for (p_k, p_v) in list(params.items()):
+
+ _params.append('%s=%s' % (p_k, p_v))
+
+ collector.append(value)
+ collector.append('; ')
+
+ if len(params):
+
+ collector.append('; '.join(_params))
+
+ if not len(headers) == i + 1:
+ collector.append(', ')
+
+ # Remove trailing separators.
+ if collector[-1] in (', ', '; '):
+ del collector[-1]
+
+ return ''.join(collector)
+
+
+def randombytes(n):
+ """Return n random bytes."""
+ if is_py2:
+ L = [chr(random.randrange(0, 256)) for i in range(n)]
+ else:
+ L = [chr(random.randrange(0, 256)).encode('utf-8') for i in range(n)]
+ return b"".join(L)
+
+
+def dict_from_cookiejar(cj):
+ """Returns a key/value dictionary from a CookieJar.
+
+ :param cj: CookieJar object to extract cookies from.
+ """
+
+ cookie_dict = {}
+
+ for _, cookies in list(cj._cookies.items()):
+ for _, cookies in list(cookies.items()):
+ for cookie in list(cookies.values()):
+ # print cookie
+ cookie_dict[cookie.name] = cookie.value
+
+ return cookie_dict
+
+
+def cookiejar_from_dict(cookie_dict):
+ """Returns a CookieJar from a key/value dictionary.
+
+ :param cookie_dict: Dict of key/values to insert into CookieJar.
+ """
+
+ # return cookiejar if one was passed in
+ if isinstance(cookie_dict, cookielib.CookieJar):
+ return cookie_dict
+
+ # create cookiejar
+ cj = cookielib.CookieJar()
+
+ cj = add_dict_to_cookiejar(cj, cookie_dict)
+
+ return cj
+
+
+def add_dict_to_cookiejar(cj, cookie_dict):
+ """Returns a CookieJar from a key/value dictionary.
+
+ :param cj: CookieJar to insert cookies into.
+ :param cookie_dict: Dict of key/values to insert into CookieJar.
+ """
+
+ for k, v in list(cookie_dict.items()):
+
+ cookie = cookielib.Cookie(
+ version=0,
+ name=k,
+ value=v,
+ port=None,
+ port_specified=False,
+ domain='',
+ domain_specified=False,
+ domain_initial_dot=False,
+ path='/',
+ path_specified=True,
+ secure=False,
+ expires=None,
+ discard=True,
+ comment=None,
+ comment_url=None,
+ rest={'HttpOnly': None},
+ rfc2109=False
+ )
+
+ # add cookie to cookiejar
+ cj.set_cookie(cookie)
+
+ return cj
+
+
+def get_encodings_from_content(content):
+ """Returns encodings from given content string.
+
+ :param content: bytestring to extract encodings from.
+ """
+
+ charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)
+
+ return charset_re.findall(content)
+
+
+def get_encoding_from_headers(headers):
+ """Returns encodings from given HTTP Header Dict.
+
+ :param headers: dictionary to extract encoding from.
+ """
+
+ content_type = headers.get('content-type')
+
+ if not content_type:
+ return None
+
+ content_type, params = cgi.parse_header(content_type)
+
+ if 'charset' in params:
+ return params['charset'].strip("'\"")
+
+ if 'text' in content_type:
+ return 'ISO-8859-1'
+
+
+def stream_decode_response_unicode(iterator, r):
+ """Stream decodes a iterator."""
+
+ if r.encoding is None:
+ for item in iterator:
+ yield item
+ return
+
+ decoder = codecs.getincrementaldecoder(r.encoding)(errors='replace')
+ for chunk in iterator:
+ rv = decoder.decode(chunk)
+ if rv:
+ yield rv
+ rv = decoder.decode('', final=True)
+ if rv:
+ yield rv
+
+
+def get_unicode_from_response(r):
+ """Returns the requested content back in unicode.
+
+ :param r: Response object to get unicode content from.
+
+ Tried:
+
+ 1. charset from content-type
+
+ 2. every encodings from ``<meta ... charset=XXX>``
+
+ 3. fall back and replace all unicode characters
+
+ """
+
+ tried_encodings = []
+
+ # Try charset from content-type
+ encoding = get_encoding_from_headers(r.headers)
+
+ if encoding:
+ try:
+ return str(r.content, encoding)
+ except UnicodeError:
+ tried_encodings.append(encoding)
+
+ # Fall back:
+ try:
+ return str(r.content, encoding, errors='replace')
+ except TypeError:
+ return r.content
+
+
+def stream_decompress(iterator, mode='gzip'):
+ """
+ Stream decodes an iterator over compressed data
+
+ :param iterator: An iterator over compressed data
+ :param mode: 'gzip' or 'deflate'
+ :return: An iterator over decompressed data
+ """
+
+ if mode not in ['gzip', 'deflate']:
+ raise ValueError('stream_decompress mode must be gzip or deflate')
+
+ zlib_mode = 16 + zlib.MAX_WBITS if mode == 'gzip' else -zlib.MAX_WBITS
+ dec = zlib.decompressobj(zlib_mode)
+ try:
+ for chunk in iterator:
+ rv = dec.decompress(chunk)
+ if rv:
+ yield rv
+ except zlib.error:
+ # If there was an error decompressing, just return the raw chunk
+ yield chunk
+ # Continue to return the rest of the raw data
+ for chunk in iterator:
+ yield chunk
+ else:
+ # Make sure everything has been returned from the decompression object
+ buf = dec.decompress(bytes())
+ rv = buf + dec.flush()
+ if rv:
+ yield rv
+
+
+def stream_untransfer(gen, resp):
+ if 'gzip' in resp.headers.get('content-encoding', ''):
+ gen = stream_decompress(gen, mode='gzip')
+ elif 'deflate' in resp.headers.get('content-encoding', ''):
+ gen = stream_decompress(gen, mode='deflate')
+
+ return gen
+
+
+# The unreserved URI characters (RFC 3986)
+UNRESERVED_SET = frozenset(
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ + "0123456789-._~")
+
+
+def unquote_unreserved(uri):
+ """Un-escape any percent-escape sequences in a URI that are unreserved
+ characters.
+ This leaves all reserved, illegal and non-ASCII bytes encoded.
+ """
+ parts = uri.split('%')
+ for i in range(1, len(parts)):
+ h = parts[i][0:2]
+ if len(h) == 2:
+ c = chr(int(h, 16))
+ if c in UNRESERVED_SET:
+ parts[i] = c + parts[i][2:]
+ else:
+ parts[i] = '%' + parts[i]
+ else:
+ parts[i] = '%' + parts[i]
+ return ''.join(parts)
+
+
+def requote_uri(uri):
+ """Re-quote the given URI.
+
+ This function passes the given URI through an unquote/quote cycle to
+ ensure that it is fully and consistently quoted.
+ """
+ # Unquote only the unreserved characters
+ # Then quote only illegal characters (do not quote reserved, unreserved,
+ # or '%')
+ return quote(unquote_unreserved(uri), safe="!#$%&'()*+,/:;=?@[]~")