From 4f1d00442c88e487756cb5e3dc642312b177166c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 17 Mar 2022 15:30:22 +0530 Subject: [PATCH 1/2] fix(as_json): Pop None key if exists JSON doesn't allow null key as per spec, it should be a string only: https://datatracker.ietf.org/doc/html/rfc7159#section-4 ref discussions: * https://github.com/frappe/frappe/issues/14292 * https://github.com/frappe/frappe/pull/14504/files#r821526085 --- frappe/__init__.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 86f8be35ea..60189a2565 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE """ Frappe - Low Code Open Source Framework in Python and JS @@ -20,10 +20,10 @@ if _dev_server: warnings.simplefilter('always', DeprecationWarning) warnings.simplefilter('always', PendingDeprecationWarning) -from werkzeug.local import Local, release_local import sys, importlib, inspect, json -import typing import click +from werkzeug.local import Local, release_local +from typing import TYPE_CHECKING, Dict, List, Union # Local application imports from .exceptions import * @@ -143,15 +143,14 @@ lang = local("lang") # This if block is never executed when running the code. It is only used for # telling static code analyzer where to find dynamically defined attributes. -if typing.TYPE_CHECKING: - from frappe.utils.redis_wrapper import RedisWrapper - +if TYPE_CHECKING: from frappe.database.mariadb.database import MariaDBDatabase from frappe.database.postgres.database import PostgresDatabase from frappe.query_builder.builder import MariaDB, Postgres + from frappe.utils.redis_wrapper import RedisWrapper - db: typing.Union[MariaDBDatabase, PostgresDatabase] - qb: typing.Union[MariaDB, Postgres] + db: Union[MariaDBDatabase, PostgresDatabase] + qb: Union[MariaDB, Postgres] # end: static analysis hack @@ -1522,12 +1521,14 @@ def get_value(*args, **kwargs): """ return db.get_value(*args, **kwargs) -def as_json(obj, indent=1): +def as_json(obj: Union[Dict, List], indent=1) -> str: from frappe.utils.response import json_handler - try: - return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': ')) - except TypeError: - return json.dumps(obj, indent=indent, default=json_handler, separators=(',', ': ')) + + if isinstance(obj, dict) and None in obj: + obj.pop(None) + + return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': ')) + def are_emails_muted(): from frappe.utils import cint From 1c8d2fd5369f76700130fd4c33d31bcaed0f779c Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 21 Mar 2022 17:49:26 +0530 Subject: [PATCH 2/2] fix: Sort keys for illegal JSON --- frappe/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 60189a2565..df8e1fbfb1 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1524,11 +1524,13 @@ def get_value(*args, **kwargs): def as_json(obj: Union[Dict, List], indent=1) -> str: from frappe.utils.response import json_handler - if isinstance(obj, dict) and None in obj: - obj.pop(None) - - return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': ')) - + try: + return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': ')) + except TypeError: + # this would break in case the keys are not all os "str" type - as defined in the JSON + # adding this to ensure keys are sorted (expected behaviour) + sorted_obj = dict(sorted(obj.items(), key=lambda kv: str(kv[0]))) + return json.dumps(sorted_obj, indent=indent, default=json_handler, separators=(',', ': ')) def are_emails_muted(): from frappe.utils import cint