Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add json parameter #2258

Merged
merged 5 commits into from
Oct 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions requests/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def request(method, url, **kwargs):
: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, bytes, or file-like object to send in the body of the :class:`Request`.
:param json: (optional) json data 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.
Expand Down Expand Up @@ -77,15 +78,16 @@ def head(url, **kwargs):
return request('head', url, **kwargs)


def post(url, data=None, **kwargs):
def post(url, data=None, json=None, **kwargs):
"""Sends a POST request. Returns :class:`Response` object.

:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
:param json: (optional) json data to send in the body of the :class:`Request`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would get passed through with the kwargs, right, so I guess this is just so people know about the parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:param \*\*kwargs: Optional arguments that ``request`` takes.
"""

return request('post', url, data=data, **kwargs)
return request('post', url, data=data, json=json, **kwargs)


def put(url, data=None, **kwargs):
Expand Down
23 changes: 17 additions & 6 deletions requests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
CONTENT_CHUNK_SIZE = 10 * 1024
ITER_CHUNK_SIZE = 512

json_dumps = json.dumps


class RequestEncodingMixin(object):
@property
Expand Down Expand Up @@ -189,7 +191,8 @@ class Request(RequestHooksMixin):
:param url: URL to send.
:param headers: dictionary of headers to send.
:param files: dictionary of {filename: fileobject} files to multipart upload.
:param data: the body to attach the request. If a dictionary is provided, form-encoding will take place.
:param data: the body to attach to the request. If a dictionary is provided, form-encoding will take place.
:param json: json for the body to attach to the request (if data is not specified).
:param params: dictionary of URL parameters to append to the URL.
:param auth: Auth handler or (user, pass) tuple.
:param cookies: dictionary or CookieJar of cookies to attach to this request.
Expand All @@ -209,6 +212,7 @@ def __init__(self,
headers=None,
files=None,
data=None,
json=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm late to the party, was out - wouldn't this also change the meaning for anyone who creates a Request by hand, eg

params = {'date_created': '1-1-14'}
req = Request('GET', 'http://jsonip.com', {'User-Agent': 'foobar'}, None, {}, params)

where now suddenly params is JSON data?

params=None,
auth=None,
cookies=None,
Expand All @@ -230,6 +234,7 @@ def __init__(self,
self.headers = headers
self.files = files
self.data = data
self.json = json
self.params = params
self.auth = auth
self.cookies = cookies
Expand All @@ -246,6 +251,7 @@ def prepare(self):
headers=self.headers,
files=self.files,
data=self.data,
json=self.json,
params=self.params,
auth=self.auth,
cookies=self.cookies,
Expand Down Expand Up @@ -289,14 +295,15 @@ def __init__(self):
self.hooks = default_hooks()

def prepare(self, method=None, url=None, headers=None, files=None,
data=None, params=None, auth=None, cookies=None, hooks=None):
data=None, params=None, auth=None, cookies=None, hooks=None,
json=None):
"""Prepares the entire request with the given parameters."""

self.prepare_method(method)
self.prepare_url(url, params)
self.prepare_headers(headers)
self.prepare_cookies(cookies)
self.prepare_body(data, files)
self.prepare_body(data, files, json)
self.prepare_auth(auth, url)
# Note that prepare_auth must be last to enable authentication schemes
# such as OAuth to work on a fully prepared request.
Expand Down Expand Up @@ -397,7 +404,7 @@ def prepare_headers(self, headers):
else:
self.headers = CaseInsensitiveDict()

def prepare_body(self, data, files):
def prepare_body(self, data, files, json=None):
"""Prepares the given HTTP body data."""

# Check if file, fo, generator, iterator.
Expand All @@ -408,6 +415,10 @@ def prepare_body(self, data, files):
content_type = None
length = None

if json is not None:
content_type = 'application/json'
body = json_dumps(json)

is_stream = all([
hasattr(data, '__iter__'),
not isinstance(data, (basestring, list, tuple, dict))
Expand All @@ -433,7 +444,7 @@ def prepare_body(self, data, files):
if files:
(body, content_type) = self._encode_files(files, data)
else:
if data:
if data and json is None:
body = self._encode_params(data)
if isinstance(data, basestring) or hasattr(data, 'read'):
content_type = None
Expand All @@ -443,7 +454,7 @@ def prepare_body(self, data, files):
self.prepare_content_length(body)

# Add content-type if it wasn't explicitly provided.
if (content_type) and (not 'content-type' in self.headers):
if content_type and ('content-type' not in self.headers):
self.headers['Content-Type'] = content_type

self.body = body
Expand Down
10 changes: 8 additions & 2 deletions requests/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def prepare_request(self, request):
url=request.url,
files=request.files,
data=request.data,
json=request.json,
headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict),
params=merge_setting(request.params, self.params),
auth=merge_setting(auth, self.auth),
Expand All @@ -376,6 +377,7 @@ def prepare_request(self, request):
def request(self, method, url,
params=None,
data=None,
json=None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here.

headers=None,
cookies=None,
files=None,
Expand All @@ -396,6 +398,8 @@ def request(self, method, url,
string for the :class:`Request`.
:param data: (optional) Dictionary or bytes to send in the body of the
:class:`Request`.
:param json: (optional) json 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
Expand Down Expand Up @@ -426,6 +430,7 @@ def request(self, method, url,
headers = headers,
files = files,
data = data or {},
json = json,
params = params or {},
auth = auth,
cookies = cookies,
Expand Down Expand Up @@ -479,15 +484,16 @@ def head(self, url, **kwargs):
kwargs.setdefault('allow_redirects', False)
return self.request('HEAD', url, **kwargs)

def post(self, url, data=None, **kwargs):
def post(self, url, data=None, json=None, **kwargs):
"""Sends a POST request. Returns :class:`Response` object.

:param url: URL for the new :class:`Request` object.
:param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`.
:param json: (optional) json to send in the body of the :class:`Request`.
:param \*\*kwargs: Optional arguments that ``request`` takes.
"""

return self.request('POST', url, data=data, **kwargs)
return self.request('POST', url, data=data, json=json, **kwargs)

def put(self, url, data=None, **kwargs):
"""Sends a PUT request. Returns :class:`Response` object.
Expand Down
9 changes: 9 additions & 0 deletions test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,15 @@ def test_requests_history_is_saved(self):
assert item.history == total[0:i]
i=i+1

def test_json_param_post_content_type_works(self):
r = requests.post(
httpbin('post'),
json={'life': 42}
)
assert r.status_code == 200
assert 'application/json' in r.request.headers['Content-Type']
assert {'life': 42} == r.json()['json']


class TestContentEncodingDetection(unittest.TestCase):

Expand Down