aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSVN-Git Migration <python-modules-team@lists.alioth.debian.org>2015-10-08 13:41:22 -0700
committerSVN-Git Migration <python-modules-team@lists.alioth.debian.org>2015-10-08 13:41:22 -0700
commit3a4ef8165fb2951781a7bcc4189e90faf26caf2d (patch)
tree5223d80835a57dad6b7b6e0c37f689441ccb4e1e
parent40337989ba5056432c9f2af3c42267e5ee9e3e18 (diff)
downloadpython-requests-3a4ef8165fb2951781a7bcc4189e90faf26caf2d.tar
python-requests-3a4ef8165fb2951781a7bcc4189e90faf26caf2d.tar.gz
Imported Upstream version 0.11.2
-rw-r--r--HISTORY.rst11
-rw-r--r--MANIFEST.in2
-rw-r--r--PKG-INFO31
-rw-r--r--README.rst4
-rw-r--r--requests.egg-info/PKG-INFO31
-rw-r--r--requests.egg-info/SOURCES.txt6
-rw-r--r--requests/__init__.py4
-rw-r--r--requests/auth.py20
-rw-r--r--requests/exceptions.py5
-rw-r--r--requests/models.py101
-rw-r--r--requests/packages/urllib3/__init__.py2
-rw-r--r--requests/packages/urllib3/connectionpool.py9
-rw-r--r--requests/sessions.py3
-rw-r--r--requests/utils.py73
-rwxr-xr-xsetup.py4
-rwxr-xr-xtests/test_requests.py846
-rwxr-xr-xtests/test_requests_async.py68
-rw-r--r--tests/test_requests_ext.py112
-rwxr-xr-xtests/test_requests_https.py31
19 files changed, 1274 insertions, 89 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index e73903d..153cbb5 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -1,6 +1,17 @@
+.. :changelog:
+
History
-------
+0.11.2 (2012-04-22)
++++++++++++++++++++
+
+- Attempt to use the OS's certificate bundle if ``certifi`` isn't available.
+- Infinite digest auth redirect fix
+- Multi-part file upload improvements
+- Fix decoding of invalid %encodings in URLs
+- If there is no content in a response don't throw an error the second time that content is attempted to be read.
+- Upload data on redirects.
0.11.1 (2012-03-30)
+++++++++++++++++++
diff --git a/MANIFEST.in b/MANIFEST.in
index ef350d0..0417d6f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1 +1 @@
-include README.rst LICENSE NOTICE HISTORY.rst test_requests.py
+include README.rst LICENSE NOTICE HISTORY.rst tests/*.py
diff --git a/PKG-INFO b/PKG-INFO
index 68c9ccc..dc8b700 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,14 +1,27 @@
Metadata-Version: 1.0
Name: requests
-Version: 0.11.1
+Version: 0.11.2
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: me@kennethreitz.com
-License: ISC
+License: Copyright (c) 2012 Kenneth Reitz.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, 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.
Description: Requests: HTTP for Humans
=========================
+
.. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop
Requests is an ISC Licensed HTTP library, written in Python, for human
@@ -34,8 +47,7 @@ Description: Requests: HTTP for Humans
See `the same code, without Requests <https://gist.github.com/973705>`_.
- Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
- **PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
+ Requests allow you to send HTTP/1.1 requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by httplib and `urllib3
<https://github.com/shazow/urllib3>`_, but it does all the hard work and crazy
@@ -84,9 +96,20 @@ Description: Requests: HTTP for Humans
.. _AUTHORS: https://github.com/kennethreitz/requests/blob/develop/AUTHORS.rst
+ .. :changelog:
+
History
-------
+ 0.11.2 (2012-04-22)
+ +++++++++++++++++++
+
+ - Attempt to use the OS's certificate bundle if ``certifi`` isn't available.
+ - Infinite digest auth redirect fix
+ - Multi-part file upload improvements
+ - Fix decoding of invalid %encodings in URLs
+ - If there is no content in a response don't throw an error the second time that content is attempted to be read.
+ - Upload data on redirects.
0.11.1 (2012-03-30)
+++++++++++++++++++
diff --git a/README.rst b/README.rst
index 7ea6ab6..46f1862 100644
--- a/README.rst
+++ b/README.rst
@@ -1,6 +1,7 @@
Requests: HTTP for Humans
=========================
+
.. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop
Requests is an ISC Licensed HTTP library, written in Python, for human
@@ -26,8 +27,7 @@ Things shouldn't be this way. Not in Python.
See `the same code, without Requests <https://gist.github.com/973705>`_.
-Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
-**PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
+Requests allow you to send HTTP/1.1 requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by httplib and `urllib3
<https://github.com/shazow/urllib3>`_, but it does all the hard work and crazy
diff --git a/requests.egg-info/PKG-INFO b/requests.egg-info/PKG-INFO
index 68c9ccc..dc8b700 100644
--- a/requests.egg-info/PKG-INFO
+++ b/requests.egg-info/PKG-INFO
@@ -1,14 +1,27 @@
Metadata-Version: 1.0
Name: requests
-Version: 0.11.1
+Version: 0.11.2
Summary: Python HTTP for Humans.
Home-page: http://python-requests.org
Author: Kenneth Reitz
Author-email: me@kennethreitz.com
-License: ISC
+License: Copyright (c) 2012 Kenneth Reitz.
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, 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.
Description: Requests: HTTP for Humans
=========================
+
.. image:: https://secure.travis-ci.org/kennethreitz/requests.png?branch=develop
Requests is an ISC Licensed HTTP library, written in Python, for human
@@ -34,8 +47,7 @@ Description: Requests: HTTP for Humans
See `the same code, without Requests <https://gist.github.com/973705>`_.
- Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
- **PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
+ Requests allow you to send HTTP/1.1 requests. You can add headers, form data,
multipart files, and parameters with simple Python dictionaries, and access the
response data in the same way. It's powered by httplib and `urllib3
<https://github.com/shazow/urllib3>`_, but it does all the hard work and crazy
@@ -84,9 +96,20 @@ Description: Requests: HTTP for Humans
.. _AUTHORS: https://github.com/kennethreitz/requests/blob/develop/AUTHORS.rst
+ .. :changelog:
+
History
-------
+ 0.11.2 (2012-04-22)
+ +++++++++++++++++++
+
+ - Attempt to use the OS's certificate bundle if ``certifi`` isn't available.
+ - Infinite digest auth redirect fix
+ - Multi-part file upload improvements
+ - Fix decoding of invalid %encodings in URLs
+ - If there is no content in a response don't throw an error the second time that content is attempted to be read.
+ - Upload data on redirects.
0.11.1 (2012-03-30)
+++++++++++++++++++
diff --git a/requests.egg-info/SOURCES.txt b/requests.egg-info/SOURCES.txt
index 71152be..f228744 100644
--- a/requests.egg-info/SOURCES.txt
+++ b/requests.egg-info/SOURCES.txt
@@ -39,4 +39,8 @@ requests/packages/urllib3/util.py
requests/packages/urllib3/packages/__init__.py
requests/packages/urllib3/packages/six.py
requests/packages/urllib3/packages/mimetools_choose_boundary/__init__.py
-requests/packages/urllib3/packages/ssl_match_hostname/__init__.py \ No newline at end of file
+requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
+tests/test_requests.py
+tests/test_requests_async.py
+tests/test_requests_ext.py
+tests/test_requests_https.py \ No newline at end of file
diff --git a/requests/__init__.py b/requests/__init__.py
index 0ace12b..96ef774 100644
--- a/requests/__init__.py
+++ b/requests/__init__.py
@@ -15,8 +15,8 @@ requests
"""
__title__ = 'requests'
-__version__ = '0.11.1'
-__build__ = 0x001101
+__version__ = '0.11.2'
+__build__ = 0x001102
__author__ = 'Kenneth Reitz'
__license__ = 'ISC'
__copyright__ = 'Copyright 2012 Kenneth Reitz'
diff --git a/requests/auth.py b/requests/auth.py
index 385dd27..353180a 100644
--- a/requests/auth.py
+++ b/requests/auth.py
@@ -56,6 +56,8 @@ class HTTPDigestAuth(AuthBase):
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():
@@ -74,21 +76,21 @@ class HTTPDigestAuth(AuthBase):
algorithm = algorithm.upper()
# lambdas assume digest modules are imported at the top level
if algorithm == 'MD5':
- def h(x):
+ def md5_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.md5(x).hexdigest()
- H = h
+ hash_utf8 = md5_utf8
elif algorithm == 'SHA':
- def h(x):
+ def sha_utf8(x):
if isinstance(x, str):
x = x.encode('utf-8')
return hashlib.sha1(x).hexdigest()
- H = h
+ hash_utf8 = sha_utf8
# XXX MD5-sess
- KD = lambda s, d: H("%s:%s" % (s, d))
+ KD = lambda s, d: hash_utf8("%s:%s" % (s, d))
- if H is None:
+ if hash_utf8 is None:
return None
# XXX not implemented yet
@@ -115,10 +117,10 @@ class HTTPDigestAuth(AuthBase):
s += randombytes(8)
cnonce = (hashlib.sha1(s).hexdigest()[:16])
- noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
- respdig = KD(H(A1), noncebit)
+ 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(H(A1), "%s:%s" % (nonce, H(A2)))
+ respdig = KD(hash_utf8(A1), "%s:%s" % (nonce, hash_utf8(A2)))
else:
# XXX handle auth-int.
return None
diff --git a/requests/exceptions.py b/requests/exceptions.py
index 3c262e3..1cffa80 100644
--- a/requests/exceptions.py
+++ b/requests/exceptions.py
@@ -35,4 +35,7 @@ class MissingSchema(RequestException, ValueError):
"""The URL schema (e.g. http or https) is missing."""
class InvalidSchema(RequestException, ValueError):
- """See defaults.py for valid schemas.""" \ No newline at end of file
+ """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/models.py b/requests/models.py
index 70e3503..60f58d2 100644
--- a/requests/models.py
+++ b/requests/models.py
@@ -16,7 +16,7 @@ from .status_codes import codes
from .auth import HTTPBasicAuth, HTTPProxyAuth
from .packages.urllib3.response import HTTPResponse
-from .packages.urllib3.exceptions import MaxRetryError
+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
@@ -24,10 +24,11 @@ from .packages.urllib3.filepost import encode_multipart_formdata
from .defaults import SCHEMAS
from .exceptions import (
ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects,
- URLRequired, SSLError, MissingSchema, InvalidSchema)
+ 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)
+ 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)
@@ -61,6 +62,7 @@ class Request(object):
proxies=None,
hooks=None,
config=None,
+ prefetch=False,
_poolmanager=None,
verify=None,
session=None,
@@ -80,7 +82,7 @@ class Request(object):
self.headers = dict(headers or [])
#: Dictionary of files to multipart upload (``{filename: content}``).
- self.files = files
+ self.files = None
#: HTTP Method to use.
self.method = method
@@ -90,7 +92,8 @@ class Request(object):
self.data = None
#: Dictionary or byte of querystring data to attach to the
- #: :class:`Request <Request>`.
+ #: :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
@@ -113,6 +116,7 @@ class Request(object):
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>`.
@@ -147,6 +151,9 @@ class Request(object):
#: SSL Certificate
self.cert = cert
+ #: Prefetch response content
+ self.prefetch = prefetch
+
if headers:
headers = CaseInsensitiveDict(self.headers)
else:
@@ -328,6 +335,29 @@ class Request(object):
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."""
@@ -407,7 +437,18 @@ class Request(object):
def register_hook(self, event, hook):
"""Properly register a hook."""
- return self.hooks[event].append(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.
@@ -435,26 +476,7 @@ class Request(object):
# Multi-part file uploads.
if self.files:
- if not isinstance(self.data, str):
-
- try:
- fields = self.data.copy()
- except AttributeError:
- fields = dict(self.data)
-
- for (k, v) in list(self.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)
- else:
- pass
- # TODO: Conflict?
+ (body, content_type) = self._enc_files
else:
if self.data:
@@ -496,10 +518,13 @@ class Request(object):
self.__dict__.update(r.__dict__)
else:
# Check to see if keep_alive is allowed.
- if self.config.get('keep_alive'):
- conn = self._poolmanager.connection_from_url(url)
- else:
- conn = connectionpool.connection_from_url(url)
+ 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:
@@ -513,13 +538,15 @@ class Request(object):
if not cert_loc and self.config.get('trust_env'):
cert_loc = os.environ.get('REQUESTS_CA_BUNDLE')
- # Curl compatiblity.
+ # Curl compatibility.
if not cert_loc and self.config.get('trust_env'):
cert_loc = os.environ.get('CURL_CA_BUNDLE')
- # Use the awesome certifi list.
if not cert_loc:
- cert_loc = __import__('certifi').where()
+ 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
@@ -604,7 +631,7 @@ class Request(object):
self.__dict__.update(r.__dict__)
# If prefetch is True, mark content as consumed.
- if prefetch:
+ if prefetch or self.prefetch:
# Save the response.
self.response.content
@@ -623,7 +650,7 @@ class Response(object):
def __init__(self):
- self._content = None
+ self._content = False
self._content_consumed = False
#: Integer Code of responded HTTP Status.
@@ -679,7 +706,7 @@ class Response(object):
return False
return True
- def iter_content(self, chunk_size=10 * 1024, decode_unicode=False):
+ 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
@@ -736,7 +763,7 @@ class Response(object):
def content(self):
"""Content of the response, in bytes."""
- if self._content is None:
+ if self._content is False:
# Read the contents.
try:
if self._content_consumed:
diff --git a/requests/packages/urllib3/__init__.py b/requests/packages/urllib3/__init__.py
index 2d6fece..81e76f5 100644
--- a/requests/packages/urllib3/__init__.py
+++ b/requests/packages/urllib3/__init__.py
@@ -10,7 +10,7 @@ urllib3 - Thread-safe connection pooling and re-using.
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
__license__ = 'MIT'
-__version__ = '1.3'
+__version__ = 'dev'
from .connectionpool import (
diff --git a/requests/packages/urllib3/connectionpool.py b/requests/packages/urllib3/connectionpool.py
index c3cb3b1..336aa77 100644
--- a/requests/packages/urllib3/connectionpool.py
+++ b/requests/packages/urllib3/connectionpool.py
@@ -260,10 +260,11 @@ class HTTPConnectionPool(ConnectionPool, RequestMethods):
httplib_response = conn.getresponse()
- log.debug("\"%s %s %s\" %s %s" %
- (method, url,
- conn._http_vsn_str, # pylint: disable-msg=W0212
- httplib_response.status, httplib_response.length))
+ # 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
diff --git a/requests/sessions.py b/requests/sessions.py
index 94c94bf..0e43030 100644
--- a/requests/sessions.py
+++ b/requests/sessions.py
@@ -52,7 +52,7 @@ class Session(object):
__attrs__ = [
'headers', 'cookies', 'auth', 'timeout', 'proxies', 'hooks',
- 'params', 'config', 'verify', 'cert']
+ 'params', 'config', 'verify', 'cert', 'prefetch']
def __init__(self,
@@ -179,6 +179,7 @@ class Session(object):
allow_redirects=allow_redirects,
proxies=proxies,
config=config,
+ prefetch=prefetch,
verify=verify,
cert=cert,
_poolmanager=self.poolmanager
diff --git a/requests/utils.py b/requests/utils.py
index b722d99..925547a 100644
--- a/requests/utils.py
+++ b/requests/utils.py
@@ -21,9 +21,36 @@ 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."""
@@ -37,34 +64,40 @@ def dict_to_sequence(d):
def get_netrc_auth(url):
"""Returns the Requests tuple auth for a given url from netrc."""
- locations = (os.path.expanduser('~/{0}'.format(f)) for f in NETRC_FILES)
- netrc_path = None
+ 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
+ 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
+ # Abort early if there isn't one.
+ if netrc_path is None:
+ return netrc_path
- ri = urlparse(url)
+ ri = urlparse(url)
- # Strip port numbers from netloc
- host = ri.netloc.split(':')[0]
+ # 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, AttributeError):
- # If there was a parsing error or a permissions issue reading the file,
- # we'll just skip netrc auth
+ 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."""
diff --git a/setup.py b/setup.py
index 849ab83..3133a97 100755
--- a/setup.py
+++ b/setup.py
@@ -18,7 +18,7 @@ if sys.argv[-1] == 'publish':
sys.exit()
if sys.argv[-1] == 'test':
- os.system('python test_requests.py')
+ os.system('python tests/test_requests.py')
sys.exit()
required = ['certifi>=0.0.7',]
@@ -51,7 +51,7 @@ setup(
package_data={'': ['LICENSE', 'NOTICE']},
include_package_data=True,
install_requires=required,
- license='ISC',
+ license=open("LICENSE").read(),
classifiers=(
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
diff --git a/tests/test_requests.py b/tests/test_requests.py
new file mode 100755
index 0000000..ac8329e
--- /dev/null
+++ b/tests/test_requests.py
@@ -0,0 +1,846 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# from __future__ import unicode_literals
+
+# Path hack.
+import sys
+import os
+sys.path.insert(0, os.path.abspath('..'))
+
+import json
+import os
+import sys
+import unittest
+import pickle
+
+import requests
+from requests.compat import str, StringIO
+# import envoy
+from requests import HTTPError
+from requests import get, post, head, put
+from requests.auth import HTTPBasicAuth, HTTPDigestAuth
+
+if 'HTTPBIN_URL' not in os.environ:
+ os.environ['HTTPBIN_URL'] = 'http://httpbin.org/'
+
+HTTPBIN_URL = os.environ.get('HTTPBIN_URL')
+
+
+def httpbin(*suffix):
+ """Returns url for HTTPBIN resource."""
+ return HTTPBIN_URL + '/'.join(suffix)
+
+
+SERVICES = (httpbin, )
+
+_httpbin = False
+
+
+class TestSetup(object):
+ """Requests test cases."""
+
+ # It goes to eleven.
+ _multiprocess_can_split_ = True
+
+ def setUp(self):
+
+ global _httpbin
+
+ if (not 'HTTPBIN_URL' in os.environ) and not _httpbin:
+ # c = envoy.connect('httpbin %s' % (PORT))
+ # time.sleep(1)
+ _httpbin = True
+
+
+class RequestsTestSuite(TestSetup, unittest.TestCase):
+ """Requests test cases."""
+
+ def test_entry_points(self):
+
+ requests.session
+ requests.session().get
+ requests.session().head
+ requests.get
+ requests.head
+ requests.put
+ requests.patch
+ requests.post
+
+ def test_invalid_url(self):
+ self.assertRaises(ValueError, get, 'hiwpefhipowhefopw')
+
+ def test_path_is_not_double_encoded(self):
+ request = requests.Request("http://0.0.0.0/get/test case")
+
+ self.assertEqual(request.path_url, "/get/test%20case")
+
+ def test_HTTP_200_OK_GET(self):
+ r = get(httpbin('get'))
+ self.assertEqual(r.status_code, 200)
+
+ def test_response_sent(self):
+ r = get(httpbin('get'))
+
+ self.assertTrue(r.request.sent)
+
+ def test_HTTP_302_ALLOW_REDIRECT_GET(self):
+ r = get(httpbin('redirect', '1'))
+ self.assertEqual(r.status_code, 200)
+
+ def test_HTTP_302_GET(self):
+ r = get(httpbin('redirect', '1'), allow_redirects=False)
+ self.assertEqual(r.status_code, 302)
+
+ def test_HTTP_200_OK_GET_WITH_PARAMS(self):
+ heads = {'User-agent': 'Mozilla/5.0'}
+
+ r = get(httpbin('user-agent'), headers=heads)
+
+ assert heads['User-agent'] in r.text
+ self.assertEqual(r.status_code, 200)
+
+ def test_HTTP_200_OK_GET_WITH_MIXED_PARAMS(self):
+ heads = {'User-agent': 'Mozilla/5.0'}
+
+ r = get(httpbin('get') + '?test=true', params={'q': 'test'}, headers=heads)
+ self.assertEqual(r.status_code, 200)
+
+ # def test_unicode_headers(self):
+ # # Simply calling requests with a unicode instance should simply work
+ # # when the characters are all representable using latin-1:
+ # heads = { u'User-Agent': u'Requests Test Suite' }
+ # requests.get(url=httpbin('get'), headers=heads)
+
+ # # Characters outside latin-1 should raise an exception:
+ # heads = { u'User-Agent': u'\u30cd\u30c3\u30c8\u30ef\u30fc\u30af' }
+ # self.assertRaises(UnicodeEncodeError, requests.get,
+ # url=httpbin('get'), headers=heads)
+
+ # def test_session_with_escaped_url(self):
+ # # Test a URL that contains percent-escaped characters
+ # # This URL should not be modified (double-escaped)
+ # # Tests:
+ # # - Quoted illegal characters ("%20" (' '), "%3C" ('<'), "%3E" ('>'))
+ # # - Quoted reserved characters ("%25" ('%'), "%23" ('#'), "%2F" ('/'))
+ # # - Quoted non-ASCII characters ("%C3%98", "%C3%A5")
+ # path_fully_escaped = '%3Ca%25b%23c%2Fd%3E/%C3%98%20%C3%A5'
+ # url = httpbin('get/' + path_fully_escaped)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/' + path_fully_escaped))
+
+ # # Test that illegal characters in a path get properly percent-escaped
+ # # Tests:
+ # # - Bare illegal characters (space, '<')
+ # # - Bare non-ASCII characters ('\u00d8')
+ # path = u'<a%25b%23c%2Fd%3E/\u00d8 %C3%A5'
+ # url = httpbin('get/' + path)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/' + path_fully_escaped))
+
+ # # Test that reserved characters in a path do not get percent-escaped
+ # # Tests:
+ # # - All reserved characters (RFC 3986), except '?', '#', '[' and ']',
+ # # which are not allowed in the path, and ';' which delimits
+ # # parameters.
+ # # All such characters must be allowed bare in path, and must not be
+ # # encoded.
+ # # - Special unreserved characters (RFC 3986), which should not be
+ # # encoded (even though it wouldn't hurt).
+ # path_reserved = '!$&\'()*+,/:=@-._~'
+ # url = httpbin('get/' + path_reserved)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/' + path_reserved))
+
+ # # Test that percent-encoded unreserved characters in a path get
+ # # normalised to their un-encoded forms.
+ # path_unreserved = 'ABCDwxyz1234-._~'
+ # path_unreserved_escaped = '%41%42%43%44%77%78%79%7A%31%32%33%34%2D%2E%5F%7E'
+ # url = httpbin('get/' + path_unreserved_escaped)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/' + path_unreserved))
+
+ # # Re-run all of the same tests on the query part of the URI
+ # query_fully_escaped = '%3Ca%25b%23c%2Fd%3E=%C3%98%20%C3%A5'
+ # url = httpbin('get/?' + query_fully_escaped)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/?' + query_fully_escaped))
+
+ # query = u'<a%25b%23c%2Fd%3E=\u00d8 %C3%A5'
+ # url = httpbin('get/?' + query)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/?' + query_fully_escaped))
+
+ # # The legal characters in query happens to be the same as in path
+ # query_reserved = '!$&\'()*+,/:=@-._~'
+ # url = httpbin('get/?' + query_reserved)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/?' + query_reserved))
+
+ # query_unreserved = 'ABCDwxyz=1234-._~'
+ # query_unreserved_escaped = '%41%42%43%44%77%78%79%7A=%31%32%33%34%2D%2E%5F%7E'
+ # url = httpbin('get/?' + query_unreserved_escaped)
+ # response = get(url)
+ # self.assertEqual(response.url, httpbin('get/?' + query_unreserved))
+
+ def test_user_agent_transfers(self):
+ """Issue XX"""
+
+ heads = {
+ 'User-agent':
+ 'Mozilla/5.0 (github.com/kennethreitz/requests)'
+ }
+
+ r = get(httpbin('user-agent'), headers=heads)
+ self.assertTrue(heads['User-agent'] in r.text)
+
+ heads = {
+ 'user-agent':
+ 'Mozilla/5.0 (github.com/kennethreitz/requests)'
+ }
+
+ r = get(httpbin('user-agent'), headers=heads)
+ self.assertTrue(heads['user-agent'] in r.text)
+
+ def test_HTTP_200_OK_HEAD(self):
+ r = head(httpbin('get'))
+ self.assertEqual(r.status_code, 200)
+
+ def test_HTTP_200_OK_PUT(self):
+ r = put(httpbin('put'))
+ self.assertEqual(r.status_code, 200)
+
+ def test_BASICAUTH_TUPLE_HTTP_200_OK_GET(self):
+
+ for service in SERVICES:
+
+ auth = ('user', 'pass')
+ url = service('basic-auth', 'user', 'pass')
+
+ r = get(url, auth=auth)
+ self.assertEqual(r.status_code, 200)
+
+ r = get(url)
+ self.assertEqual(r.status_code, 401)
+
+ s = requests.session(auth=auth)
+ r = get(url, session=s)
+ self.assertEqual(r.status_code, 200)
+
+ def test_BASICAUTH_HTTP_200_OK_GET(self):
+
+ for service in SERVICES:
+
+ auth = HTTPBasicAuth('user', 'pass')
+ url = service('basic-auth', 'user', 'pass')
+
+ r = get(url, auth=auth)
+ self.assertEqual(r.status_code, 200)
+
+ auth = ('user', 'pass')
+ r = get(url, auth=auth)
+ self.assertEqual(r.status_code, 200)
+
+ r = get(url)
+ self.assertEqual(r.status_code, 401)
+
+ s = requests.session(auth=auth)
+ r = get(url, session=s)
+ self.assertEqual(r.status_code, 200)
+
+ def test_DIGESTAUTH_HTTP_200_OK_GET(self):
+
+ for service in SERVICES:
+
+ auth = HTTPDigestAuth('user', 'pass')
+ url = service('digest-auth', 'auth', 'user', 'pass')
+
+ r = get(url, auth=auth)
+ self.assertEqual(r.status_code, 200)
+
+ r = get(url)
+ self.assertEqual(r.status_code, 401)
+
+ s = requests.session(auth=auth)
+ r = get(url, session=s)
+ self.assertEqual(r.status_code, 200)
+
+ def test_DIGESTAUTH_WRONG_HTTP_401_GET(self):
+
+ for service in SERVICES:
+
+ auth = HTTPDigestAuth('user', 'wrongpass')
+ url = service('digest-auth', 'auth', 'user', 'pass')
+
+ r = get(url, auth=auth)
+ self.assertEqual(r.status_code, 401)
+
+ s = requests.session(auth=auth)
+ r = get(url, session=s)
+ self.assertEqual(r.status_code, 401)
+
+ def test_POSTBIN_GET_POST_FILES(self):
+
+ for service in SERVICES:
+
+ url = service('post')
+ post1 = post(url).raise_for_status()
+
+ post1 = post(url, data={'some': 'data'})
+ self.assertEqual(post1.status_code, 200)
+
+ with open(__file__) as f:
+ post2 = post(url, files={'some': f})
+ self.assertEqual(post2.status_code, 200)
+
+ post3 = post(url, data='[{"some": "json"}]')
+ self.assertEqual(post3.status_code, 200)
+
+ def test_POSTBIN_GET_POST_FILES_WITH_PARAMS(self):
+
+ for service in SERVICES:
+
+ with open(__file__) as f:
+ url = service('post')
+ post1 = post(url,
+ files={'some': f},
+ data={'some': 'data'})
+
+ self.assertEqual(post1.status_code, 200)
+
+ def test_POSTBIN_GET_POST_FILES_WITH_HEADERS(self):
+
+ for service in SERVICES:
+
+ url = service('post')
+
+ with open(__file__) as f:
+
+ post2 = post(url,
+ files={'some': f},
+ headers={'User-Agent': 'requests-tests'})
+
+ self.assertEqual(post2.status_code, 200)
+
+ def test_nonzero_evaluation(self):
+
+ for service in SERVICES:
+
+ r = get(service('status', '500'))
+ self.assertEqual(bool(r), False)
+
+ r = get(service('/get'))
+ self.assertEqual(bool(r), True)
+
+ def test_request_ok_set(self):
+
+ for service in SERVICES:
+
+ r = get(service('status', '404'))
+ # print r.status_code
+ # r.raise_for_status()
+ self.assertEqual(r.ok, False)
+
+ def test_status_raising(self):
+ r = get(httpbin('status', '404'))
+ self.assertRaises(HTTPError, r.raise_for_status)
+
+ r = get(httpbin('status', '200'))
+ self.assertFalse(r.error)
+ r.raise_for_status()
+
+ def test_default_status_raising(self):
+ config = {'danger_mode': True}
+ args = [httpbin('status', '404')]
+ kwargs = dict(config=config)
+ self.assertRaises(HTTPError, get, *args, **kwargs)
+
+ r = get(httpbin('status', '200'))
+ self.assertEqual(r.status_code, 200)
+
+ def test_decompress_gzip(self):
+
+ r = get(httpbin('gzip'))
+ r.content.decode('ascii')
+
+ def test_response_has_unicode_url(self):
+
+ for service in SERVICES:
+
+ url = service('get')
+
+ response = get(url)
+
+ assert isinstance(response.url, str)
+
+ def test_unicode_get(self):
+
+ for service in SERVICES:
+
+ url = service('/get')
+
+ get(url, params={'foo': 'føø'})
+ get(url, params={'føø': 'føø'})
+ get(url, params={'føø': 'føø'})
+ get(url, params={'foo': 'foo'})
+ get(service('ø'), params={'foo': 'foo'})
+
+ def test_httpauth_recursion(self):
+
+ http_auth = HTTPBasicAuth('user', 'BADpass')
+
+ for service in SERVICES:
+ r = get(service('basic-auth', 'user', 'pass'), auth=http_auth)
+ self.assertEqual(r.status_code, 401)
+
+ def test_urlencoded_post_data(self):
+
+ for service in SERVICES:
+
+ r = post(service('post'), data=dict(test='fooaowpeuf'))
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post'))
+
+ rbody = json.loads(r.text)
+
+ self.assertEqual(rbody.get('form'), dict(test='fooaowpeuf'))
+ self.assertEqual(rbody.get('data'), '')
+
+ def test_nonurlencoded_post_data(self):
+
+ for service in SERVICES:
+
+ r = post(service('post'), data='fooaowpeuf')
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post'))
+
+ rbody = json.loads(r.text)
+ # Body wasn't valid url encoded data, so the server returns None as
+ # "form" and the raw body as "data".
+
+ assert rbody.get('form') in (None, {})
+ self.assertEqual(rbody.get('data'), 'fooaowpeuf')
+
+ def test_urlencoded_post_querystring(self):
+
+ for service in SERVICES:
+
+ r = post(service('post'), params=dict(test='fooaowpeuf'))
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post?test=fooaowpeuf'))
+
+ rbody = json.loads(r.text)
+ self.assertEqual(rbody.get('form'), {}) # No form supplied
+ self.assertEqual(rbody.get('data'), '')
+
+ def test_urlencoded_post_query_and_data(self):
+
+ for service in SERVICES:
+
+ r = post(
+ service('post'),
+ params=dict(test='fooaowpeuf'),
+ data=dict(test2="foobar"))
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post?test=fooaowpeuf'))
+
+ rbody = json.loads(r.text)
+ self.assertEqual(rbody.get('form'), dict(test2='foobar'))
+ self.assertEqual(rbody.get('data'), '')
+
+ def test_nonurlencoded_postdata(self):
+
+ for service in SERVICES:
+
+ r = post(service('post'), data="foobar")
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+
+ rbody = json.loads(r.text)
+
+ assert rbody.get('form') in (None, {})
+ self.assertEqual(rbody.get('data'), 'foobar')
+
+ def test_urlencoded_get_query_multivalued_param(self):
+
+ for service in SERVICES:
+
+ r = get(service('get'), params=dict(test=['foo', 'baz']))
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.url, service('get?test=foo&test=baz'))
+
+ def test_urlencoded_post_querystring_multivalued(self):
+
+ for service in SERVICES:
+
+ r = post(service('post'), params=dict(test=['foo', 'baz']))
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post?test=foo&test=baz'))
+
+ rbody = json.loads(r.text)
+ self.assertEqual(rbody.get('form'), {}) # No form supplied
+ self.assertEqual(rbody.get('data'), '')
+
+ def test_urlencoded_post_query_multivalued_and_data(self):
+
+ for service in SERVICES:
+
+ r = post(
+ service('post'),
+ params=dict(test=['foo', 'baz']),
+ data=dict(test2="foobar", test3=['foo', 'baz']))
+
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(r.headers['content-type'], 'application/json')
+ self.assertEqual(r.url, service('post?test=foo&test=baz'))
+
+ # print(r.text)
+ # print('-----------------------')
+
+ rbody = json.loads(r.text)
+ self.assertEqual(rbody.get('form'), dict(test2='foobar', test3=['foo', 'baz']))
+ self.assertEqual(rbody.get('data'), '')
+
+ def test_GET_no_redirect(self):
+
+ for service in SERVICES:
+
+ r = get(service('redirect', '3'), allow_redirects=False)
+ self.assertEqual(r.status_code, 302)
+ self.assertEqual(len(r.history), 0)
+
+ def test_HEAD_no_redirect(self):
+
+ for service in SERVICES:
+
+ r = head(service('redirect', '3'), allow_redirects=False)
+ self.assertEqual(r.status_code, 302)
+ self.assertEqual(len(r.history), 0)
+
+ def test_redirect_history(self):
+
+ for service in SERVICES:
+
+ r = get(service('redirect', '3'))
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(len(r.history), 3)
+
+ def test_relative_redirect_history(self):
+
+ for service in SERVICES:
+
+ r = get(service('relative-redirect', '3'))
+ self.assertEqual(r.status_code, 200)
+ self.assertEqual(len(r.history), 3)
+
+ def test_session_HTTP_200_OK_GET(self):
+
+ s = requests.session()
+ r = get(httpbin('get'), session=s)
+ self.assertEqual(r.status_code, 200)
+
+ def test_session_persistent_headers(self):
+
+ heads = {'User-agent': 'Mozilla/5.0'}
+
+ s = requests.session()
+ s.headers = heads
+
+ # Make 2 requests from Session object, should send header both times
+ r1 = get(httpbin('user-agent'), session=s)
+ assert heads['User-agent'] in r1.text
+
+ r2 = get(httpbin('user-agent'), session=s)
+ assert heads['User-agent'] in r2.text
+
+ new_heads = {'User-agent': 'blah'}
+ r3 = get(httpbin('user-agent'), headers=new_heads, session=s)
+ assert new_heads['User-agent'] in r3.text
+
+ self.assertEqual(r2.status_code, 200)
+
+ def test_single_hook(self):
+
+ def add_foo_header(args):
+ if not args.get('headers'):
+ args['headers'] = {}
+
+ args['headers'].update({
+ 'X-Foo': 'foo'
+ })
+
+ return args
+
+ for service in SERVICES:
+ url = service('headers')
+ response = get(url=url, hooks={'args': add_foo_header})
+
+ assert 'foo' in response.text
+
+ def test_multiple_hooks(self):
+
+ def add_foo_header(args):
+ if not args.get('headers'):
+ args['headers'] = {}
+
+ args['headers'].update({
+ 'X-Foo': 'foo'
+ })
+
+ return args
+
+ def add_bar_header(args):
+ if not args.get('headers'):
+ args['headers'] = {}
+
+ args['headers'].update({
+ 'X-Bar': 'bar'
+ })
+
+ return args
+
+ for service in SERVICES:
+ url = service('headers')
+
+ response = get(url=url,
+ hooks={
+ 'args': [add_foo_header, add_bar_header]
+ }
+ )
+
+ assert 'foo' in response.text
+ assert 'bar' in response.text
+
+ def test_session_persistent_cookies(self):
+
+ s = requests.session()
+
+ # Internally dispatched cookies are sent.
+ _c = {'kenneth': 'reitz', 'bessie': 'monke'}
+ r = get(httpbin('cookies'), cookies=_c, session=s)
+ r = get(httpbin('cookies'), session=s)
+
+ # Those cookies persist transparently.
+ c = json.loads(r.text).get('cookies')
+ assert c == _c
+
+ # Double check.
+ r = get(httpbin('cookies'), cookies={}, session=s)
+ c = json.loads(r.text).get('cookies')
+ assert c == _c
+
+ # Remove a cookie by setting it's value to None.
+ r = get(httpbin('cookies'), cookies={'bessie': None}, session=s)
+ c = json.loads(r.text).get('cookies')
+ del _c['bessie']
+ assert c == _c
+
+ # Test session-level cookies.
+ s = requests.session(cookies=_c)
+ r = get(httpbin('cookies'), session=s)
+ c = json.loads(r.text).get('cookies')
+ assert c == _c
+
+ # Have the server set a cookie.
+ r = get(httpbin('cookies', 'set', 'k', 'v'), allow_redirects=True, session=s)
+ c = json.loads(r.text).get('cookies')
+
+ assert 'k' in c
+
+ # And server-set cookie persistience.
+ r = get(httpbin('cookies'), session=s)
+ c = json.loads(r.text).get('cookies')
+
+ assert 'k' in c
+
+ def test_session_persistent_params(self):
+
+ params = {'a': 'a_test'}
+
+ s = requests.session()
+ s.params = params
+
+ # Make 2 requests from Session object, should send header both times
+ r1 = get(httpbin('get'), session=s)
+ assert params['a'] in r1.text
+
+ params2 = {'b': 'b_test'}
+
+ r2 = get(httpbin('get'), params=params2, session=s)
+ assert params['a'] in r2.text
+ assert params2['b'] in r2.text
+
+ params3 = {'b': 'b_test', 'a': None, 'c': 'c_test'}
+
+ r3 = get(httpbin('get'), params=params3, session=s)
+
+ assert not params['a'] in r3.text
+ assert params3['b'] in r3.text
+ assert params3['c'] in r3.text
+
+ def test_session_pickling(self):
+
+ s = requests.session(
+ headers={'header': 'value'},
+ cookies={'a-cookie': 'cookie-value'},
+ auth=('username', 'password'))
+
+ ds = pickle.loads(pickle.dumps(s))
+
+ self.assertEqual(s.headers, ds.headers)
+ self.assertEqual(s.cookies, ds.cookies)
+ self.assertEqual(s.auth, ds.auth)
+
+ def test_unpickled_session_requests(self):
+ s = requests.session()
+ r = get(httpbin('cookies', 'set', 'k', 'v'), allow_redirects=True, session=s)
+ c = json.loads(r.text).get('cookies')
+ assert 'k' in c
+
+ ds = pickle.loads(pickle.dumps(s))
+ r = get(httpbin('cookies'), session=ds)
+ c = json.loads(r.text).get('cookies')
+ assert 'k' in c
+
+ ds1 = pickle.loads(pickle.dumps(requests.session()))
+ ds2 = pickle.loads(pickle.dumps(requests.session(prefetch=True)))
+ assert not ds1.prefetch
+ assert ds2.prefetch
+
+ def test_invalid_content(self):
+ # WARNING: if you're using a terrible DNS provider (comcast),
+ # this will fail.
+ try:
+ hah = 'http://somedomainthatclearlydoesntexistg.com'
+ r = get(hah, allow_redirects=False)
+ except requests.ConnectionError:
+ pass # \o/
+ else:
+ assert False
+
+ config = {'safe_mode': True}
+ r = get(hah, allow_redirects=False, config=config)
+ assert r.content == None
+
+ def test_cached_response(self):
+
+ r1 = get(httpbin('get'), prefetch=False)
+ assert not r1._content
+ assert r1.content
+ assert r1.text
+
+ r2 = get(httpbin('get'), prefetch=True)
+ assert r2._content
+ assert r2.content
+ assert r2.text
+
+ def test_iter_lines(self):
+
+ lines = (0, 2, 10, 100)
+
+ for i in lines:
+ r = get(httpbin('stream', str(i)), prefetch=False)
+ lines = list(r.iter_lines())
+ len_lines = len(lines)
+
+ self.assertEqual(i, len_lines)
+
+ # Tests that trailing whitespaces within lines do not get stripped.
+ # Tests that a trailing non-terminated line does not get stripped.
+ quote = (
+ '''Agamemnon \n'''
+ '''\tWhy will he not upon our fair request\r\n'''
+ '''\tUntent his person and share the air with us?'''
+ )
+
+ # Make a request and monkey-patch its contents
+ r = get(httpbin('get'))
+ r.raw = StringIO(quote)
+
+ lines = list(r.iter_lines())
+ len_lines = len(lines)
+ self.assertEqual(len_lines, 3)
+
+ joined = lines[0] + '\n' + lines[1] + '\r\n' + lines[2]
+ self.assertEqual(joined, quote)
+
+ def test_safe_mode(self):
+
+ safe = requests.session(config=dict(safe_mode=True))
+
+ # Safe mode creates empty responses for failed requests.
+ # Iterating on these responses should produce empty sequences
+ r = get('http://_/', session=safe)
+ self.assertEqual(list(r.iter_lines()), [])
+ assert isinstance(r.error, requests.exceptions.ConnectionError)
+
+ r = get('http://_/', session=safe)
+ self.assertEqual(list(r.iter_content()), [])
+ assert isinstance(r.error, requests.exceptions.ConnectionError)
+
+ # When not in safe mode, should raise Timeout exception
+ self.assertRaises(
+ requests.exceptions.Timeout,
+ get,
+ httpbin('stream', '1000'), timeout=0.0001)
+
+ # In safe mode, should return a blank response
+ r = get(httpbin('stream', '1000'), timeout=0.0001,
+ config=dict(safe_mode=True))
+ assert r.content is None
+ assert isinstance(r.error, requests.exceptions.Timeout)
+
+ def test_upload_binary_data(self):
+
+ requests.get(httpbin('post'), auth=('a', 'b'), data='\xff')
+
+ def test_useful_exception_for_invalid_port(self):
+ # If we pass a legitimate URL with an invalid port, we should fail.
+ self.assertRaises(
+ ValueError,
+ get,
+ 'http://google.com:banana')
+
+ def test_useful_exception_for_invalid_scheme(self):
+
+ # If we pass a legitimate URL with a scheme not supported
+ # by requests, we should fail.
+ self.assertRaises(
+ ValueError,
+ get,
+ 'ftp://ftp.kernel.org/pub/')
+
+ def test_can_have_none_in_header_values(self):
+ try:
+ # Don't choke on headers with none in the value.
+ requests.get(httpbin('headers'), headers={'Foo': None})
+ except TypeError:
+ self.fail()
+
+ def test_danger_mode_redirects(self):
+ s = requests.session()
+ s.config['danger_mode'] = True
+ s.get(httpbin('redirect', '4'))
+
+
+ def test_empty_response(self):
+ r = requests.get(httpbin('status', '404'))
+ r.text
+
+ def test_no_content(self):
+ r = requests.get(httpbin('status', "0"), config={"safe_mode":True})
+ r.content
+ r.content
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_requests_async.py b/tests/test_requests_async.py
new file mode 100755
index 0000000..1d28261
--- /dev/null
+++ b/tests/test_requests_async.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Path hack.
+import sys, os
+sys.path.insert(0, os.path.abspath('..'))
+
+import sys
+import unittest
+
+import select
+has_poll = hasattr(select, "poll")
+
+from requests import async
+import envoy
+
+sys.path.append('.')
+from test_requests import httpbin, RequestsTestSuite, SERVICES
+
+
+class RequestsTestSuiteUsingAsyncApi(RequestsTestSuite):
+ """Requests async test cases."""
+
+ def patched(f):
+ """Automatically send request after creation."""
+
+ def wrapped(*args, **kwargs):
+
+ request = f(*args, **kwargs)
+ return async.map([request])[0]
+
+ return wrapped
+
+ # Patched requests.api functions.
+ global request
+ request = patched(async.request)
+
+ global delete, get, head, options, patch, post, put
+ delete = patched(async.delete)
+ get = patched(async.get)
+ head = patched(async.head)
+ options = patched(async.options)
+ patch = patched(async.patch)
+ post = patched(async.post)
+ put = patched(async.put)
+
+
+ def test_entry_points(self):
+
+ async.request
+
+ async.delete
+ async.get
+ async.head
+ async.options
+ async.patch
+ async.post
+ async.put
+
+ async.map
+ async.send
+
+ def test_select_poll(self):
+ """Test to make sure we don't overwrite the poll"""
+ self.assertEqual(hasattr(select, "poll"), has_poll)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_requests_ext.py b/tests/test_requests_ext.py
new file mode 100644
index 0000000..1e4d89b
--- /dev/null
+++ b/tests/test_requests_ext.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Path hack.
+import sys, os
+sys.path.insert(0, os.path.abspath('..'))
+
+import unittest
+
+import requests
+from requests.compat import is_py2, is_py3
+
+try:
+ import omnijson as json
+except ImportError:
+ import json
+
+
+class RequestsTestSuite(unittest.TestCase):
+ """Requests test cases."""
+
+ # It goes to eleven.
+ _multiprocess_can_split_ = True
+
+ def test_addition(self):
+ assert (1 + 1) == 2
+
+
+ def test_ssl_hostname_ok(self):
+ requests.get('https://github.com', verify=True)
+
+
+ def test_ssl_hostname_not_ok(self):
+ requests.get('https://kennethreitz.com', verify=False)
+
+ self.assertRaises(requests.exceptions.SSLError, requests.get, 'https://kennethreitz.com')
+
+
+ def test_ssl_hostname_session_not_ok(self):
+
+ s = requests.session()
+
+ self.assertRaises(requests.exceptions.SSLError, s.get, 'https://kennethreitz.com')
+
+ s.get('https://kennethreitz.com', verify=False)
+
+
+ def test_binary_post(self):
+ '''We need to be careful how we build the utf-8 string since
+ unicode literals are a syntax error in python3
+ '''
+
+ if is_py2:
+ # Blasphemy!
+ utf8_string = eval("u'Smörgås'.encode('utf-8')")
+ elif is_py3:
+ utf8_string = 'Smörgås'.encode('utf-8')
+ else:
+ raise EnvironmentError('Flesh out this test for your environment.')
+ requests.post('http://www.google.com/', data=utf8_string)
+
+
+
+ def test_unicode_error(self):
+ url = 'http://blip.fm/~1abvfu'
+ requests.get(url)
+
+
+ def test_chunked_head_redirect(self):
+ url = "http://t.co/NFrx0zLG"
+ r = requests.head(url, allow_redirects=True)
+ self.assertEqual(r.status_code, 200)
+
+ def test_unicode_redirect(self):
+ '''This url redirects to a location that has a nonstandard
+ character in it, that breaks requests in python2.7
+
+ After some research, the cause was identified as an unintended
+ sideeffect of overriding of str with unicode.
+
+ In the case that the redirected url is actually a malformed
+ "bytes" object, i.e. a string with character c where
+ ord(c) > 127,
+ then unicode(url) breaks.
+ '''
+ r = requests.get('http://www.marketwire.com/mw/release.' +
+ 'do?id=1628202&sourceType=3')
+ assert r.ok
+
+ def test_unicode_url_outright(self):
+ '''This url visits in my browser'''
+ r = requests.get('http://www.marketwire.com/press-release/' +
+ 'jp-morgan-behauptet-sich-der-spitze-euro' +
+ 'p%C3%A4ischer-anleihe-analysten-laut-umf' +
+ 'rageergebnissen-1628202.htm')
+ assert r.ok
+
+ def test_redirect_encoding(self):
+ '''This url redirects to
+ http://www.dealipedia.com/deal_view_investment.php?r=20012'''
+
+ r = requests.get('http://feedproxy.google.com/~r/Dealipedia' +
+ 'News/~3/BQtUJRJeZlo/deal_view_investment.' +
+ 'php')
+ assert r.ok
+
+
+
+
+if __name__ == '__main__':
+ unittest.main()
+
diff --git a/tests/test_requests_https.py b/tests/test_requests_https.py
new file mode 100755
index 0000000..c6ea8f3
--- /dev/null
+++ b/tests/test_requests_https.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys, os
+import json
+import unittest
+
+# Path hack.
+sys.path.insert(0, os.path.abspath('..'))
+import requests
+
+class HTTPSTest(unittest.TestCase):
+ """Smoke test for https functionality."""
+
+ smoke_url = "https://github.com"
+
+ def perform_smoke_test(self, verify=False):
+ result = requests.get(self.smoke_url, verify=verify)
+ self.assertEqual(result.status_code, 200)
+
+ def test_smoke(self):
+ """Smoke test without verification."""
+ self.perform_smoke_test(verify=False)
+
+ def test_smoke_verified(self):
+ """Smoke test with SSL verification."""
+ self.perform_smoke_test(verify=True)
+
+
+if __name__ == '__main__':
+ unittest.main()