reflex 0.7.5a1__py3-none-any.whl → 0.7.6__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.

@@ -2,7 +2,7 @@
2
2
  {% from "web/pages/macros.js.jinja2" import renderHooks %}
3
3
 
4
4
  {% block early_imports %}
5
- import '$/styles/styles.css'
5
+ import '$/styles/__reflex_global_styles.css'
6
6
  {% endblock %}
7
7
 
8
8
  {% block declaration %}
reflex/__init__.py CHANGED
@@ -370,5 +370,5 @@ getattr, __dir__, __all__ = lazy_loader.attach(
370
370
  )
371
371
 
372
372
 
373
- def __getattr__(name: ModuleType | Any):
373
+ def __getattr__(name: str):
374
374
  return getattr(name)
reflex/admin.py CHANGED
@@ -1,8 +1,12 @@
1
1
  """The Reflex Admin Dashboard."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  from dataclasses import dataclass, field
6
+ from typing import TYPE_CHECKING
4
7
 
5
- from starlette_admin.base import BaseAdmin as Admin
8
+ if TYPE_CHECKING:
9
+ from starlette_admin.base import BaseAdmin as Admin
6
10
 
7
11
 
8
12
  @dataclass
@@ -11,4 +15,4 @@ class AdminDash:
11
15
 
12
16
  models: list = field(default_factory=list)
13
17
  view_overrides: dict = field(default_factory=dict)
14
- admin: Admin | None = None
18
+ admin: "Admin | None" = None
reflex/app.py CHANGED
@@ -40,8 +40,6 @@ from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
40
40
  from socketio import ASGIApp, AsyncNamespace, AsyncServer
41
41
  from starlette.datastructures import Headers
42
42
  from starlette.datastructures import UploadFile as StarletteUploadFile
43
- from starlette_admin.contrib.sqla.admin import Admin
44
- from starlette_admin.contrib.sqla.view import ModelView
45
43
 
46
44
  from reflex import constants
47
45
  from reflex.admin import AdminDash
@@ -295,6 +293,7 @@ class UnevaluatedPage:
295
293
  image: str
296
294
  on_load: EventType[()] | None
297
295
  meta: list[dict[str, str]]
296
+ context: dict[str, Any] | None
298
297
 
299
298
 
300
299
  @dataclasses.dataclass()
@@ -445,6 +444,8 @@ class App(MiddlewareMixin, LifespanMixin):
445
444
  "rx.BaseState cannot be subclassed directly. Use rx.State instead"
446
445
  )
447
446
 
447
+ get_config(reload=True)
448
+
448
449
  if "breakpoints" in self.style:
449
450
  set_breakpoints(self.style.pop("breakpoints"))
450
451
 
@@ -681,6 +682,7 @@ class App(MiddlewareMixin, LifespanMixin):
681
682
  image: str = constants.DefaultPage.IMAGE,
682
683
  on_load: EventType[()] | None = None,
683
684
  meta: list[dict[str, str]] = constants.DefaultPage.META_LIST,
685
+ context: dict[str, Any] | None = None,
684
686
  ):
685
687
  """Add a page to the app.
686
688
 
@@ -695,6 +697,7 @@ class App(MiddlewareMixin, LifespanMixin):
695
697
  image: The image to display on the page.
696
698
  on_load: The event handler(s) that will be called each time the page load.
697
699
  meta: The metadata of the page.
700
+ context: Values passed to page for custom page-specific logic.
698
701
 
699
702
  Raises:
700
703
  PageValueError: When the component is not set for a non-404 page.
@@ -762,6 +765,7 @@ class App(MiddlewareMixin, LifespanMixin):
762
765
  image=image,
763
766
  on_load=on_load,
764
767
  meta=meta,
768
+ context=context,
765
769
  )
766
770
 
767
771
  def _compile_page(self, route: str, save_page: bool = True):
@@ -885,6 +889,12 @@ class App(MiddlewareMixin, LifespanMixin):
885
889
 
886
890
  def _setup_admin_dash(self):
887
891
  """Setup the admin dash."""
892
+ try:
893
+ from starlette_admin.contrib.sqla.admin import Admin
894
+ from starlette_admin.contrib.sqla.view import ModelView
895
+ except ImportError:
896
+ return
897
+
888
898
  # Get the admin dash.
889
899
  if not self.api:
890
900
  return
@@ -1427,7 +1437,7 @@ class App(MiddlewareMixin, LifespanMixin):
1427
1437
  async with self.state_manager.modify_state(token) as state:
1428
1438
  # No other event handler can modify the state while in this context.
1429
1439
  yield state
1430
- delta = state.get_delta()
1440
+ delta = await state._get_resolved_delta()
1431
1441
  if delta:
1432
1442
  # When the state is modified reset dirty status and emit the delta to the frontend.
