| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2021-06-19 19:57:56 +08:00
										 |  |  | import decimal | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | import io | 
					
						
							|  |  |  | import uuid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import pytest | 
					
						
							|  |  |  | from werkzeug.http import http_date | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import flask | 
					
						
							| 
									
										
										
										
											2020-11-01 11:17:04 +08:00
										 |  |  | from flask import json | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  | from flask.json.provider import DefaultJSONProvider | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize("debug", (True, False)) | 
					
						
							|  |  |  | def test_bad_request_debug_message(app, client, debug): | 
					
						
							|  |  |  |     app.config["DEBUG"] = debug | 
					
						
							|  |  |  |     app.config["TRAP_BAD_REQUEST_ERRORS"] = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/json", methods=["POST"]) | 
					
						
							|  |  |  |     def post_json(): | 
					
						
							|  |  |  |         flask.request.get_json() | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.post("/json", data=None, content_type="application/json") | 
					
						
							|  |  |  |     assert rv.status_code == 400 | 
					
						
							|  |  |  |     contains = b"Failed to decode JSON object" in rv.data | 
					
						
							|  |  |  |     assert contains == debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_json_bad_requests(app, client): | 
					
						
							|  |  |  |     @app.route("/json", methods=["POST"]) | 
					
						
							|  |  |  |     def return_json(): | 
					
						
							|  |  |  |         return flask.jsonify(foo=str(flask.request.get_json())) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.post("/json", data="malformed", content_type="application/json") | 
					
						
							|  |  |  |     assert rv.status_code == 400 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_json_custom_mimetypes(app, client): | 
					
						
							|  |  |  |     @app.route("/json", methods=["POST"]) | 
					
						
							|  |  |  |     def return_json(): | 
					
						
							|  |  |  |         return flask.request.get_json() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.post("/json", data='"foo"', content_type="application/x+json") | 
					
						
							|  |  |  |     assert rv.data == b"foo" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     "test_value,expected", [(True, '"\\u2603"'), (False, '"\u2603"')] | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | def test_json_as_unicode(test_value, expected, app, app_ctx): | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     app.json.ensure_ascii = test_value | 
					
						
							|  |  |  |     rv = app.json.dumps("\N{SNOWMAN}") | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |     assert rv == expected | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_json_dump_to_file(app, app_ctx): | 
					
						
							|  |  |  |     test_data = {"name": "Flask"} | 
					
						
							|  |  |  |     out = io.StringIO() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     flask.json.dump(test_data, out) | 
					
						
							|  |  |  |     out.seek(0) | 
					
						
							|  |  |  |     rv = flask.json.load(out) | 
					
						
							|  |  |  |     assert rv == test_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     "test_value", [0, -1, 1, 23, 3.14, "s", "longer string", True, False, None] | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | def test_jsonify_basic_types(test_value, app, client): | 
					
						
							|  |  |  |     url = "/jsonify_basic_types" | 
					
						
							|  |  |  |     app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x)) | 
					
						
							|  |  |  |     rv = client.get(url) | 
					
						
							|  |  |  |     assert rv.mimetype == "application/json" | 
					
						
							|  |  |  |     assert flask.json.loads(rv.data) == test_value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_jsonify_dicts(app, client): | 
					
						
							|  |  |  |     d = { | 
					
						
							|  |  |  |         "a": 0, | 
					
						
							|  |  |  |         "b": 23, | 
					
						
							|  |  |  |         "c": 3.14, | 
					
						
							|  |  |  |         "d": "t", | 
					
						
							|  |  |  |         "e": "Hi", | 
					
						
							|  |  |  |         "f": True, | 
					
						
							|  |  |  |         "g": False, | 
					
						
							|  |  |  |         "h": ["test list", 10, False], | 
					
						
							|  |  |  |         "i": {"test": "dict"}, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/kw") | 
					
						
							|  |  |  |     def return_kwargs(): | 
					
						
							|  |  |  |         return flask.jsonify(**d) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/dict") | 
					
						
							|  |  |  |     def return_dict(): | 
					
						
							|  |  |  |         return flask.jsonify(d) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for url in "/kw", "/dict": | 
					
						
							|  |  |  |         rv = client.get(url) | 
					
						
							|  |  |  |         assert rv.mimetype == "application/json" | 
					
						
							|  |  |  |         assert flask.json.loads(rv.data) == d | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_jsonify_arrays(app, client): | 
					
						
							|  |  |  |     """Test jsonify of lists and args unpacking.""" | 
					
						
							|  |  |  |     a_list = [ | 
					
						
							|  |  |  |         0, | 
					
						
							|  |  |  |         42, | 
					
						
							|  |  |  |         3.14, | 
					
						
							|  |  |  |         "t", | 
					
						
							|  |  |  |         "hello", | 
					
						
							|  |  |  |         True, | 
					
						
							|  |  |  |         False, | 
					
						
							|  |  |  |         ["test list", 2, False], | 
					
						
							|  |  |  |         {"test": "dict"}, | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/args_unpack") | 
					
						
							|  |  |  |     def return_args_unpack(): | 
					
						
							|  |  |  |         return flask.jsonify(*a_list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/array") | 
					
						
							|  |  |  |     def return_array(): | 
					
						
							|  |  |  |         return flask.jsonify(a_list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for url in "/args_unpack", "/array": | 
					
						
							|  |  |  |         rv = client.get(url) | 
					
						
							|  |  |  |         assert rv.mimetype == "application/json" | 
					
						
							|  |  |  |         assert flask.json.loads(rv.data) == a_list | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-26 01:42:44 +08:00
										 |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     "value", [datetime.datetime(1973, 3, 11, 6, 30, 45), datetime.date(1975, 1, 5)] | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | def test_jsonify_datetime(app, client, value): | 
					
						
							|  |  |  |     @app.route("/") | 
					
						
							|  |  |  |     def index(): | 
					
						
							|  |  |  |         return flask.jsonify(value=value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     r = client.get() | 
					
						
							|  |  |  |     assert r.json["value"] == http_date(value) | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FixedOffset(datetime.tzinfo): | 
					
						
							|  |  |  |     """Fixed offset in hours east from UTC.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This is a slight adaptation of the ``FixedOffset`` example found in | 
					
						
							|  |  |  |     https://docs.python.org/2.7/library/datetime.html. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, hours, name): | 
					
						
							|  |  |  |         self.__offset = datetime.timedelta(hours=hours) | 
					
						
							|  |  |  |         self.__name = name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def utcoffset(self, dt): | 
					
						
							|  |  |  |         return self.__offset | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def tzname(self, dt): | 
					
						
							|  |  |  |         return self.__name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def dst(self, dt): | 
					
						
							|  |  |  |         return datetime.timedelta() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize("tz", (("UTC", 0), ("PST", -8), ("KST", 9))) | 
					
						
							|  |  |  | def test_jsonify_aware_datetimes(tz): | 
					
						
							|  |  |  |     """Test if aware datetime.datetime objects are converted into GMT.""" | 
					
						
							|  |  |  |     tzinfo = FixedOffset(hours=tz[1], name=tz[0]) | 
					
						
							|  |  |  |     dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) | 
					
						
							|  |  |  |     gmt = FixedOffset(hours=0, name="GMT") | 
					
						
							|  |  |  |     expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     assert flask.json.dumps(dt) == expected | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_jsonify_uuid_types(app, client): | 
					
						
							|  |  |  |     """Test jsonify with uuid.UUID types""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 01:24:40 +08:00
										 |  |  |     test_uuid = uuid.UUID(bytes=b"\xde\xad\xbe\xef" * 4) | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |     url = "/uuid_test" | 
					
						
							|  |  |  |     app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.get(url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv_x = flask.json.loads(rv.data)["x"] | 
					
						
							|  |  |  |     assert rv_x == str(test_uuid) | 
					
						
							|  |  |  |     rv_uuid = uuid.UUID(rv_x) | 
					
						
							|  |  |  |     assert rv_uuid == test_uuid | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-19 19:57:56 +08:00
										 |  |  | def test_json_decimal(): | 
					
						
							|  |  |  |     rv = flask.json.dumps(decimal.Decimal("0.003")) | 
					
						
							|  |  |  |     assert rv == '"0.003"' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | def test_json_attr(app, client): | 
					
						
							|  |  |  |     @app.route("/add", methods=["POST"]) | 
					
						
							|  |  |  |     def add(): | 
					
						
							|  |  |  |         json = flask.request.get_json() | 
					
						
							|  |  |  |         return str(json["a"] + json["b"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.post( | 
					
						
							|  |  |  |         "/add", | 
					
						
							|  |  |  |         data=flask.json.dumps({"a": 1, "b": 2}), | 
					
						
							|  |  |  |         content_type="application/json", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert rv.data == b"3" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 14:41:49 +08:00
										 |  |  | def test_tojson_filter(app, req_ctx): | 
					
						
							|  |  |  |     # The tojson filter is tested in Jinja, this confirms that it's | 
					
						
							|  |  |  |     # using Flask's dumps. | 
					
						
							|  |  |  |     rv = flask.render_template_string( | 
					
						
							|  |  |  |         "const data = {{ data|tojson }};", | 
					
						
							|  |  |  |         data={"name": "</script>", "time": datetime.datetime(2021, 2, 1, 7, 15)}, | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert rv == ( | 
					
						
							|  |  |  |         'const data = {"name": "\\u003c/script\\u003e",' | 
					
						
							|  |  |  |         ' "time": "Mon, 01 Feb 2021 07:15:00 GMT"};' | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_json_customization(app, client): | 
					
						
							|  |  |  |     class X:  # noqa: B903, for Python2 compatibility | 
					
						
							|  |  |  |         def __init__(self, val): | 
					
						
							|  |  |  |             self.val = val | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     def default(o): | 
					
						
							|  |  |  |         if isinstance(o, X): | 
					
						
							|  |  |  |             return f"<{o.val}>" | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |         return DefaultJSONProvider.default(o) | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     class CustomProvider(DefaultJSONProvider): | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |         def object_hook(self, obj): | 
					
						
							|  |  |  |             if len(obj) == 1 and "_foo" in obj: | 
					
						
							|  |  |  |                 return X(obj["_foo"]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |             return obj | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |         def loads(self, s, **kwargs): | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |             kwargs.setdefault("object_hook", self.object_hook) | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |             return super().loads(s, **kwargs) | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     app.json = CustomProvider(app) | 
					
						
							|  |  |  |     app.json.default = default | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     @app.route("/", methods=["POST"]) | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |     def index(): | 
					
						
							|  |  |  |         return flask.json.dumps(flask.request.get_json()["x"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.post( | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |         "/", | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |         data=flask.json.dumps({"x": {"_foo": 42}}), | 
					
						
							|  |  |  |         content_type="application/json", | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     assert rv.data == b'"<42>"' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _has_encoding(name): | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         import codecs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         codecs.lookup(name) | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     except LookupError: | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_json_key_sorting(app, client): | 
					
						
							|  |  |  |     app.debug = True | 
					
						
							| 
									
										
										
										
											2022-07-13 22:41:43 +08:00
										 |  |  |     assert app.json.sort_keys | 
					
						
							| 
									
										
										
										
											2020-11-01 11:13:03 +08:00
										 |  |  |     d = dict.fromkeys(range(20), "foo") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @app.route("/") | 
					
						
							|  |  |  |     def index(): | 
					
						
							|  |  |  |         return flask.jsonify(values=d) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rv = client.get("/") | 
					
						
							|  |  |  |     lines = [x.strip() for x in rv.data.strip().decode("utf-8").splitlines()] | 
					
						
							|  |  |  |     sorted_by_str = [ | 
					
						
							|  |  |  |         "{", | 
					
						
							|  |  |  |         '"values": {', | 
					
						
							|  |  |  |         '"0": "foo",', | 
					
						
							|  |  |  |         '"1": "foo",', | 
					
						
							|  |  |  |         '"10": "foo",', | 
					
						
							|  |  |  |         '"11": "foo",', | 
					
						
							|  |  |  |         '"12": "foo",', | 
					
						
							|  |  |  |         '"13": "foo",', | 
					
						
							|  |  |  |         '"14": "foo",', | 
					
						
							|  |  |  |         '"15": "foo",', | 
					
						
							|  |  |  |         '"16": "foo",', | 
					
						
							|  |  |  |         '"17": "foo",', | 
					
						
							|  |  |  |         '"18": "foo",', | 
					
						
							|  |  |  |         '"19": "foo",', | 
					
						
							|  |  |  |         '"2": "foo",', | 
					
						
							|  |  |  |         '"3": "foo",', | 
					
						
							|  |  |  |         '"4": "foo",', | 
					
						
							|  |  |  |         '"5": "foo",', | 
					
						
							|  |  |  |         '"6": "foo",', | 
					
						
							|  |  |  |         '"7": "foo",', | 
					
						
							|  |  |  |         '"8": "foo",', | 
					
						
							|  |  |  |         '"9": "foo"', | 
					
						
							|  |  |  |         "}", | 
					
						
							|  |  |  |         "}", | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |     sorted_by_int = [ | 
					
						
							|  |  |  |         "{", | 
					
						
							|  |  |  |         '"values": {', | 
					
						
							|  |  |  |         '"0": "foo",', | 
					
						
							|  |  |  |         '"1": "foo",', | 
					
						
							|  |  |  |         '"2": "foo",', | 
					
						
							|  |  |  |         '"3": "foo",', | 
					
						
							|  |  |  |         '"4": "foo",', | 
					
						
							|  |  |  |         '"5": "foo",', | 
					
						
							|  |  |  |         '"6": "foo",', | 
					
						
							|  |  |  |         '"7": "foo",', | 
					
						
							|  |  |  |         '"8": "foo",', | 
					
						
							|  |  |  |         '"9": "foo",', | 
					
						
							|  |  |  |         '"10": "foo",', | 
					
						
							|  |  |  |         '"11": "foo",', | 
					
						
							|  |  |  |         '"12": "foo",', | 
					
						
							|  |  |  |         '"13": "foo",', | 
					
						
							|  |  |  |         '"14": "foo",', | 
					
						
							|  |  |  |         '"15": "foo",', | 
					
						
							|  |  |  |         '"16": "foo",', | 
					
						
							|  |  |  |         '"17": "foo",', | 
					
						
							|  |  |  |         '"18": "foo",', | 
					
						
							|  |  |  |         '"19": "foo"', | 
					
						
							|  |  |  |         "}", | 
					
						
							|  |  |  |         "}", | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         assert lines == sorted_by_int | 
					
						
							|  |  |  |     except AssertionError: | 
					
						
							|  |  |  |         assert lines == sorted_by_str | 
					
						
							| 
									
										
										
										
											2020-11-01 11:17:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def test_html_method(): | 
					
						
							|  |  |  |     class ObjectWithHTML: | 
					
						
							|  |  |  |         def __html__(self): | 
					
						
							|  |  |  |             return "<p>test</p>" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     result = json.dumps(ObjectWithHTML()) | 
					
						
							|  |  |  |     assert result == '"<p>test</p>"' |