Browse Source

format_dict: only unescape quotes when removing outer quotes (#1609)

Masen Furer 1 năm trước cách đây
mục cha
commit
26e45c1f18
2 tập tin đã thay đổi với 31 bổ sung3 xóa
  1. 15 3
      reflex/utils/format.py
  2. 16 0
      tests/components/test_tag.py

+ 15 - 3
reflex/utils/format.py

@@ -473,10 +473,22 @@ def format_dict(prop: ComponentStyle) -> str:
     # Dump the dict to a string.
     # Dump the dict to a string.
     fprop = json_dumps(prop)
     fprop = json_dumps(prop)
 
 
+    def unescape_double_quotes_in_var(m: re.Match) -> str:
+        # Since the outer quotes are removed, the inner escaped quotes must be unescaped.
+        return re.sub('\\\\"', '"', m.group(1))
+
     # This substitution is necessary to unwrap var values.
     # This substitution is necessary to unwrap var values.
-    fprop = re.sub('"{', "", fprop)
-    fprop = re.sub('}"', "", fprop)
-    fprop = re.sub('\\\\"', '"', fprop)
+    fprop = re.sub(
+        pattern=r"""
+            (?<!\\)      # must NOT start with a backslash
+            "            # match opening double quote of JSON value
+            {(.*?)}      # extract the value between curly braces (non-greedy)
+            "            # match must end with an unescaped double quote
+        """,
+        repl=unescape_double_quotes_in_var,
+        string=fprop,
+        flags=re.VERBOSE,
+    )
 
 
     # Return the formatted dict.
     # Return the formatted dict.
     return fprop
     return fprop

+ 16 - 0
tests/components/test_tag.py

@@ -4,6 +4,7 @@ import pytest
 
 
 from reflex.components.tags import CondTag, Tag, tagless
 from reflex.components.tags import CondTag, Tag, tagless
 from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
 from reflex.event import EVENT_ARG, EventChain, EventHandler, EventSpec
+from reflex.style import Style
 from reflex.vars import BaseVar, Var
 from reflex.vars import BaseVar, Var
 
 
 
 
@@ -23,6 +24,14 @@ def mock_event(arg):
         ([1, 2, 3], "{[1, 2, 3]}"),
         ([1, 2, 3], "{[1, 2, 3]}"),
         (["a", "b", "c"], '{["a", "b", "c"]}'),
         (["a", "b", "c"], '{["a", "b", "c"]}'),
         ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
         ({"a": 1, "b": 2, "c": 3}, '{{"a": 1, "b": 2, "c": 3}}'),
+        ({"a": 'foo "bar" baz'}, r'{{"a": "foo \"bar\" baz"}}'),
+        (
+            {
+                "a": 'foo "{ "bar" }" baz',
+                "b": BaseVar(name="val", type_="str"),
+            },
+            r'{{"a": "foo \"{ \"bar\" }\" baz", "b": val}}',
+        ),
         (
         (
             EventChain(events=[EventSpec(handler=EventHandler(fn=mock_event))]),
             EventChain(events=[EventSpec(handler=EventHandler(fn=mock_event))]),
             '{_e => Event([E("mock_event", {})], _e)}',
             '{_e => Event([E("mock_event", {})], _e)}',
@@ -57,6 +66,13 @@ def mock_event(arg):
             {"a": BaseVar(name='state.colors["val"]', type_="str")},
             {"a": BaseVar(name='state.colors["val"]', type_="str")},
             '{{"a": state.colors["val"]}}',
             '{{"a": state.colors["val"]}}',
         ),
         ),
+        # tricky real-world case from markdown component
+        (
+            {
+                "h1": f"{{({{node, ...props}}) => <Heading {{...props}} {''.join(Tag(name='', props=Style({'as_': 'h1'})).format_props())} />}}"
+            },
+            '{{"h1": ({node, ...props}) => <Heading {...props} as={`h1`} />}}',
+        ),
     ],
     ],
 )
 )
 def test_format_prop(prop: Var, formatted: str):
 def test_format_prop(prop: Var, formatted: str):