1433
1443
  state._clean()
@@ -17,6 +17,7 @@ from reflex.components.component import (
17
17
  StatefulComponent,
18
18
  )
19
19
  from reflex.config import environment, get_config
20
+ from reflex.constants.compiler import PageNames
20
21
  from reflex.state import BaseState
21
22
  from reflex.style import SYSTEM_COLOR_MODE
22
23
  from reflex.utils import console, path_ops
@@ -174,6 +175,42 @@ def compile_root_stylesheet(stylesheets: list[str]) -> tuple[str, str]:
174
175
  return output_path, code
175
176
 
176
177
 
178
+ def _validate_stylesheet(stylesheet_full_path: Path, assets_app_path: Path) -> None:
179
+ """Validate the stylesheet.
180
+
181
+ Args:
182
+ stylesheet_full_path: The stylesheet to validate.
183
+ assets_app_path: The path to the assets directory.
184
+
185
+ Raises:
186
+ ValueError: If the stylesheet is not supported.
187
+ FileNotFoundError: If the stylesheet is not found.
188
+ """
189
+ suffix = stylesheet_full_path.suffix[1:] if stylesheet_full_path.suffix else ""
190
+ if suffix not in constants.Reflex.STYLESHEETS_SUPPORTED:
191
+ raise ValueError(f"Stylesheet file {stylesheet_full_path} is not supported.")
192
+ if not stylesheet_full_path.absolute().is_relative_to(assets_app_path.absolute()):
193
+ raise FileNotFoundError(
194
+ f"Cannot include stylesheets from outside the assets directory: {stylesheet_full_path}"
195
+ )
196
+ if not stylesheet_full_path.name:
197
+ raise ValueError(
198
+ f"Stylesheet file name cannot be empty: {stylesheet_full_path}"
199
+ )
200
+ if (
201
+ len(
202
+ stylesheet_full_path.absolute()
203
+ .relative_to(assets_app_path.absolute())
204
+ .parts
205
+ )
206
+ == 1
207
+ and stylesheet_full_path.stem == PageNames.STYLESHEET_ROOT
208
+ ):
209
+ raise ValueError(
210
+ f"Stylesheet file name cannot be '{PageNames.STYLESHEET_ROOT}': {stylesheet_full_path}"
211
+ )
212
+
213
+
177
214
  def _compile_root_stylesheet(stylesheets: list[str]) -> str:
178
215
  """Compile the root stylesheet.
179
216
 
@@ -194,10 +231,14 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
194
231
  )
195
232
 
196
233
  failed_to_import_sass = False
234
+ assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
235
+
236
+ stylesheets_files: list[Path] = []
237
+ stylesheets_urls = []
238
+
197
239
  for stylesheet in stylesheets:
198
240
  if not utils.is_valid_url(stylesheet):
199
241
  # check if stylesheet provided exists.
200
- assets_app_path = Path.cwd() / constants.Dirs.APP_ASSETS
201
242
  stylesheet_full_path = assets_app_path / stylesheet.strip("/")
202
243
 
203
244
  if not stylesheet_full_path.exists():
@@ -206,53 +247,51 @@ def _compile_root_stylesheet(stylesheets: list[str]) -> str:
206
247
  )
207
248
 
208
249
  if stylesheet_full_path.is_dir():
209
- # NOTE: this can create an infinite loop, for example:
210
- # assets/
211
- # | dir_a/
212
- # | | dir_c/ (symlink to "assets/dir_a")
213
- # | dir_b/
214
- # so to avoid the infinite loop, we don't include symbolic links
215
- stylesheets += [
216
- str(p.relative_to(assets_app_path))
217
- for p in stylesheet_full_path.iterdir()
218
- if not (p.is_symlink() and p.is_dir())
219
- ]
220
- continue
221
-
222
- if (
223
- stylesheet_full_path.suffix[1:].lower()
224
- in constants.Reflex.STYLESHEETS_SUPPORTED
225
- ):
226
- target = (
227
- get_web_dir()
228
- / constants.Dirs.STYLES
229
- / (stylesheet.rsplit(".", 1)[0].strip("/") + ".css")
250
+ all_files = (
251
+ file
252
+ for ext in constants.Reflex.STYLESHEETS_SUPPORTED
253
+ for file in stylesheet_full_path.rglob("*." + ext)
230
254
  )
231
- target.parent.mkdir(parents=True, exist_ok=True)
232
-
233
- if stylesheet_full_path.suffix == ".css":
234
- path_ops.cp(src=stylesheet_full_path, dest=target, overwrite=True)
235
- else:
236
- try:
237
- from sass import compile as sass_compile
238
-
239
- target.write_text(
240
- data=sass_compile(
241
- filename=str(stylesheet_full_path),
242
- output_style="compressed",
243
- ),
244
- encoding="utf8",
245
- )
246
- except ImportError:
247
- failed_to_import_sass = True
255
+ for file in all_files:
256
+ if file.is_dir():
257
+ continue
258
+ # Validate the stylesheet.
259
+ _validate_stylesheet(file, assets_app_path)
260
+ stylesheets_files.append(file)
261
+
248
262
  else:
249
- raise FileNotFoundError(
250
- f'The stylesheet file "{stylesheet_full_path}" is not a valid file.'
251
- )
263
+ # Validate the stylesheet.
264
+ _validate_stylesheet(stylesheet_full_path, assets_app_path)
265
+ stylesheets_files.append(stylesheet_full_path)
266
+ else:
267
+ stylesheets_urls.append(stylesheet)
252
268
 
253
- stylesheet = f"./{stylesheet.rsplit('.', 1)[0].strip('/')}.css"
269
+ sheets.extend(dict.fromkeys(stylesheets_urls))
270
+
271
+ for stylesheet in stylesheets_files:
272
+ target_path = stylesheet.relative_to(assets_app_path).with_suffix(".css")
273
+ target = get_web_dir() / constants.Dirs.STYLES / target_path
274
+
275
+ target.parent.mkdir(parents=True, exist_ok=True)
276
+
277
+ if stylesheet.suffix == ".css":
278
+ path_ops.cp(src=stylesheet, dest=target, overwrite=True)
279
+ else:
280
+ try:
281
+ from sass import compile as sass_compile
282
+
283
+ target.write_text(
284
+ data=sass_compile(
285
+ filename=str(stylesheet),
286
+ output_style="compressed",
287
+ ),
288
+ encoding="utf8",
289
+ )
290
+ except ImportError:
291
+ failed_to_import_sass = True
254
292
 
255
- sheets.append(stylesheet) if stylesheet not in sheets else None
293
+ str_target_path = "./" + str(target_path)
294
+ sheets.append(str_target_path) if str_target_path not in sheets else None
256
295
 
257
296
  if failed_to_import_sass:
258
297
  console.error(
@@ -151,7 +151,7 @@ def cond(condition: Any, c1: Any, c2: Any = types.Unset(), /) -> Component | Var
151
151
  c1_var = Var.create(c1)
152
152
  c2_var = Var.create(c2)
153
153
 
154
- if condition is c1_var:
154
+ if c1_var is cond_var or c1_var.equals(cond_var):
155
155
  c1_var = c1_var.to(types.value_inside_optional(c1_var._var_type))
156
156
 
157
157
  # Create the conditional var.
@@ -55,6 +55,9 @@ class Foreach(Component):
55
55
  ForeachVarError: If the iterable is of type Any.
56
56
  TypeError: If the render function is a ComponentState.
57
57
  UntypedVarError: If the iterable is of type Any without a type annotation.
58
+
59
+ # noqa: DAR401 with_traceback
60
+ # noqa: DAR402 UntypedVarError
58
61
  """
59
62
  from reflex.vars import ArrayVar, ObjectVar, StringVar
60
63
 
@@ -107,7 +110,7 @@ class Foreach(Component):
107
110
  iterable,
108
111
  "foreach",
109
112
  "https://reflex.dev/docs/library/dynamic-rendering/foreach/",
110
- ) from e
113
+ ).with_traceback(e.__traceback__) from None
111
114
  return component
112
115
 
113
116
  def _render(self) -> IterTag:
@@ -572,6 +572,12 @@ class Select(BaseHTML):
572
572
  # Fired when the select value changes
573
573
  on_change: EventHandler[input_event]
574
574
 
575
+ # The controlled value of the select, read only unless used with on_change
576
+ value: Var[str]
577
+
578
+ # The default value of the select when initially rendered
579
+ default_value: Var[str]
580
+
575
581
 
