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

Throw value error when serializing JSON object with NaN value #5810

Merged
merged 5 commits into from
May 6, 2021
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
4 changes: 4 additions & 0 deletions requests/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ def __init__(self, *args, **kwargs):
super(RequestException, self).__init__(*args, **kwargs)


class InvalidJSONError(RequestException):
"""A JSON error occurred."""


class HTTPError(RequestException):
"""An HTTP error occurred."""

Expand Down
9 changes: 7 additions & 2 deletions requests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from .cookies import cookiejar_from_dict, get_cookie_header, _copy_cookie_jar
from .exceptions import (
HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError,
ContentDecodingError, ConnectionError, StreamConsumedError)
ContentDecodingError, ConnectionError, StreamConsumedError, InvalidJSONError)
from ._internal_utils import to_native_string, unicode_is_ascii
from .utils import (
guess_filename, get_auth_from_url, requote_uri,
Expand Down Expand Up @@ -466,7 +466,12 @@ def prepare_body(self, data, files, json=None):
# urllib3 requires a bytes-like body. Python 2's json.dumps
# provides this natively, but Python 3 gives a Unicode string.
content_type = 'application/json'
body = complexjson.dumps(json)

try:
body = complexjson.dumps(json, allow_nan=False)
except ValueError as ve:
raise InvalidJSONError(ve, request=self)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sigmavirus24 is this okay? Couldn't do it the exact way you specified, but request contains the JSON info.

Copy link
Contributor

Choose a reason for hiding this comment

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

I have an instinct of "No" but that's because people do not read the docs and sometimes pass open files and such in addition to the json= keyword and keeping a reference to those might not be the best but that's a "The user has shot themselves in the foot" situation only for high traffic (lots of requests doing the wrong thing explicitly that has undefined behaviour), poorly designed software (because it's potentially not releasing/decref'ing the exception which doesn't decref the request which doesn't decref the file obj).

tl;dr - This should be fine


if not isinstance(body, bytes):
body = body.encode('utf-8')

Expand Down
7 changes: 6 additions & 1 deletion tests/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from requests.exceptions import (
ConnectionError, ConnectTimeout, InvalidSchema, InvalidURL,
MissingSchema, ReadTimeout, Timeout, RetryError, TooManyRedirects,
ProxyError, InvalidHeader, UnrewindableBodyError, SSLError, InvalidProxyURL)
ProxyError, InvalidHeader, UnrewindableBodyError, SSLError, InvalidProxyURL, InvalidJSONError)
from requests.models import PreparedRequest
from requests.structures import CaseInsensitiveDict
from requests.sessions import SessionRedirectMixin
Expand Down Expand Up @@ -2566,3 +2566,8 @@ def test_parameters_for_nonstandard_schemes(self, input, params, expected):
r = requests.Request('GET', url=input, params=params)
p = r.prepare()
assert p.url == expected

def test_post_json_nan(self, httpbin):
data = {"foo": float("nan")}
with pytest.raises(requests.exceptions.InvalidJSONError):
r = requests.post(httpbin('post'), json=data)