瀏覽代碼

serialize uuid and allow overwriting serializers (#4888)

* serialize uuid and allow overwriting serializers

* add frame info

* reword place info
Khaleel Al-Adhami 2 月之前
父節點
當前提交
33714aa4a3
共有 1 個文件被更改,包括 38 次插入4 次删除
  1. 38 4
      reflex/utils/serializers.py

+ 38 - 4
reflex/utils/serializers.py

@@ -5,6 +5,7 @@ from __future__ import annotations
 import contextlib
 import dataclasses
 import functools
+import inspect
 import json
 import warnings
 from datetime import date, datetime, time, timedelta
@@ -21,13 +22,14 @@ from typing import (
     get_type_hints,
     overload,
 )
+from uuid import UUID
 
 from pydantic import BaseModel as BaseModelV2
 from pydantic.v1 import BaseModel as BaseModelV1
 
 from reflex.base import Base
 from reflex.constants.colors import Color, format_color
-from reflex.utils import types
+from reflex.utils import console, types
 
 # Mapping from type to a serializer.
 # The serializer should convert the type to a JSON object.
@@ -47,6 +49,7 @@ SERIALIZED_FUNCTION = TypeVar("SERIALIZED_FUNCTION", bound=Serializer)
 def serializer(
     fn: None = None,
     to: Type[SerializedType] | None = None,
+    overwrite: bool | None = None,
 ) -> Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]: ...
 
 
@@ -54,18 +57,21 @@ def serializer(
 def serializer(
     fn: SERIALIZED_FUNCTION,
     to: Type[SerializedType] | None = None,
+    overwrite: bool | None = None,
 ) -> SERIALIZED_FUNCTION: ...
 
 
 def serializer(
     fn: SERIALIZED_FUNCTION | None = None,
     to: Any = None,
+    overwrite: bool | None = None,
 ) -> SERIALIZED_FUNCTION | Callable[[SERIALIZED_FUNCTION], SERIALIZED_FUNCTION]:
     """Decorator to add a serializer for a given type.
 
     Args:
         fn: The function to decorate.
         to: The type returned by the serializer. If this is `str`, then any Var created from this type will be treated as a string.
+        overwrite: Whether to overwrite the existing serializer.
 
     Returns:
         The decorated function.
@@ -85,9 +91,24 @@ def serializer(
 
         # Make sure the type is not already registered.
         registered_fn = SERIALIZERS.get(type_)
-        if registered_fn is not None and registered_fn != fn:
-            raise ValueError(
-                f"Serializer for type {type_} is already registered as {registered_fn.__qualname__}."
+        if registered_fn is not None and registered_fn != fn and overwrite is not True:
+            message = f"Overwriting serializer for type {type_} from {registered_fn.__module__}:{registered_fn.__qualname__} to {fn.__module__}:{fn.__qualname__}."
+            if overwrite is False:
+                raise ValueError(message)
+            caller_frame = next(
+                filter(
+                    lambda frame: frame.filename != __file__,
+                    inspect.getouterframes(inspect.currentframe()),
+                ),
+                None,
+            )
+            file_info = (
+                f"(at {caller_frame.filename}:{caller_frame.lineno})"
+                if caller_frame
+                else ""
+            )
+            console.warn(
+                f"{message} Call rx.serializer with `overwrite=True` if this is intentional. {file_info}"
             )
 
         to_type = to or type_hints.get("return")
@@ -351,6 +372,19 @@ def serialize_enum(en: Enum) -> str:
     return en.value
 
 
+@serializer(to=str)
+def serialize_uuid(uuid: UUID) -> str:
+    """Serialize a UUID to a JSON string.
+
+    Args:
+        uuid: The UUID to serialize.
+
+    Returns:
+        The serialized UUID.
+    """
+    return str(uuid)
+
+
 @serializer(to=str)
 def serialize_color(color: Color) -> str:
     """Serialize a color.