Skip to content

gh-158270: json: make dumps() skip JSONEncoder - #158271

Closed
methane wants to merge 4 commits into
python:mainfrom
methane:codex/json-encode-fastpath
Closed

methane wants to merge 4 commits into
python:mainfrom
methane:codex/json-encode-fastpath

Conversation

@methane

@methane methane commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

@methane methane added performance Performance or resource usage stdlib Standard Library Python modules in the Lib/ directory labels Sep 27, 2026
@methane methane changed the title bpo-158270: json: make dump() and dumps() that skip JSONEncoder gh-158270: json: make dump() and dumps() that skip JSONEncoder Sep 27, 2026
@methane methane changed the title gh-158270: json: make dump() and dumps() that skip JSONEncoder gh-158270: json: make dump() and that skip JSONEncoder Sep 27, 2026
@methane methane changed the title gh-158270: json: make dump() and that skip JSONEncoder gh-158270: json: make dump() skip JSONEncoder Sep 27, 2026
@methane
methane force-pushed the codex/json-encode-fastpath branch from 4d781e8 to e425c49 Compare September 27, 2026 03:56
@eendebakpt

Copy link
Copy Markdown
Contributor

There is a behaviour change when users override the JSONEncoder.default. This is not very clean programming, but it is used (e.g. https://stackoverflow.com/a/18561055, accepted answer to "Making object JSON serializable with regular encoder"): JSONEncoder.default = _default) and JSONEncoder.default is a public attribute.

Reproducer:

import io
import json


class Foo:
    pass


_orig_default = json.JSONEncoder.default

def _patched_default(self, o):
    if isinstance(o, Foo):
        return "FOO"
    return _orig_default(self, o)

json.JSONEncoder.default = _patched_default

obj = {"a": Foo()}

print("dumps(obj)          ->", json.dumps(obj))
sio = io.StringIO()
json.dump(obj, sio, indent=2)
print("dump(obj, indent=2) ->", sio.getvalue().replace("\n", ""))
try:
    print("dumps(obj, indent=2)->", json.dumps(obj, indent=2).replace("\n", ""))
except TypeError as e:
    print("dumps(obj, indent=2)-> TypeError:", e)

main:

dumps(obj)          -> {"a": "FOO"}
dump(obj, indent=2) -> {  "a": "FOO"}
dumps(obj, indent=2)-> {  "a": "FOO"}

this PR:

dumps(obj)          -> {"a": "FOO"}
dump(obj, indent=2) -> {  "a": "FOO"}
dumps(obj, indent=2)-> TypeError: Object of type Foo is not JSON serializable

@hugovk hugovk changed the title gh-158270: json: make dump() skip JSONEncoder gh-158270: json: make dumps() skip JSONEncoder Sep 28, 2026
@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Sep 28, 2026
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.

@methane

methane commented Sep 28, 2026

Copy link
Copy Markdown
Member Author

I was not expecting monkey patching JSONEncoder to be used in practice rather than creating a subclass.
Given that this optimization is not particularly critical, I don't think it is worth implementing at the expense of compatibility.

@methane methane closed this Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting core review performance Performance or resource usage stdlib Standard Library Python modules in the Lib/ directory type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants