diff --git a/MANIFEST.in b/MANIFEST.in index 75b3f3d6..09ba9bef 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include README.rst LICENSE NOTICE HISTORY.rst tests/*. requests/cacert.pem +include README.rst LICENSE NOTICE HISTORY.rst tests/* requests/cacert.pem diff --git a/docs/api.rst b/docs/api.rst index 499ffd3c..79145581 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -38,6 +38,9 @@ They all return an instance of the :class:`Response ` object. .. autofunction:: session +.. autoclass:: Session + :inherited-members: + Exceptions diff --git a/requests/models.py b/requests/models.py index f3d7d768..58e7f9a7 100644 --- a/requests/models.py +++ b/requests/models.py @@ -35,7 +35,7 @@ from .utils import ( guess_json_utf) from .compat import ( cookielib, urlparse, urlunparse, urljoin, urlsplit, urlencode, str, bytes, - StringIO, is_py2, chardet, json, builtin_str, urldefrag) + StringIO, is_py2, chardet, json, builtin_str, urldefrag, basestring) REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved) CONTENT_CHUNK_SIZE = 10 * 1024 @@ -97,7 +97,7 @@ class Request(object): #: :class:`Request `. self.data = None - #: Dictionary or byte of querystring data to attach to the + #: Dictionary of querystring data to attach to the #: :class:`Request `. The dictionary values can be lists for representing #: multivalued query parameters. self.params = None @@ -334,7 +334,9 @@ class Request(object): elif hasattr(data, '__iter__'): result = [] for k, vs in to_key_val_list(data): - for v in isinstance(vs, list) and vs or [vs]: + if isinstance(vs, basestring) or not hasattr(vs, '__iter__'): + vs = [vs] + for v in vs: if v is not None: result.append( (k.encode('utf-8') if isinstance(k, str) else k, diff --git a/requests/sessions.py b/requests/sessions.py index f0d4f3c7..0962d819 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -73,15 +73,42 @@ class Session(object): verify=True, cert=None): + #: A case-insensitive dictionary of headers to be sent on each + #: :class:`Request ` sent from this + #: :class:`Session `. self.headers = from_key_val_list(headers or []) + + #: Authentication tuple or object to attach to + #: :class:`Request `. self.auth = auth + + #: Float describing the timeout of the each :class:`Request `. self.timeout = timeout + + #: Dictionary mapping protocol to the URL of the proxy (e.g. + #: {'http': 'foo.bar:3128'}) to be used on each + #: :class:`Request `. self.proxies = from_key_val_list(proxies or []) + + #: Event-handling hooks. self.hooks = from_key_val_list(hooks or {}) + + #: Dictionary of querystring data to attach to each + #: :class:`Request `. The dictionary values may be lists for + #: representing multivalued query parameters. self.params = from_key_val_list(params or []) + + #: Dictionary of configuration parameters for this + #: :class:`Session `. self.config = from_key_val_list(config or {}) + + #: Prefetch response content. self.prefetch = prefetch + + #: SSL Verification. self.verify = verify + + #: SSL certificate. self.cert = cert for (k, v) in list(defaults.items()): diff --git a/requests/status_codes.py b/requests/status_codes.py index e25ecdb9..08edab4e 100644 --- a/requests/status_codes.py +++ b/requests/status_codes.py @@ -10,7 +10,7 @@ _codes = { 102: ('processing',), 103: ('checkpoint',), 122: ('uri_too_long', 'request_uri_too_long'), - 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/'), + 200: ('ok', 'okay', 'all_ok', 'all_okay', 'all_good', '\\o/', '✓'), 201: ('created',), 202: ('accepted',), 203: ('non_authoritative_info', 'non_authoritative_information'), @@ -65,7 +65,7 @@ _codes = { 499: ('client_closed_request',), # Server Error. - 500: ('internal_server_error', 'server_error', '/o\\'), + 500: ('internal_server_error', 'server_error', '/o\\', '✗'), 501: ('not_implemented',), 502: ('bad_gateway',), 503: ('service_unavailable', 'unavailable'), diff --git a/requests/utils.py b/requests/utils.py index ec9f4d2c..b3d33f4f 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -539,11 +539,18 @@ def default_user_agent(): _implementation_version = platform.python_version() # Complete Guess else: _implementation_version = 'Unknown' + + try: + p_system = platform.system() + p_release = platform.release() + except IOError: + p_system = 'Unknown' + p_release = 'Unknown' return " ".join([ 'python-requests/%s' % __version__, '%s/%s' % (_implementation, _implementation_version), - '%s/%s' % (platform.system(), platform.release()), + '%s/%s' % (p_system, p_release), ]) diff --git a/setup.py b/setup.py index 9844ab1b..4324fc33 100755 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ setup( include_package_data=True, install_requires=requires, license=open('LICENSE').read(), + zip_safe=False, classifiers=( 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', diff --git a/tests/test_requests.py b/tests/test_requests.py index 80709d5a..6615678f 100755 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -647,6 +647,30 @@ class RequestsTestSuite(TestSetup, TestBaseMixin, unittest.TestCase): self.assertEqual(rbody.get('form'), dict(test2='foobar', test3=['foo', 'baz'])) self.assertEqual(rbody.get('data'), '') + def test_multivalued_data_encoding(self): + """ + Make sure data encoding works on a value that is an iterable but not + a list + """ + for service in SERVICES: + # Can't have unicode literals in Python3, so avoid them. + # TODO: fixup when moving to Python 3.3 + if (sys.version_info[0] == 2): + nonascii = '\xc3\xa9'.decode('utf-8') + else: + nonascii = '\xe9' + + r = post( + service('post'), + data=dict(test=('foo', nonascii))) + + self.assertEqual(r.status_code, 200) + self.assertEqual(r.headers['content-type'], 'application/json') + + rbody = json.loads(r.text) + self.assertEqual(rbody.get('form'), + dict(test=['foo', nonascii])) + def test_GET_no_redirect(self): for service in SERVICES: