reflex 0.8.7a1__py3-none-any.whl → 0.8.8a2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (70) hide show
  1. reflex/.templates/web/utils/state.js +5 -5
  2. reflex/app.py +15 -7
  3. reflex/app_mixins/lifespan.py +8 -2
  4. reflex/compiler/compiler.py +14 -14
  5. reflex/compiler/templates.py +629 -102
  6. reflex/compiler/utils.py +30 -21
  7. reflex/components/base/bare.py +17 -0
  8. reflex/components/component.py +38 -34
  9. reflex/components/core/cond.py +6 -12
  10. reflex/components/core/foreach.py +1 -1
  11. reflex/components/core/match.py +83 -60
  12. reflex/components/dynamic.py +3 -3
  13. reflex/components/el/elements/forms.py +31 -14
  14. reflex/components/el/elements/forms.pyi +0 -5
  15. reflex/components/lucide/icon.py +2 -1
  16. reflex/components/lucide/icon.pyi +2 -1
  17. reflex/components/markdown/markdown.py +2 -2
  18. reflex/components/radix/primitives/accordion.py +1 -1
  19. reflex/components/radix/primitives/drawer.py +10 -22
  20. reflex/components/radix/primitives/form.py +1 -1
  21. reflex/components/radix/primitives/slider.py +1 -1
  22. reflex/components/tags/cond_tag.py +14 -5
  23. reflex/components/tags/iter_tag.py +0 -26
  24. reflex/components/tags/match_tag.py +15 -6
  25. reflex/components/tags/tag.py +3 -6
  26. reflex/components/tags/tagless.py +14 -0
  27. reflex/constants/base.py +0 -2
  28. reflex/constants/compiler.py +1 -1
  29. reflex/constants/installer.py +4 -4
  30. reflex/custom_components/custom_components.py +202 -15
  31. reflex/event.py +1 -1
  32. reflex/experimental/client_state.py +1 -1
  33. reflex/istate/manager.py +2 -1
  34. reflex/plugins/shared_tailwind.py +87 -62
  35. reflex/plugins/tailwind_v3.py +2 -2
  36. reflex/plugins/tailwind_v4.py +4 -4
  37. reflex/state.py +5 -1
  38. reflex/utils/format.py +3 -4
  39. reflex/utils/frontend_skeleton.py +2 -2
  40. reflex/utils/imports.py +18 -0
  41. reflex/utils/pyi_generator.py +10 -2
  42. reflex/utils/telemetry.py +4 -1
  43. reflex/utils/templates.py +1 -6
  44. {reflex-0.8.7a1.dist-info → reflex-0.8.8a2.dist-info}/METADATA +3 -4
  45. {reflex-0.8.7a1.dist-info → reflex-0.8.8a2.dist-info}/RECORD +48 -70
  46. reflex/.templates/jinja/app/rxconfig.py.jinja2 +0 -9
  47. reflex/.templates/jinja/custom_components/README.md.jinja2 +0 -9
  48. reflex/.templates/jinja/custom_components/__init__.py.jinja2 +0 -1
  49. reflex/.templates/jinja/custom_components/demo_app.py.jinja2 +0 -39
  50. reflex/.templates/jinja/custom_components/pyproject.toml.jinja2 +0 -25
  51. reflex/.templates/jinja/custom_components/src.py.jinja2 +0 -57
  52. reflex/.templates/jinja/web/package.json.jinja2 +0 -27
  53. reflex/.templates/jinja/web/pages/_app.js.jinja2 +0 -62
  54. reflex/.templates/jinja/web/pages/_document.js.jinja2 +0 -9
  55. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -21
  56. reflex/.templates/jinja/web/pages/component.js.jinja2 +0 -2
  57. reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +0 -22
  58. reflex/.templates/jinja/web/pages/index.js.jinja2 +0 -18
  59. reflex/.templates/jinja/web/pages/macros.js.jinja2 +0 -38
  60. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +0 -15
  61. reflex/.templates/jinja/web/pages/stateful_components.js.jinja2 +0 -5
  62. reflex/.templates/jinja/web/pages/utils.js.jinja2 +0 -93
  63. reflex/.templates/jinja/web/styles/styles.css.jinja2 +0 -6
  64. reflex/.templates/jinja/web/utils/context.js.jinja2 +0 -129
  65. reflex/.templates/jinja/web/utils/theme.js.jinja2 +0 -1
  66. reflex/.templates/jinja/web/vite.config.js.jinja2 +0 -74
  67. reflex/components/core/client_side_routing.pyi +0 -68
  68. {reflex-0.8.7a1.dist-info → reflex-0.8.8a2.dist-info}/WHEEL +0 -0
  69. {reflex-0.8.7a1.dist-info → reflex-0.8.8a2.dist-info}/entry_points.txt +0 -0
  70. {reflex-0.8.7a1.dist-info → reflex-0.8.8a2.dist-info}/licenses/LICENSE +0 -0
@@ -18,6 +18,202 @@ from reflex.constants import CustomComponents
18
18
  from reflex.utils import console, frontend_skeleton
19
19
 
20
20
 
21
+ def _pyproject_toml_template(
22
+ package_name: str, module_name: str, reflex_version: str
23
+ ) -> str:
24
+ """Template for custom components pyproject.toml.
25
+
26
+ Args:
27
+ package_name: The name of the package.
28
+ module_name: The name of the module.
29
+ reflex_version: The version of Reflex.
30
+
31
+ Returns:
32
+ Rendered pyproject.toml content as string.
33
+ """
34
+ return f"""[build-system]
35
+ requires = ["setuptools", "wheel"]
36
+ build-backend = "setuptools.build_meta"
37
+
38
+ [project]
39
+ name = "{package_name}"
40
+ version = "0.0.1"
41
+ description = "Reflex custom component {module_name}"
42
+ readme = "README.md"
43
+ license = {{ text = "Apache-2.0" }}
44
+ requires-python = ">=3.10"
45
+ authors = [{{ name = "", email = "YOUREMAIL@domain.com" }}]
46
+ keywords = ["reflex","reflex-custom-components"]
47
+
48
+ dependencies = ["reflex>={reflex_version}"]
49
+
50
+ classifiers = ["Development Status :: 4 - Beta"]
51
+
52
+ [project.urls]
53
+
54
+ [project.optional-dependencies]
55
+ dev = ["build", "twine"]
56
+
57
+ [tool.setuptools.packages.find]
58
+ where = ["custom_components"]
59
+ """
60
+
61
+
62
+ def _readme_template(module_name: str, package_name: str) -> str:
63
+ """Template for custom components README.
64
+
65
+ Args:
66
+ module_name: The name of the module.
67
+ package_name: The name of the package.
68
+
69
+ Returns:
70
+ Rendered README.md content as string.
71
+ """
72
+ return f"""# {module_name}
73
+
74
+ A Reflex custom component {module_name}.
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ pip install {package_name}
80
+ ```
81
+ """
82
+
83
+
84
+ def _source_template(component_class_name: str, module_name: str) -> str:
85
+ """Template for custom components source.
86
+
87
+ Args:
88
+ component_class_name: The name of the component class.
89
+ module_name: The name of the module.
90
+
91
+ Returns:
92
+ Rendered custom component source code as string.
93
+ """
94
+ return rf'''
95
+ """Reflex custom component {component_class_name}."""
96
+
97
+ # For wrapping react guide, visit https://reflex.dev/docs/wrapping-react/overview/
98
+
99
+ import reflex as rx
100
+
101
+ # Some libraries you want to wrap may require dynamic imports.
102
+ # This is because they they may not be compatible with Server-Side Rendering (SSR).
103
+ # To handle this in Reflex, all you need to do is subclass `NoSSRComponent` instead.
104
+ # For example:
105
+ # from reflex.components.component import NoSSRComponent
106
+ # class {component_class_name}(NoSSRComponent):
107
+ # pass
108
+
109
+
110
+ class {component_class_name}(rx.Component):
111
+ """{component_class_name} component."""
112
+
113
+ # The React library to wrap.
114
+ library = "Fill-Me"
115
+
116
+ # The React component tag.
117
+ tag = "Fill-Me"
118
+
119
+ # If the tag is the default export from the module, you must set is_default = True.
120
+ # This is normally used when components don't have curly braces around them when importing.
121
+ # is_default = True
122
+
123
+ # If you are wrapping another components with the same tag as a component in your project
124
+ # you can use aliases to differentiate between them and avoid naming conflicts.
125
+ # alias = "Other{component_class_name}"
126
+
127
+ # The props of the React component.
128
+ # Note: when Reflex compiles the component to Javascript,
129
+ # `snake_case` property names are automatically formatted as `camelCase`.
130
+ # The prop names may be defined in `camelCase` as well.
131
+ # some_prop: rx.Var[str] = "some default value"
132
+ # some_other_prop: rx.Var[int] = 1
133
+
134
+ # By default Reflex will install the library you have specified in the library property.
135
+ # However, sometimes you may need to install other libraries to use a component.
136
+ # In this case you can use the lib_dependencies property to specify other libraries to install.
137
+ # lib_dependencies: list[str] = []
138
+
139
+ # Event triggers declaration if any.
140
+ # Below is equivalent to merging `{{ "on_change": lambda e: [e] }}`
141
+ # onto the default event triggers of parent/base Component.
142
+ # The function defined for the `on_change` trigger maps event for the javascript
143
+ # trigger to what will be passed to the backend event handler function.
144
+ # on_change: rx.EventHandler[lambda e: [e]]
145
+
146
+ # To add custom code to your component
147
+ # def _get_custom_code(self) -> str:
148
+ # return "const customCode = 'customCode';"
149
+
150
+
151
+ {module_name} = {component_class_name}.create
152
+ '''
153
+
154
+
155
+ def _init_template(module_name: str) -> str:
156
+ """Template for custom components __init__.py.
157
+
158
+ Args:
159
+ module_name: The name of the module.
160
+
161
+ Returns:
162
+ Rendered __init__.py content as string.
163
+ """
164
+ return f"from .{module_name} import *"
165
+
166
+
167
+ def _demo_app_template(custom_component_module_dir: str, module_name: str) -> str:
168
+ """Template for custom components demo app.
169
+
170
+ Args:
171
+ custom_component_module_dir: The directory of the custom component module.
172
+ module_name: The name of the module.
173
+
174
+ Returns:
175
+ Rendered demo app source code as string.
176
+ """
177
+ return rf'''
178
+ """Welcome to Reflex! This file showcases the custom component in a basic app."""
179
+
180
+ from rxconfig import config
181
+
182
+ import reflex as rx
183
+
184
+ from {custom_component_module_dir} import {module_name}
185
+
186
+ filename = f"{{config.app_name}}/{{config.app_name}}.py"
187
+
188
+
189
+ class State(rx.State):
190
+ """The app state."""
191
+ pass
192
+
193
+ def index() -> rx.Component:
194
+ return rx.center(
195
+ rx.theme_panel(),
196
+ rx.vstack(
197
+ rx.heading("Welcome to Reflex!", size="9"),
198
+ rx.text(
199
+ "Test your custom component by editing ",
200
+ rx.code(filename),
201
+ font_size="2em",
202
+ ),
203
+ {module_name}(),
204
+ align="center",
205
+ spacing="7",
206
+ ),
207
+ height="100vh",
208
+ )
209
+
210
+
211
+ # Add state and page to the app.
212
+ app = rx.App()
213
+ app.add_page(index)
214
+ '''
215
+
216
+
21
217
  def set_loglevel(ctx: Any, self: Any, value: str | None):
