@@ -747,6 +747,9 @@ def orjson_loads(data: str | bytes) -> Any:
747747
748748
749749def _replace_non_finite_floats (obj : Any ) -> Any :
750+ # Copy-on-write: only allocate a new container if a child actually
751+ # changed. ``is`` works as a "did we replace this" check because
752+ # unchanged values are returned as the same object.
750753 if isinstance (obj , float ):
751754 if obj != obj :
752755 return NAN_SENTINEL
@@ -756,9 +759,23 @@ def _replace_non_finite_floats(obj: Any) -> Any:
756759 return NEG_INF_SENTINEL
757760 return obj
758761 if isinstance (obj , dict ):
759- return {k : _replace_non_finite_floats (v ) for k , v in obj .items ()}
762+ new = None
763+ for k , v in obj .items ():
764+ v2 = _replace_non_finite_floats (v )
765+ if v2 is not v :
766+ if new is None :
767+ new = dict (obj )
768+ new [k ] = v2
769+ return new if new is not None else obj
760770 if isinstance (obj , (list , tuple )):
761- return [_replace_non_finite_floats (v ) for v in obj ]
771+ new = None
772+ for i , v in enumerate (obj ):
773+ v2 = _replace_non_finite_floats (v )
774+ if v2 is not v :
775+ if new is None :
776+ new = list (obj )
777+ new [i ] = v2
778+ return new if new is not None else obj
762779 return obj
763780
764781
@@ -799,6 +816,10 @@ def _default(o: Any) -> Any:
799816 orjson .OPT_NON_STR_KEYS
800817 | orjson .OPT_PASSTHROUGH_SUBCLASS
801818 | orjson .OPT_PASSTHROUGH_DATACLASS
819+ # Route datetimes through default= so they get the same
820+ # space-separated format that ``serializers.serialize_datetime``
821+ # produces; orjson's native output uses an ISO 'T' separator.
822+ | orjson .OPT_PASSTHROUGH_DATETIME
802823 ),
803824 ).decode ()
804825 except TypeError :
0 commit comments