576
582
  AUTO_HEIGHT_JS = """
577
583
  const autoHeightOnInput = (e, is_enabled) => {
@@ -3027,6 +3027,8 @@ class Select(BaseHTML):
3027
3027
  name: Var[str] | str | None = None,
3028
3028
  required: Var[bool] | bool | None = None,
3029
3029
  size: Var[int] | int | None = None,
3030
+ value: Var[str] | str | None = None,
3031
+ default_value: Var[str] | str | None = None,
3030
3032
  access_key: Var[str] | str | None = None,
3031
3033
  auto_capitalize: Literal[
3032
3034
  "characters", "none", "off", "on", "sentences", "words"
@@ -3246,6 +3248,8 @@ class Select(BaseHTML):
3246
3248
  required: Indicates that the select control must have a selected option
3247
3249
  size: Number of visible options in a drop-down list
3248
3250
  on_change: Fired when the select value changes
3251
+ value: The controlled value of the select, read only unless used with on_change
3252
+ default_value: The default value of the select when initially rendered
3249
3253
  access_key: Provides a hint for generating a keyboard shortcut for the current element.
3250
3254
  auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
3251
3255
  content_editable: Indicates whether the element's content is editable.
@@ -64,6 +64,7 @@ _SUBMOD_ATTRS: dict = {
64
64
  "ResponsiveContainer",
65
65
  "legend",
66
66
  "Legend",
67
+ "tooltip",
67
68
  "graphing_tooltip",
68
69
  "GraphingTooltip",
69
70
  "label",
@@ -65,6 +65,7 @@ from .general import label as label
65
65
  from .general import label_list as label_list
66
66
  from .general import legend as legend
67
67
  from .general import responsive_container as responsive_container
68
+ from .general import tooltip as tooltip
68
69
  from .polar import Pie as Pie
69
70
  from .polar import PolarAngleAxis as PolarAngleAxis
70
71
  from .polar import PolarGrid as PolarGrid
@@ -259,7 +259,7 @@ class Cell(Recharts):
259
259
 
260
260
  responsive_container = ResponsiveContainer.create
261
261
  legend = Legend.create
262
- graphing_tooltip = GraphingTooltip.create
262
+ graphing_tooltip = tooltip = GraphingTooltip.create
263
263
  label = Label.create
264
264
  label_list = LabelList.create
265
265
  cell = Cell.create
@@ -533,7 +533,7 @@ class Cell(Recharts):
533
533
 
534
534
  responsive_container = ResponsiveContainer.create
535
535
  legend = Legend.create
536
- graphing_tooltip = GraphingTooltip.create
536
+ graphing_tooltip = tooltip = GraphingTooltip.create
537
537
  label = Label.create
538
538
  label_list = LabelList.create
539
539
  cell = Cell.create
reflex/config.py CHANGED
@@ -604,12 +604,6 @@ class EnvironmentVariables:
604
604
  # Whether to use Granian for the backend. By default, the backend uses Uvicorn if available.
605
605
  REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False)
606
606
 
607
- # The username to use for authentication on python package repository. Username and password must both be provided.
608
- TWINE_USERNAME: EnvVar[str | None] = env_var(None)
609
-
610
- # The password to use for authentication on python package repository. Username and password must both be provided.
611
- TWINE_PASSWORD: EnvVar[str | None] = env_var(None)
612
-
613
607
  # Whether to use the system installed bun. If set to false, bun will be bundled with the app.
614
608
  REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False)
615
609
 
reflex/constants/base.py CHANGED
@@ -61,6 +61,18 @@ class Dirs(SimpleNamespace):
61
61
  UPLOAD_IS_USED = "upload_is_used"
62
62
 
63
63
 
64
+ def _reflex_version() -> str:
65
+ """Get the Reflex version.
66
+
67
+ Returns:
68
+ The Reflex version.
69
+ """
70
+ try:
71
+ return metadata.version("reflex")
72
+ except metadata.PackageNotFoundError:
73
+ return "unknown"
74
+
75
+
64
76
  class Reflex(SimpleNamespace):
65
77
  """Base constants concerning Reflex."""
66
78
 
@@ -68,7 +80,7 @@ class Reflex(SimpleNamespace):
68
80
  # The name of the Reflex package.
69
81
  MODULE_NAME = "reflex"
70
82
  # The current version of Reflex.
71
- VERSION = metadata.version(MODULE_NAME)
83
+ VERSION = _reflex_version()
72
84
 
73
85
  # The reflex json file.
74
86
  JSON = "reflex.json"
@@ -85,7 +85,7 @@ class PageNames(SimpleNamespace):
85
85
  # The name of the app root page.
86
86
  APP_ROOT = "_app"
87
87
  # The root stylesheet filename.
88
- STYLESHEET_ROOT = "styles"
88
+ STYLESHEET_ROOT = "__reflex_global_styles"
89
89
  # The name of the document root page.
90
90
  DOCUMENT_ROOT = "_document"
91
91
  # The name of the theme page.
@@ -14,10 +14,10 @@ class Bun(SimpleNamespace):
14
14
  """Bun constants."""
15
15
 
16
16
  # The Bun version.
17
- VERSION = "1.2.4"
17
+ VERSION = "1.2.8"
18
18
 
19
19
  # Min Bun Version
20
- MIN_VERSION = "1.2.4"
20
+ MIN_VERSION = "1.2.8"
21
21
 
22
22
  # URL to bun install script.
23
23
  INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh"
@@ -62,8 +62,6 @@ registry = "{registry}"
62
62
  class Node(SimpleNamespace):
63
63
  """Node/ NPM constants."""
64
64
 
65
- # The Node version.
66
- VERSION = "22.11.0"
67
65
  # The minimum required node version.
68
66
  MIN_VERSION = "18.18.0"
69
67