Skip to content
Closed
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
9 changes: 8 additions & 1 deletion Lib/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
__author__ = 'Bob Ippolito <bob@redivi.com>'

from .decoder import JSONDecoder, JSONDecodeError
from .encoder import JSONEncoder
from .encoder import JSONEncoder, _iterencode_oneshot
import codecs

_default_encoder = JSONEncoder(
Expand Down Expand Up @@ -233,6 +233,13 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
default is None and not sort_keys and not kw):
return _default_encoder.encode(obj)
if cls is None:
if not kw:
it = _iterencode_oneshot(
obj, skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan,
indent=indent, separators=separators, default=default,
sort_keys=sort_keys)
return ''.join(it)
cls = JSONEncoder
return cls(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
Expand Down
36 changes: 36 additions & 0 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,3 +456,39 @@ def _iterencode_once(o, _current_indent_level):
del _iterencode, _iterencode_dict, _iterencode_list

return _iterencode_once


# Same error as JSONEncoder.default(), without creating an encoder instance.
def _default(o):
raise TypeError(f'Object of type {o.__class__.__name__} '
f'is not JSON serializable')


# Shortcut for JSONEncoder().iterencode(o, _one_shot=True).
def _iterencode_oneshot(obj, *, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, indent=None,
separators=None, default=None, sort_keys=False):
if c_make_encoder is None:
return JSONEncoder(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan,
sort_keys=sort_keys, indent=indent, separators=separators,
default=default).iterencode(obj, _one_shot=True)

item_separator = ', '
key_separator = ': '
if separators is not None:
item_separator, key_separator = separators
elif indent is not None:
item_separator = ','

if indent is not None and not isinstance(indent, str):
indent = ' ' * indent

_iterencode = c_make_encoder(
{} if check_circular else None,
_default if default is None else default,
encode_basestring_ascii if ensure_ascii else encode_basestring,
indent, key_separator, item_separator,
sort_keys, skipkeys, allow_nan)
return _iterencode(obj, 0)
11 changes: 11 additions & 0 deletions Lib/test/test_json/test_default.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ def test_default(self):
self.dumps(type, default=repr),
self.dumps(repr(type)))

def test_falsey_callable_default(self):
class Default:
def __bool__(self):
return False

def __call__(self, obj):
return str(obj)

self.assertEqual(self.dumps(type, default=Default()),
self.dumps(str(type)))

def test_bad_default(self):
def default(obj):
if obj is NotImplemented:
Expand Down
15 changes: 15 additions & 0 deletions Lib/test/test_json/test_dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ def test_dump(self):
self.json.dump({}, sio)
self.assertEqual(sio.getvalue(), '{}')

def test_dump_options(self):
class Default:
def __bool__(self):
return False

def __call__(self, obj):
return 'custom'

sio = StringIO()
self.json.dump({'é': object(), 'a': [1, 2]}, sio,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This tests dump() but only dumps() is now changed.

ensure_ascii=False, indent=2, sort_keys=True,
default=Default())
self.assertEqual(sio.getvalue(),
'{\n "a": [\n 1,\n 2\n ],\n "é": "custom"\n}')

def test_dumps(self):
self.assertEqual(self.dumps({}), '{}')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
When possible, make :func:`json.dumps` bypass
:class:`json.JSONEncoder` and call the internal encoding logic directly.
This reduces the time required to encode small JSON objects by about 50%
with non-default options.
Loading