[ENG-759] [ENG-1104] patch json.dumps to handle __wrapped__ objects (#4166)

* [ENG-1104] patch `json.dumps` to handle `__wrapped__` objects

Unwrap proxied objects if the default serializer doesn't work.

* pre-commit fixup

* Skip default fallback logic when `cls` is specified

`cls` will provide its own default serialization mechanism, passing a `cls`
Encoder class is now also a way to opt-out of our patching shenanigans and just
use your own code.

This will work, provided the library doing the JSON encoding isn't also using
their own custom class.

* Override JSONEncoder.default instead of json.dumps

Many libraries (like httpx, used by openai), will use `from json import dumps`,
and if they do that before `reflex.state` gets imported, then they will get the
original dumps function instead of our patched one.

To workaround this, monkeypatch the `JSONEncoder.default` function instead.

This is also nicer behavior for custom subclasses of JSONEncoder; if someone
wants to opt-out of our monkeypatching, they can simply not call
`super().default(o)` in their subclass, which by default only raises a
TypeError.

---------

Co-authored-by: Nikhil Rao <nikhil@reflex.dev>
This commit is contained in:
Masen Furer 2024-11-04 10:11:51 -08:00 committed by GitHub
parent 0d9fc53a7d
commit 163acf70a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -8,6 +8,7 @@ import copy
import dataclasses import dataclasses
import functools import functools
import inspect import inspect
import json
import pickle import pickle
import sys import sys
import uuid import uuid
@ -3713,6 +3714,29 @@ def serialize_mutable_proxy(mp: MutableProxy):
return mp.__wrapped__ return mp.__wrapped__
_orig_json_JSONEncoder_default = json.JSONEncoder.default
def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
"""Wrap JSONEncoder.default to handle MutableProxy objects.
Args:
self: the JSONEncoder instance.
o: the object to serialize.
Returns:
A JSON-able object.
"""
try:
return o.__wrapped__
except AttributeError:
pass
return _orig_json_JSONEncoder_default(self, o)
json.JSONEncoder.default = _json_JSONEncoder_default_wrapper
class ImmutableMutableProxy(MutableProxy): class ImmutableMutableProxy(MutableProxy):
"""A proxy for a mutable object that tracks changes. """A proxy for a mutable object that tracks changes.