22
218
  """Set the log level.
23
219
 
@@ -77,11 +273,9 @@ def _create_package_config(module_name: str, package_name: str):
77
273
  module_name: The name of the module.
78
274
  package_name: The name of the package typically constructed with `reflex-` prefix and a meaningful library name.
79
275
  """
80
- from reflex.compiler import templates
81
-
82
276
  pyproject = Path(CustomComponents.PYPROJECT_TOML)
83
277
  pyproject.write_text(
84
- templates.CUSTOM_COMPONENTS_PYPROJECT_TOML.render(
278
+ _pyproject_toml_template(
85
279
  module_name=module_name,
86
280
  package_name=package_name,
87
281
  reflex_version=constants.Reflex.VERSION,
@@ -96,11 +290,9 @@ def _create_readme(module_name: str, package_name: str):
96
290
  module_name: The name of the module.
97
291
  package_name: The name of the python package to be published.
98
292
  """
99
- from reflex.compiler import templates
100
-
101
293
  readme = Path(CustomComponents.PACKAGE_README)
102
294
  readme.write_text(
103
- templates.CUSTOM_COMPONENTS_README.render(
295
+ _readme_template(
104
296
  module_name=module_name,
105
297
  package_name=package_name,
106
298
  )
@@ -119,19 +311,15 @@ def _write_source_and_init_py(
119
311
  component_class_name: The name of the component class.
120
312
  module_name: The name of the module.
121
313
  """
122
- from reflex.compiler import templates
123
-
124
314
  module_path = custom_component_src_dir / f"{module_name}.py"
125
315
  module_path.write_text(
126
- templates.CUSTOM_COMPONENTS_SOURCE.render(
316
+ _source_template(
127
317
  component_class_name=component_class_name, module_name=module_name
128
318
  )
129
319
  )
130
320
 
131
321
  init_path = custom_component_src_dir / CustomComponents.INIT_FILE
132
- init_path.write_text(
133
- templates.CUSTOM_COMPONENTS_INIT_FILE.render(module_name=module_name)
134
- )
322
+ init_path.write_text(_init_template(module_name=module_name))
135
323
 
136
324
 
137
325
  def _populate_demo_app(name_variants: NameVariants):
@@ -141,7 +329,6 @@ def _populate_demo_app(name_variants: NameVariants):
141
329
  name_variants: the tuple including various names such as package name, class name needed for the project.
142
330
  """
143
331
  from reflex import constants
144
- from reflex.compiler import templates
145
332
  from reflex.reflex import _init
146
333
 
147
334
  demo_app_dir = Path(name_variants.demo_app_dir)
@@ -155,10 +342,10 @@ def _populate_demo_app(name_variants: NameVariants):
155
342
  # We start with the blank template as basis.
156
343
  _init(name=demo_app_name, template=constants.Templates.DEFAULT)
157
344
  # Then overwrite the app source file with the one we want for testing custom components.
158
- # This source file is rendered using jinja template file.
345
+ # This source file is rendered using template file.
159
346
  demo_file = Path(f"{demo_app_name}/{demo_app_name}.py")
160
347
  demo_file.write_text(
161
- templates.CUSTOM_COMPONENTS_DEMO_APP.render(
348
+ _demo_app_template(
162
349
  custom_component_module_dir=name_variants.custom_component_module_dir,
163
350
  module_name=name_variants.module_name,
164
351
  )
reflex/event.py CHANGED
@@ -1930,7 +1930,7 @@ class LiteralEventVar(VarOperationCall, LiteralVar, EventVar):
1930
1930
  _var_type=EventSpec,
1931
1931
  _var_data=_var_data,
1932
1932
  _var_value=value,
1933
- _func=FunctionStringVar("Event"),
1933
+ _func=FunctionStringVar("ReflexEvent"),
1934
1934
  _args=(
1935
1935
  # event handler name
1936
1936
  ".".join(
@@ -167,7 +167,7 @@ class ClientStateVar(Var):
167
167
  ] = None
168
168
  imports.update(_refs_import)
169
169
  return cls(
170
- _js_expr="",
170
+ _js_expr="null",
171
171
  _setter_name=setter_name,
172
172
  _getter_name=var_name,
173
173
  _id_name=id_name,
reflex/istate/manager.py CHANGED
@@ -667,7 +667,8 @@ class StateManagerRedis(StateManager):
667
667
  _substate_key(client_token, substate),
668
668
  substate,
669
669
  lock_id,
670
- )
670
+ ),
671
+ name=f"reflex_set_state|{client_token}|{substate.get_full_name()}",
671
672
  )
672
673
  for substate in state.substates.values()
673
674
  ]
@@ -1,12 +1,11 @@
1
1
  """Tailwind CSS configuration types for Reflex plugins."""
2
2
 
3
3
  import dataclasses
4
+ from collections.abc import Mapping
4
5
  from copy import deepcopy
5
6
  from typing import Any, Literal, TypedDict
6
7
 
7
- from typing_extensions import NotRequired
8
-
9
- from reflex.utils.decorator import once
8
+ from typing_extensions import NotRequired, Unpack
10
9
 
11
10
  from .base import Plugin as PluginBase
12
11
 
@@ -80,71 +79,97 @@ class TailwindConfig(TypedDict):
80
79
  plugins: NotRequired[list[TailwindPluginConfig]]
81
80
 
82
81
 
83
- @once
84
- def tailwind_config_js_template():
85
- """Get the Tailwind config template.
82
+ def tailwind_config_js_template(
83
+ *, default_content: list[str], **kwargs: Unpack[TailwindConfig]
84
+ ):
85
+ """Generate a Tailwind CSS configuration file in JavaScript format.
86
+
87
+ Args:
88
+ default_content: The default content to use if none is provided.
89
+ **kwargs: The template variables.
86
90
 
87
91
  Returns:
88
92
  The Tailwind config template.
89
93
  """
90
- from reflex.compiler.templates import from_string
91
-
92
- source = r"""
93
- {# Extract destructured imports from plugin dicts only #}
94
- {%- set imports = [] %}
95
-
96
- {%- for plugin in plugins if plugin is mapping and plugin.import is defined %}
97
- {%- set _ = imports.append(plugin.import) %}
98
- {%- endfor %}
99
-
100
- {%- for imp in imports %}
101
- import { {{ imp.name }} } from {{ imp.from | tojson }};
102
- {%- endfor %}
103
-
104
- {%- for plugin in plugins %}
105
- {% if plugin is mapping and plugin.call is not defined %}
106
- import plugin{{ loop.index }} from {{ plugin.name | tojson }};
107
- {%- elif plugin is not mapping %}
108
- import plugin{{ loop.index }} from {{ plugin | tojson }};
109
- {%- endif %}
110
- {%- endfor %}
111
-
112
- {%- for preset in presets %}
113
- import preset{{ loop.index }} from {{ preset | tojson }};
114
- {%- endfor %}
115
-
116
- export default {
117
- content: {{ (content if content is defined else DEFAULT_CONTENT) | tojson }},
118
- {% if theme is defined %}theme: {{ theme | tojson }},{% else %}theme: {},{% endif %}
119
- {% if darkMode is defined %}darkMode: {{ darkMode | tojson }},{% endif %}
120
- {% if corePlugins is defined %}corePlugins: {{ corePlugins | tojson }},{% endif %}
121
- {% if important is defined %}important: {{ important | tojson }},{% endif %}
122
- {% if prefix is defined %}prefix: {{ prefix | tojson }},{% endif %}
123
- {% if separator is defined %}separator: {{ separator | tojson }},{% endif %}
124
- {% if presets is defined %}
125
- presets: [
126
- {% for preset in presets %}
127
- preset{{ loop.index }},
128
- {% endfor %}
129
- ],
130
- {% endif %}
131
- plugins: [
132
- {% for plugin in plugins %}
133
- {% if plugin is mapping and plugin.call is defined %}
134
- {{ plugin.call }}(
135
- {%- if plugin.args is defined -%}
136
- {{ plugin.args | tojson }}
137
- {%- endif -%}
138
- ),
139
- {% else %}
140
- plugin{{ loop.index }},
141
- {% endif %}
142
- {% endfor %}
94
+ import json
95
+
96
+ # Extract parameters
97
+ plugins = kwargs.get("plugins", [])
98
+ presets = kwargs.get("presets", [])
99
+ content = kwargs.get("content")
100
+ theme = kwargs.get("theme")
101
+ dark_mode = kwargs.get("darkMode")
102
+ core_plugins = kwargs.get("corePlugins")
103
+ important = kwargs.get("important")
104
+ prefix = kwargs.get("prefix")
105
+ separator = kwargs.get("separator")
106
+
107
+ # Extract destructured imports from plugin dicts only
108
+ imports = [
109
+ plugin["import"]
110
+ for plugin in plugins
111
+ if isinstance(plugin, Mapping) and "import" in plugin
143
112
  ]
144
- };
145
- """
146
113
 
147
- return from_string(source)
114
+ # Generate import statements for destructured imports
115
+ import_lines = "\n".join(
116
+ [
117
+ f"import {{ {imp['name']} }} from {json.dumps(imp['from'])};"
118
+ for imp in imports
119
+ ]
120
+ )
121
+
122
+ # Generate plugin imports
123
+ plugin_imports = []
124
+ for i, plugin in enumerate(plugins, 1):
125
+ if isinstance(plugin, Mapping) and "call" not in plugin:
126
+ plugin_imports.append(
127
+ f"import plugin{i} from {json.dumps(plugin['name'])};"
128
+ )
129
+ elif not isinstance(plugin, Mapping):
130
+ plugin_imports.append(f"import plugin{i} from {json.dumps(plugin)};")
131
+
132
+ plugin_imports_lines = "\n".join(plugin_imports)
133
+
134
+ presets_imports_lines = "\n".join(
135
+ [
136
+ f"import preset{i} from {json.dumps(preset)};"
137
+ for i, preset in enumerate(presets, 1)
138
+ ]
139
+ )
140
+
141
+ # Generate plugin array
142
+ plugin_list = []
143
+ for i, plugin in enumerate(plugins, 1):
144
+ if isinstance(plugin, Mapping) and "call" in plugin:
145
+ args_part = ""
146
+ if "args" in plugin:
147
+ args_part = json.dumps(plugin["args"])
148
+ plugin_list.append(f"{plugin['call']}({args_part})")
149
+ else:
150
+ plugin_list.append(f"plugin{i}")
151
+
152
+ plugin_use_str = ",".join(plugin_list)
153
+
154
+ return rf"""
155
+ {import_lines}
156
+
157
+ {plugin_imports_lines}
158
+
159
+ {presets_imports_lines}
160
+
161
+ export default {{
162
+ content: {json.dumps(content if content else default_content)},
163
+ theme: {json.dumps(theme if theme else {})},
164
+ {f"darkMode: {json.dumps(dark_mode)}," if dark_mode is not None else ""}
165
+ {f"corePlugins: {json.dumps(core_plugins)}," if core_plugins is not None else ""}
166
+ {f"importants: {json.dumps(important)}," if important is not None else ""}
167
+ {f"prefix: {json.dumps(prefix)}," if prefix is not None else ""}
168
+ {f"separator: {json.dumps(separator)}," if separator is not None else ""}
169
+ {f"presets: [{', '.join(f'preset{i}' for i in range(1, len(presets) + 1))}]," if presets else ""}
170
+ plugins: [{plugin_use_str}]
171
+ }};
172
+ """
148
173
 
149
174
 
150
175
  @dataclasses.dataclass
@@ -48,9 +48,9 @@ def compile_config(config: TailwindConfig):
48
48
  Returns:
49
49
  The compiled Tailwind config.
50
50
  """
51
- return Constants.CONFIG, tailwind_config_js_template().render(
51
+ return Constants.CONFIG, tailwind_config_js_template(
52
52
  **config,
53
- DEFAULT_CONTENT=Constants.CONTENT,
53
+ default_content=Constants.CONTENT,
54
54
  )
55
55
 
56
56
 
@@ -17,7 +17,7 @@ class Constants(SimpleNamespace):
17
17
  """Tailwind constants."""
18
18
 
19
19
  # The Tailwindcss version
20
- VERSION = "tailwindcss@4.1.11"
20
+ VERSION = "tailwindcss@4.1.12"
21
21
  # The Tailwind config.
22
22
  CONFIG = "tailwind.config.js"
23
23
  # Default Tailwind content paths
@@ -47,9 +47,9 @@ def compile_config(config: TailwindConfig):
47
47
  Returns:
48
48
  The compiled Tailwind config.
49
49
  """
50
- return Constants.CONFIG, tailwind_config_js_template().render(
50
+ return Constants.CONFIG, tailwind_config_js_template(
51
51
  **config,
52
- DEFAULT_CONTENT=Constants.CONTENT,
52
+ default_content=Constants.CONTENT,
53
53
  )
54
54
 
55
55
 
@@ -156,7 +156,7 @@ class TailwindV4Plugin(TailwindPlugin):
156
156
  return [
157
157
  *super().get_frontend_development_dependencies(**context),
158
158
  Constants.VERSION,
159
- "@tailwindcss/postcss@4.1.11",
159
+ "@tailwindcss/postcss@4.1.12",
160
160
  ]
161
161
 
162
162
  def pre_compile(self, **context):
reflex/state.py CHANGED
@@ -11,6 +11,7 @@ import functools
11
11
  import inspect
12
12
  import pickle
13
13
  import sys
14
+ import time
14
15
  import typing
15
16
  import warnings
16
17
  from collections.abc import AsyncIterator, Callable, Sequence
@@ -284,7 +285,10 @@ async def _resolve_delta(delta: Delta) -> Delta:
284
285
  for state_name, state_delta in delta.items():
285
286
  for var_name, value in state_delta.items():
286
287
  if asyncio.iscoroutine(value):
287
- tasks[state_name, var_name] = asyncio.create_task(value)
288
+ tasks[state_name, var_name] = asyncio.create_task(
289
+ value,
290
+ name=f"reflex_resolve_delta|{state_name}|{var_name}|{time.time()}",
291
+ )
288
292
  for (state_name, var_name), task in tasks.items():
289
293
  delta[state_name][var_name] = await task
290
294
  return delta
reflex/utils/format.py CHANGED
@@ -330,7 +330,7 @@ def format_route(route: str) -> str:
330
330
 
331
331
  def format_match(
332
332
  cond: str | Var,
333
- match_cases: list[list[Var]],
333
+ match_cases: list[tuple[list[Var], Var]],
334
334
  default: Var,
335
335
  ) -> str:
336
336
  """Format a match expression whose return type is a Var.
@@ -347,8 +347,7 @@ def format_match(
347
347
  switch_code = f"(() => {{ switch (JSON.stringify({cond})) {{"
348
348
 
349
349
  for case in match_cases:
350
- conditions = case[:-1]
351
- return_value = case[-1]
350
+ conditions, return_value = case
352
351
 
353
352
  case_conditions = " ".join(
354
353
  [f"case JSON.stringify({condition!s}):" for condition in conditions]
@@ -519,7 +518,7 @@ def format_event(event_spec: EventSpec) -> str:
519
518
 
520
519
  if event_spec.client_handler_name:
521
520
  event_args.append(wrap(event_spec.client_handler_name, '"'))
522
- return f"Event({', '.join(event_args)})"
521
+ return f"ReflexEvent({', '.join(event_args)})"
523
522
 
524
523
 
525
524
  if TYPE_CHECKING:
@@ -169,7 +169,7 @@ def _update_react_router_config(config: Config, prerender_routes: bool = False):
169
169
 
170
170
 
171
171
  def _compile_package_json():
172
- return templates.PACKAGE_JSON.render(
172
+ return templates.package_json_template(
173
173
  scripts={
174
174
  "dev": constants.PackageJson.Commands.DEV,
175
175
  "export": constants.PackageJson.Commands.EXPORT,
@@ -192,7 +192,7 @@ def _compile_vite_config(config: Config):
192
192
  base = "/"
193
193
  if frontend_path := config.frontend_path.strip("/"):
194
194
  base += frontend_path + "/"
195
- return templates.VITE_CONFIG.render(base=base)
195
+ return templates.vite_config_template(base=base)
196
196
 
197
197
 
198
198
  def initialize_vite_config():
reflex/utils/imports.py CHANGED
@@ -7,6 +7,24 @@ from collections import defaultdict
7
7
  from collections.abc import Mapping, Sequence
8
8
 
9
9
 
10
+ def merge_parsed_imports(
11
+ *imports: ImmutableParsedImportDict,
12
+ ) -> ParsedImportDict:
13
+ """Merge multiple parsed import dicts together.
14
+
15
+ Args:
16
+ *imports: The list of import dicts to merge.
17
+
18
+ Returns:
19
+ The merged import dicts.
20
+ """
21
+ all_imports: defaultdict[str, list[ImportVar]] = defaultdict(list)
22
+ for import_dict in imports:
23
+ for lib, fields in import_dict.items():
24
+ all_imports[lib].extend(fields)
25
+ return all_imports
26
+
27
+
10
28
  def merge_imports(
11
29
  *imports: ImportDict | ParsedImportDict | ParsedImportTuple,
12
30
  ) -> ParsedImportDict:
@@ -145,7 +145,7 @@ def _get_type_hint(
145
145
  res = ""
146
146
  args = get_args(value)
147
147
 
148
- if value is type(None):
148
+ if value is type(None) or value is None:
149
149
  return "None"
150
150
 
151
151
  if rx_types.is_union(value):
@@ -425,10 +425,18 @@ def type_to_ast(typ: Any, cls: type) -> ast.expr:
425
425
  Returns:
426
426
  The AST representation of the type annotation.
427
427
  """
428
- if typ is type(None):
428
+ if typ is type(None) or typ is None:
429
429
  return ast.Name(id="None")
430
430
 
431
431
  origin = get_origin(typ)
432
+ if origin is typing.Literal:
433
+ return ast.Subscript(
434
+ value=ast.Name(id="Literal"),
435
+ slice=ast.Tuple(
436
+ elts=[ast.Constant(value=val) for val in get_args(typ)], ctx=ast.Load()
437
+ ),
438
+ ctx=ast.Load(),
439
+ )
432
440
  if origin is UnionType:
433
441
  origin = typing.Union
434
442
 
reflex/utils/telemetry.py CHANGED
@@ -353,7 +353,10 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
353
353
 
354
354
  try:
355
355
  # Within an event loop context, send the event asynchronously.
356
- task = asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
356
+ task = asyncio.create_task(
357
+ async_send(event, telemetry_enabled, **kwargs),
358
+ name=f"reflex_send_telemetry_event|{event}",
359
+ )
357
360
  background_tasks.add(task)
358
361
  task.add_done_callback(background_tasks.discard)
359
362
  except RuntimeError:
reflex/utils/templates.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """This module provides utilities for managing Reflex app templates."""
2
2
 
3
3
  import dataclasses
4
- import re
5
4
  import shutil
6
5
  import tempfile
7
6
  import zipfile
@@ -33,12 +32,8 @@ def create_config(app_name: str):
33
32
  # Import here to avoid circular imports.
34
33
  from reflex.compiler import templates
35
34
 
36
- config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
37
-
38
35
  console.debug(f"Creating {constants.Config.FILE}")
39
- constants.Config.FILE.write_text(
40
- templates.RXCONFIG.render(app_name=app_name, config_name=config_name)
41
- )
36
+ constants.Config.FILE.write_text(templates.rxconfig_template(app_name=app_name))
42
37
 
43
38
 
44
39
  def initialize_app_directory(