reflex 0.7.4a0__py3-none-any.whl → 0.7.4a1__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.

reflex/app.py CHANGED
@@ -576,6 +576,22 @@ class App(MiddlewareMixin, LifespanMixin):
576
576
  """
577
577
  if not self.api:
578
578
  raise ValueError("The app has not been initialized.")
579
+
580
+ # For py3.9 compatibility when redis is used, we MUST add any decorator pages
581
+ # before compiling the app in a thread to avoid event loop error (REF-2172).
582
+ self._apply_decorated_pages()
583
+
584
+ compile_future = concurrent.futures.ThreadPoolExecutor(max_workers=1).submit(
585
+ self._compile
586
+ )
587
+ compile_future.add_done_callback(
588
+ # Force background compile errors to print eagerly
589
+ lambda f: f.result()
590
+ )
591
+ # Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted.
592
+ if is_prod_mode():
593
+ compile_future.result()
594
+
579
595
  return self.api
580
596
 
581
597
  def _add_default_endpoints(self):
@@ -368,13 +368,11 @@ def _compile_stateful_components(
368
368
 
369
369
  # Include dynamic imports in the shared component.
370
370
  if dynamic_imports := component._get_all_dynamic_imports():
371
- rendered_components.update(
372
- {dynamic_import: None for dynamic_import in dynamic_imports}
373
- )
371
+ rendered_components.update(dict.fromkeys(dynamic_imports))
374
372
 
375
373
  # Include custom code in the shared component.
376
374
  rendered_components.update(
377
- {code: None for code in component._get_all_custom_code()},
375
+ dict.fromkeys(component._get_all_custom_code()),
378
376
  )
379
377
 
380
378
  # Include all imports in the shared component.
@@ -53,9 +53,9 @@ def wait_for_client_redirect(component: Component) -> Component:
53
53
  The conditionally rendered component.
54
54
  """
55
55
  return cond(
56
- condition=route_not_found,
57
- c1=component,
58
- c2=ClientSideRouting.create(),
56
+ route_not_found,
57
+ component,
58
+ ClientSideRouting.create(),
59
59
  )
60
60
 
61
61
 
@@ -31,7 +31,7 @@ class Cond(MemoizationLeaf):
31
31
  cls,
32
32
  cond: Var,
33
33
  comp1: BaseComponent,
34
- comp2: BaseComponent | None = None,
34
+ comp2: BaseComponent | types.Unset = types.Unset(),
35
35
  ) -> Component:
36
36
  """Create a conditional component.
37
37
 
@@ -44,10 +44,14 @@ class Cond(MemoizationLeaf):
44
44
  The conditional component.
45
45
  """
46
46
  # Wrap everything in fragments.
47
- if type(comp1).__name__ != "Fragment":
47
+ if type(comp1) is not Fragment:
48
48
  comp1 = Fragment.create(comp1)
49
- if comp2 is None or type(comp2).__name__ != "Fragment":
50
- comp2 = Fragment.create(comp2) if comp2 else Fragment.create()
49
+ if isinstance(comp2, types.Unset) or type(comp2) is not Fragment:
50
+ comp2 = (
51
+ Fragment.create(comp2)
52
+ if not isinstance(comp2, types.Unset)
53
+ else Fragment.create()
54
+ )
51
55
  return Fragment.create(
52
56
  cls._create(
53
57
  children=[comp1, comp2],
@@ -96,18 +100,22 @@ class Cond(MemoizationLeaf):
96
100
 
97
101
 
98
102
  @overload
99
- def cond(condition: Any, c1: Component, c2: Any) -> Component: ... # pyright: ignore [reportOverlappingOverload]
103
+ def cond(condition: Any, c1: Component, c2: Any, /) -> Component: ... # pyright: ignore [reportOverlappingOverload]
104
+
105
+
106
+ @overload
107
+ def cond(condition: Any, c1: Component, /) -> Component: ...
100
108
 
101
109
 
102
110
  @overload
103
- def cond(condition: Any, c1: Component) -> Component: ...
111
+ def cond(condition: Any, c1: Any, c2: Component, /) -> Component: ... # pyright: ignore [reportOverlappingOverload]
104
112
 
105
113
 
106
114
  @overload
107
- def cond(condition: Any, c1: Any, c2: Any) -> Var: ...
115
+ def cond(condition: Any, c1: Any, c2: Any, /) -> Var: ...
108
116
 
109
117
 
110
- def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
118
+ def cond(condition: Any, c1: Any, c2: Any = types.Unset(), /) -> Component | Var:
111
119
  """Create a conditional component or Prop.
112
120
 
113
121
  Args:
@@ -128,15 +136,15 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
128
136
 
129
137
  # If the first component is a component, create a Cond component.
130
138
  if isinstance(c1, BaseComponent):
131
- if c2 is not None and not isinstance(c2, BaseComponent):
132
- raise ValueError("Both arguments must be components.")
139
+ if not isinstance(c2, types.Unset) and not isinstance(c2, BaseComponent):
140
+ return Cond.create(cond_var.bool(), c1, Fragment.create(c2))
133
141
  return Cond.create(cond_var.bool(), c1, c2)
134
142
 
135
143
  # Otherwise, create a conditional Var.
136
144
  # Check that the second argument is valid.
137
145
  if isinstance(c2, BaseComponent):
138
- raise ValueError("Both arguments must be props.")
139
- if c2 is None:
146
+ return Cond.create(cond_var.bool(), Fragment.create(c1), c2)
147
+ if isinstance(c2, types.Unset):
140
148
  raise ValueError("For conditional vars, the second argument must be set.")
141
149
 
142
150
  # convert the truth and false cond parts into vars so the _var_data can be obtained.
@@ -72,13 +72,11 @@ def load_dynamic_serializer():
72
72
  rendered_components = {}
73
73
  # Include dynamic imports in the shared component.
74
74
  if dynamic_imports := component._get_all_dynamic_imports():
75
- rendered_components.update(
76
- {dynamic_import: None for dynamic_import in dynamic_imports}
77
- )
75
+ rendered_components.update(dict.fromkeys(dynamic_imports))
78
76
 
79
77
  # Include custom code in the shared component.
80
78
  rendered_components.update(
81
- {code: None for code in component._get_all_custom_code()},
79
+ dict.fromkeys(component._get_all_custom_code()),
82
80
  )
83
81
 
84
82
  rendered_components[
@@ -53,42 +53,35 @@ class Icon(LucideIconComponent):
53
53
  if "tag" not in props:
54
54
  raise AttributeError("Missing 'tag' keyword-argument for Icon")
55
55
 
56
- tag: str | Var | LiteralVar = Var.create(props.pop("tag"))
57
- if isinstance(tag, LiteralVar):
58
- if isinstance(tag, LiteralStringVar):
59
- tag = tag._var_value
56
+ tag_var: Var | LiteralVar = Var.create(props.pop("tag"))
57
+ if isinstance(tag_var, LiteralVar):
58
+ if isinstance(tag_var, LiteralStringVar):
59
+ tag = format.to_snake_case(tag_var._var_value.lower())
60
60
  else:
61
- raise TypeError(f"Icon name must be a string, got {type(tag)}")
62
- elif isinstance(tag, Var):
63
- tag_stringified = tag.guess_type()
61
+ raise TypeError(f"Icon name must be a string, got {type(tag_var)}")
62
+ elif isinstance(tag_var, Var):
63
+ tag_stringified = tag_var.guess_type()
64
64
  if not isinstance(tag_stringified, StringVar):
65
- raise TypeError(f"Icon name must be a string, got {tag._var_type}")
65
+ raise TypeError(f"Icon name must be a string, got {tag_var._var_type}")
66
66
  return DynamicIcon.create(name=tag_stringified.replace("_", "-"), **props)
67
67
 
68
- if (
69
- not isinstance(tag, str)
70
- or format.to_snake_case(tag) not in LUCIDE_ICON_LIST
71
- ):
72
- if isinstance(tag, str):
73
- icons_sorted = sorted(
74
- LUCIDE_ICON_LIST,
75
- key=lambda s, tag=tag: format.length_of_largest_common_substring(
76
- tag, s
77
- ),
78
- reverse=True,
79
- )
80
- else:
81
- icons_sorted = LUCIDE_ICON_LIST
68
+ if tag not in LUCIDE_ICON_LIST:
69
+ icons_sorted = sorted(
70
+ LUCIDE_ICON_LIST,
71
+ key=lambda s, tag=tag: format.length_of_largest_common_substring(
72
+ tag, s
73
+ ),
74
+ reverse=True,
75
+ )
82
76
  console.warn(
83
77
  f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(icons_sorted[0:10])}, ..."
84
78
  "\nSee full list at https://reflex.dev/docs/library/data-display/icon/#icons-list. Using 'circle-help' icon instead."
85
79
  )
86
- tag = "circle-help"
80
+ tag = "circle_help"
87
81
 
88
- if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
89
- props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
90
- else:
91
- props["tag"] = format.to_title_case(format.to_snake_case(tag)) + "Icon"
82
+ props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE.get(
83
+ tag, format.to_title_case(tag) + "Icon"
84
+ )
92
85
  props["alias"] = f"Lucide{props['tag']}"
93
86
  props.setdefault("color", "var(--current-color)")
94
87
  return super().create(**props)
reflex/config.py CHANGED
@@ -30,7 +30,6 @@ from typing import (
30
30
  )
31
31
 
32
32
  import pydantic.v1 as pydantic
33
- from reflex_cli.constants.hosting import Hosting
34
33
 
35
34
  from reflex import constants
36
35
  from reflex.base import Base
@@ -602,7 +601,7 @@ class EnvironmentVariables:
602
601
  # The npm registry to use.
603
602
  NPM_CONFIG_REGISTRY: EnvVar[str | None] = env_var(None)
604
603
 
605
- # Whether to use Granian for the backend. Otherwise, use Uvicorn.
604
+ # Whether to use Granian for the backend. By default, the backend uses Uvicorn if available.
606
605
  REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False)
607
606
 
608
607
  # The username to use for authentication on python package repository. Username and password must both be provided.
@@ -807,8 +806,8 @@ class Config(Base):
807
806
  # Tailwind config.
808
807
  tailwind: dict[str, Any] | None = {"plugins": ["@tailwindcss/typography"]}
809
808
 
810
- # Timeout when launching the gunicorn server. TODO(rename this to backend_timeout?)
811
- timeout: int = 120
809
+ # DEPRECATED. Timeout when launching the gunicorn server.
810
+ timeout: int | None = None
812
811
 
813
812
  # Whether to enable or disable nextJS gzip compression.
814
813
  next_compression: bool = True
@@ -819,22 +818,17 @@ class Config(Base):
819
818
  # Additional frontend packages to install.
820
819
  frontend_packages: list[str] = []
821
820
 
822
- # The hosting service backend URL.
823
- cp_backend_url: str = Hosting.HOSTING_SERVICE
824
- # The hosting service frontend URL.
825
- cp_web_url: str = Hosting.HOSTING_SERVICE_UI
826
-
827
- # The worker class used in production mode
821
+ # DEPRECATED. The worker class used in production mode
828
822
  gunicorn_worker_class: str = "uvicorn.workers.UvicornH11Worker"
829
823
 
830
- # Number of gunicorn workers from user
824
+ # DEPRECATED. Number of gunicorn workers from user
831
825
  gunicorn_workers: int | None = None
832
826
 
833
- # Number of requests before a worker is restarted; set to 0 to disable
834
- gunicorn_max_requests: int = 100
827
+ # DEPRECATED. Number of requests before a worker is restarted; set to 0 to disable
828
+ gunicorn_max_requests: int | None = None
835
829
 
836
- # Variance limit for max requests; gunicorn only
837
- gunicorn_max_requests_jitter: int = 25
830
+ # DEPRECATED. Variance limit for max requests; gunicorn only
831
+ gunicorn_max_requests_jitter: int | None = None
838
832
 
839
833
  # Indicate which type of state manager to use
840
834
  state_manager_mode: constants.StateManagerMode = constants.StateManagerMode.DISK
@@ -938,8 +932,14 @@ class Config(Base):
938
932
  """The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.0.1"`."""
939
933
  )
940
934
  else:
941
- # load env file if exists
942
- load_dotenv(env_file, override=True)
935
+ # load env files in reverse order if they exist
936
+ for env_file_path in [
937
+ Path(p)
938
+ for s in reversed(env_file.split(os.pathsep))
939
+ if (p := s.strip())
940
+ ]:
941
+ if env_file_path.exists():
942
+ load_dotenv(env_file_path, override=True)
943
943
 
944
944
  updated_values = {}
945
945
  # Iterate over the fields.
@@ -826,12 +826,19 @@ def _collect_details_for_gallery():
826
826
  Raises:
827
827
  Exit: If pyproject.toml file is ill-formed or the request to the backend services fails.
828
828
  """
829
+ import reflex_cli.constants
829
830
  from reflex_cli.utils import hosting
830
831
 
831
832
  console.rule("[bold]Authentication with Reflex Services")
832
833
  console.print("First let's log in to Reflex backend services.")
833
834
  access_token, _ = hosting.authenticated_token()
834
835
 
836
+ if not access_token:
837
+ console.error(
838
+ "Unable to authenticate with Reflex backend services. Make sure you are logged in."
839
+ )
840
+ raise typer.Exit(code=1)
841
+
835
842
  console.rule("[bold]Custom Component Information")
836
843
  params = {}
837
844
  package_name = None
@@ -845,10 +852,8 @@ def _collect_details_for_gallery():
845
852
  console.print(f"[ Custom component package name ] : {package_name}")
846
853
  params["package_name"] = package_name
847
854
 
848
- config = get_config()
849
-
850
855
  post_custom_components_gallery_endpoint = (
851
- f"{config.cp_backend_url}/custom-components/gallery"
856
+ f"{reflex_cli.constants.Hosting.HOSTING_SERVICE}/custom-components/gallery"
852
857
  )
853
858
 
854
859
  # Check the backend services if the user is allowed to update information of this package is already shared.
reflex/reflex.py CHANGED
@@ -7,13 +7,14 @@ from pathlib import Path
7
7
 
8
8
  import typer
9
9
  import typer.core
10
- from reflex_cli.v2.deployments import check_version, hosting_cli
10
+ from reflex_cli.v2.deployments import hosting_cli
11
11
 
12
12
  from reflex import constants
13
13
  from reflex.config import environment, get_config
14
14
  from reflex.custom_components.custom_components import custom_components_cli
15
15
  from reflex.state import reset_disk_state_manager
16
16
  from reflex.utils import console, redir, telemetry
17
+ from reflex.utils.exec import should_use_granian
17
18
 
18
19
  # Disable typer+rich integration for help panels
19
20
  typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
@@ -203,9 +204,19 @@ def _run(
203
204
 
204
205
  prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
205
206
 
206
- if frontend:
207
- # Get the app module.
208
- prerequisites.get_compiled_app()
207
+ # Get the app module.
208
+ app_task = prerequisites.compile_app if frontend else prerequisites.validate_app
209
+
210
+ # Granian fails if the app is already imported.
211
+ if should_use_granian():
212
+ import concurrent.futures
213
+
214
+ compile_future = concurrent.futures.ProcessPoolExecutor(max_workers=1).submit(
215
+ app_task
216
+ )
217
+ compile_future.result()
218
+ else:
219
+ app_task()
209
220
 
210
221
  # Warn if schema is not up to date.
211
222
  prerequisites.check_schema_up_to_date()
@@ -386,6 +397,7 @@ def export(
386
397
  def login(loglevel: constants.LogLevel | None = typer.Option(None)):
387
398
  """Authenticate with experimental Reflex hosting service."""
388
399
  from reflex_cli.v2 import cli as hosting_cli
400
+ from reflex_cli.v2.deployments import check_version
389
401
 
390
402
  loglevel = loglevel or get_config().loglevel
391
403
 
@@ -407,6 +419,7 @@ def logout(
407
419
  ):
408
420
  """Log out of access to Reflex hosting service."""
409
421
  from reflex_cli.v2.cli import logout
422
+ from reflex_cli.v2.deployments import check_version
410
423
 
411
424
  check_version()
412
425
 
@@ -567,6 +580,7 @@ def deploy(
567
580
  from reflex_cli.constants.base import LogLevel as HostingLogLevel
568
581
  from reflex_cli.utils import dependency
569
582
  from reflex_cli.v2 import cli as hosting_cli
583
+ from reflex_cli.v2.deployments import check_version
570
584
 
571
585
  from reflex.utils import export as export_utils
572
586
  from reflex.utils import prerequisites
reflex/state.py CHANGED
@@ -1671,6 +1671,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1671
1671
 
1672
1672
  raise TypeError(
1673
1673
  f"Your handler {handler.fn.__qualname__} must only return/yield: None, Events or other EventHandlers referenced by their class (i.e. using `type(self)` or other class references)."
1674
+ f" Returned events of types {', '.join(map(str, map(type, events)))!s}."
1674
1675
  )
1675
1676
 
1676
1677
  async def _as_state_update(
reflex/utils/exec.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import hashlib
6
+ import importlib.util
6
7
  import json
7
8
  import os
8
9
  import platform
@@ -19,6 +20,7 @@ from reflex import constants
19
20
  from reflex.config import environment, get_config
20
21
  from reflex.constants.base import LogLevel
21
22
  from reflex.utils import console, path_ops
23
+ from reflex.utils.decorator import once
22
24
  from reflex.utils.prerequisites import get_web_dir
23
25
 
24
26
  # For uvicorn windows bug fix (#2335)
@@ -185,13 +187,28 @@ def run_frontend_prod(root: Path, port: str, backend_present: bool = True):
185
187
  )
186
188
 
187
189
 
190
+ @once
191
+ def _warn_user_about_uvicorn():
192
+ console.warn(
193
+ "Using Uvicorn for backend as it is installed. This behavior will change in 0.8.0 to use Granian by default."
194
+ )
195
+
196
+
188
197
  def should_use_granian():
189
198
  """Whether to use Granian for backend.
190
199
 
191
200
  Returns:
192
201
  True if Granian should be used.
193
202
  """
194
- return environment.REFLEX_USE_GRANIAN.get()
203
+ if environment.REFLEX_USE_GRANIAN.get():
204
+ return True
205
+ if (
206
+ importlib.util.find_spec("uvicorn") is None
207
+ or importlib.util.find_spec("gunicorn") is None
208
+ ):
209
+ return True
210
+ _warn_user_about_uvicorn()
211
+ return False
195
212
 
196
213
 
197
214
  def get_app_module():
@@ -200,22 +217,9 @@ def get_app_module():
200
217
  Returns:
201
218
  The app module for the backend.
202
219
  """
203
- return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
204
-
205
-
206
- def get_granian_target():
207
- """Get the Granian target for the backend.
208
-
209
- Returns:
210
- The Granian target for the backend.
211
- """
212
- import reflex
213
-
214
- app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
220
+ config = get_config()
215
221
 
216
- return (
217
- f"{app_module_path!s}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
218
- )
222
+ return f"{config.module}:{constants.CompileVars.APP}"
219
223
 
220
224
 
221
225
  def run_backend(
@@ -317,7 +321,8 @@ def run_uvicorn_backend(host: str, port: int, loglevel: LogLevel):
317
321
  import uvicorn
318
322
 
319
323
  uvicorn.run(
320
- app=f"{get_app_module()}.{constants.CompileVars.API}",
324
+ app=f"{get_app_module()}",
325
+ factory=True,
321
326
  host=host,
322
327
  port=port,
323
328
  log_level=loglevel.value,
@@ -335,31 +340,37 @@ def run_granian_backend(host: str, port: int, loglevel: LogLevel):
335
340
  loglevel: The log level.
336
341
  """
337
342
  console.debug("Using Granian for backend")
338
- try:
339
- from granian.constants import Interfaces
340
- from granian.log import LogLevels
341
- from granian.server import Server as Granian
342
-
343
- Granian(
344
- target=get_granian_target(),
345
- address=host,
346
- port=port,
347
- interface=Interfaces.ASGI,
348
- log_level=LogLevels(loglevel.value),
349
- reload=True,
350
- reload_paths=get_reload_paths(),
351
- ).serve()
352
- except ImportError:
353
- console.error(
354
- 'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian[reload]>=1.6.0"`)'
355
- )
356
- os._exit(1)
357
343
 
344
+ from granian.constants import Interfaces
345
+ from granian.log import LogLevels
346
+ from granian.server import MPServer as Granian
358
347
 
348
+ Granian(
349
+ target=get_app_module(),
350
+ factory=True,
351
+ address=host,
352
+ port=port,
353
+ interface=Interfaces.ASGI,
354
+ log_level=LogLevels(loglevel.value),
355
+ reload=True,
356
+ reload_paths=get_reload_paths(),
357
+ ).serve()
358
+
359
+
360
+ @once
359
361
  def _get_backend_workers():
360
362
  from reflex.utils import processes
361
363
 
362
364
  config = get_config()
365
+
366
+ if config.gunicorn_workers is not None:
367
+ console.deprecate(
368
+ "config.gunicorn_workers",
369
+ reason="If you're using Granian, use GRANIAN_WORKERS instead.",
370
+ deprecation_version="0.7.4",
371
+ removal_version="0.8.0",
372
+ )
373
+
363
374
  return (
364
375
  processes.get_num_workers()
365
376
  if not config.gunicorn_workers
@@ -367,6 +378,51 @@ def _get_backend_workers():
367
378
  )
368
379
 
369
380
 
381
+ @once
382
+ def _get_backend_timeout():
383
+ config = get_config()
384
+
385
+ if config.timeout is not None:
386
+ console.deprecate(
387
+ "config.timeout",
388
+ reason="If you're using Granian, use GRANIAN_WORKERS_LIFETIME instead.",
389
+ deprecation_version="0.7.4",
390
+ removal_version="0.8.0",
391
+ )
392
+
393
+ return config.timeout
394
+
395
+
396
+ @once
397
+ def _get_backend_max_requests():
398
+ config = get_config()
399
+
400
+ if config.gunicorn_max_requests is not None:
401
+ console.deprecate(
402
+ "config.gunicorn_max_requests",
403
+ reason="",
404
+ deprecation_version="0.7.4",
405
+ removal_version="0.8.0",
406
+ )
407
+
408
+ return config.gunicorn_max_requests
409
+
410
+
411
+ @once
412
+ def _get_backend_max_requests_jitter():
413
+ config = get_config()
414
+
415
+ if config.gunicorn_max_requests_jitter is not None:
416
+ console.deprecate(
417
+ "config.gunicorn_max_requests_jitter",
418
+ reason="",
419
+ deprecation_version="0.7.4",
420
+ removal_version="0.8.0",
421
+ )
422
+
423
+ return config.gunicorn_max_requests_jitter
424
+
425
+
370
426
  def run_backend_prod(
371
427
  host: str,
372
428
  port: int,
@@ -408,17 +464,25 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
408
464
  [
409
465
  "uvicorn",
410
466
  *(
411
- [
467
+ (
412
468
  "--limit-max-requests",
413
- str(config.gunicorn_max_requests),
414
- ]
415
- if config.gunicorn_max_requests > 0
416
- else []
469
+ str(max_requessts),
470
+ )
471
+ if (
472
+ (max_requessts := _get_backend_max_requests()) is not None
473
+ and max_requessts > 0
474
+ )
475
+ else ()
476
+ ),
477
+ *(
478
+ ("--timeout-keep-alive", str(timeout))
479
+ if (timeout := _get_backend_timeout()) is not None
480
+ else ()
417
481
  ),
418
- *("--timeout-keep-alive", str(config.timeout)),
419
482
  *("--host", host),
420
483
  *("--port", str(port)),
421
484
  *("--workers", str(_get_backend_workers())),
485
+ "--factory",
422
486
  app_module,
423
487
  ]
424
488
  if constants.IS_WINDOWS
@@ -426,17 +490,34 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
426
490
  "gunicorn",
427
491
  *("--worker-class", config.gunicorn_worker_class),
428
492
  *(
429
- [
493
+ (
430
494
  "--max-requests",
431
- str(config.gunicorn_max_requests),
495
+ str(max_requessts),
496
+ )
497
+ if (
498
+ (max_requessts := _get_backend_max_requests()) is not None
499
+ and max_requessts > 0
500
+ )
501
+ else ()
502
+ ),
503
+ *(
504
+ (
432
505
  "--max-requests-jitter",
433
- str(config.gunicorn_max_requests_jitter),
434
- ]
435
- if config.gunicorn_max_requests > 0
436
- else []
506
+ str(max_requessts_jitter),
507
+ )
508
+ if (
509
+ (max_requessts_jitter := _get_backend_max_requests_jitter())
510
+ is not None
511
+ and max_requessts_jitter > 0
512
+ )
513
+ else ()
437
514
  ),
438
515
  "--preload",
439
- *("--timeout", str(config.timeout)),
516
+ *(
517
+ ("--timeout", str(timeout))
518
+ if (timeout := _get_backend_timeout()) is not None
519
+ else ()
520
+ ),
440
521
  *("--bind", f"{host}:{port}"),
441
522
  *("--threads", str(_get_backend_workers())),
442
523
  f"{app_module}()",
@@ -472,17 +553,12 @@ def run_granian_backend_prod(host: str, port: int, loglevel: LogLevel):
472
553
 
473
554
  command = [
474
555
  "granian",
475
- "--workers",
476
- str(_get_backend_workers()),
477
- "--log-level",
478
- "critical",
479
- "--host",
480
- host,
481
- "--port",
482
- str(port),
483
- "--interface",
484
- str(Interfaces.ASGI),
485
- get_granian_target(),
556
+ *("--workers", str(_get_backend_workers())),
557
+ *("--log-level", "critical"),
558
+ *("--host", host),
559
+ *("--port", str(port)),
560
+ *("--interface", str(Interfaces.ASGI)),
561
+ *("--factory", get_app_module()),
486
562
  ]
487
563
  processes.new_process(
488
564
  command,
@@ -412,6 +412,15 @@ def get_and_validate_app(reload: bool = False) -> AppInfo:
412
412
  return AppInfo(app=app, module=app_module)
413
413
 
414
414
 
415
+ def validate_app(reload: bool = False) -> None:
416
+ """Validate the app instance based on the default config.
417
+
418
+ Args:
419
+ reload: Re-import the app module from disk
420
+ """
421
+ get_and_validate_app(reload=reload)
422
+
423
+
415
424
  def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
416
425
  """Get the app module based on the default config after first compiling it.
417
426
 
@@ -430,6 +439,16 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
430
439
  return app_module
431
440
 
432
441
 
442
+ def compile_app(reload: bool = False, export: bool = False) -> None:
443
+ """Compile the app module based on the default config.
444
+
445
+ Args:
446
+ reload: Re-import the app module from disk
447
+ export: Compile the app for export
448
+ """
449
+ get_compiled_app(reload=reload, export=export)
450
+
451
+
433
452
  def get_redis() -> Redis | None:
434
453
  """Get the asynchronous redis client.
435
454
 
@@ -1542,7 +1561,7 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1542
1561
  console.error(f"Failed to unzip the template: {uze}")
1543
1562
  raise typer.Exit(1) from uze
1544
1563
 
1545
- if len(subdirs := os.listdir(unzip_dir)) != 1:
1564
+ if len(subdirs := list(unzip_dir.iterdir())) != 1:
1546
1565
  console.error(f"Expected one directory in the zip, found {subdirs}")
1547
1566
  raise typer.Exit(1)
1548
1567
 
reflex/utils/processes.py CHANGED
@@ -10,7 +10,7 @@ import signal
10
10
  import subprocess
11
11
  from concurrent import futures
12
12
  from pathlib import Path
13
- from typing import Callable, Generator, Sequence, Tuple
13
+ from typing import Any, Callable, Generator, Sequence, Tuple
14
14
 
15
15
  import psutil
16
16
  import typer
@@ -197,7 +197,7 @@ def new_process(
197
197
 
198
198
  @contextlib.contextmanager
199
199
  def run_concurrently_context(
200
- *fns: Callable | Tuple,
200
+ *fns: Callable[..., Any] | tuple[Callable[..., Any], ...],
201
201
  ) -> Generator[list[futures.Future], None, None]:
202
202
  """Run functions concurrently in a thread pool.
203
203
 
@@ -213,14 +213,14 @@ def run_concurrently_context(
213
213
  return
214
214
 
215
215
  # Convert the functions to tuples.
216
- fns = [fn if isinstance(fn, tuple) else (fn,) for fn in fns] # pyright: ignore [reportAssignmentType]
216
+ fns = tuple(fn if isinstance(fn, tuple) else (fn,) for fn in fns)
217
217
 
218
218
  # Run the functions concurrently.
219
219
  executor = None
220
220
  try:
221
221
  executor = futures.ThreadPoolExecutor(max_workers=len(fns))
222
222
  # Submit the tasks.
223
- tasks = [executor.submit(*fn) for fn in fns] # pyright: ignore [reportArgumentType]
223
+ tasks = [executor.submit(*fn) for fn in fns]
224
224
 
225
225
  # Yield control back to the main thread while tasks are running.
226
226
  yield tasks
reflex/vars/base.py CHANGED
@@ -160,7 +160,7 @@ class VarData:
160
160
  if isinstance(hooks, str):
161
161
  hooks = [hooks]
162
162
  if not isinstance(hooks, dict):
163
- hooks = {hook: None for hook in (hooks or [])}
163
+ hooks = dict.fromkeys(hooks or [])
164
164
  immutable_imports: ImmutableParsedImportDict = tuple(
165
165
  (k, tuple(v)) for k, v in parse_imports(imports or {}).items()
166
166
  )
@@ -1791,8 +1791,7 @@ class cached_property: # noqa: N801
1791
1791
  if original_del is not None:
1792
1792
  original_del(this)
1793
1793
  return
1794
- if unique_id in GLOBAL_CACHE:
1795
- del GLOBAL_CACHE[unique_id]
1794
+ GLOBAL_CACHE.pop(unique_id, None)
1796
1795
 
1797
1796
  if original_del is not None:
1798
1797
  original_del(this)
reflex/vars/sequence.py CHANGED
@@ -1683,6 +1683,8 @@ def _determine_value_of_array_index(
1683
1683
  if t is not type(None)
1684
1684
  ]
1685
1685
  )
1686
+ if origin_var_type is range:
1687
+ return int
1686
1688
  if origin_var_type in [
1687
1689
  Sequence,
1688
1690
  Iterable,
@@ -1974,3 +1976,85 @@ class LiteralColorVar(CachedVarOperation, LiteralVar, ColorVar):
1974
1976
  ):
1975
1977
  raise TypeError("Color is not a valid color.")
1976
1978
  return f"var(--{color}-{'a' if alpha else ''}{shade})"
1979
+
1980
+
1981
+ class RangeVar(ArrayVar[Sequence[int]], python_types=range):
1982
+ """Base class for immutable range vars."""
1983
+
1984
+
1985
+ @dataclasses.dataclass(
1986
+ eq=False,
1987
+ frozen=True,
1988
+ slots=True,
1989
+ )
1990
+ class LiteralRangeVar(CachedVarOperation, LiteralVar, RangeVar):
1991
+ """Base class for immutable literal range vars."""
1992
+
1993
+ _var_value: range = dataclasses.field(default_factory=lambda: range(0))
1994
+
1995
+ @classmethod
1996
+ def create(
1997
+ cls,
1998
+ value: range,
1999
+ _var_type: Type[range] | None = None,
2000
+ _var_data: VarData | None = None,
2001
+ ) -> RangeVar:
2002
+ """Create a var from a string value.
2003
+
2004
+ Args:
2005
+ value: The value to create the var from.
2006
+ _var_type: The type of the var.
2007
+ _var_data: Additional hooks and imports associated with the Var.
2008
+
2009
+ Returns:
2010
+ The var.
2011
+ """
2012
+ return cls(
2013
+ _js_expr="",
2014
+ _var_type=_var_type or range,
2015
+ _var_data=_var_data,
2016
+ _var_value=value,
2017
+ )
2018
+
2019
+ def __hash__(self) -> int:
2020
+ """Get the hash of the var.
2021
+
2022
+ Returns:
2023
+ The hash of the var.
2024
+ """
2025
+ return hash(
2026
+ (
2027
+ self.__class__.__name__,
2028
+ self._var_value.start,
2029
+ self._var_value.stop,
2030
+ self._var_value.step,
2031
+ )
2032
+ )
2033
+
2034
+ @cached_property_no_lock
2035
+ def _cached_var_name(self) -> str:
2036
+ """The name of the var.
2037
+
2038
+ Returns:
2039
+ The name of the var.
2040
+ """
2041
+ return f"Array.from({{ length: Math.ceil(({self._var_value.stop!s} - {self._var_value.start!s}) / {self._var_value.step!s}) }}, (_, i) => {self._var_value.start!s} + i * {self._var_value.step!s})"
2042
+
2043
+ @cached_property_no_lock
2044
+ def _cached_get_all_var_data(self) -> VarData | None:
2045
+ """Get all the var data.
2046
+
2047
+ Returns:
2048
+ The var data.
2049
+ """
2050
+ return self._var_data
2051
+
2052
+ def json(self) -> str:
2053
+ """Get the JSON representation of the var.
2054
+
2055
+ Returns:
2056
+ The JSON representation of the var.
2057
+ """
2058
+ return json.dumps(
2059
+ list(self._var_value),
2060
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.7.4a0
3
+ Version: 0.7.4a1
4
4
  Summary: Web apps in pure Python.
5
5
  Project-URL: homepage, https://reflex.dev
6
6
  Project-URL: repository, https://github.com/reflex-dev/reflex
@@ -23,7 +23,7 @@ Requires-Dist: build<2.0,>=1.0.3
23
23
  Requires-Dist: charset-normalizer<4.0,>=3.3.2
24
24
  Requires-Dist: distro<2.0,>=1.8.0; platform_system == 'Linux'
25
25
  Requires-Dist: fastapi!=0.111.0,!=0.111.1,>=0.96.0
26
- Requires-Dist: gunicorn<24.0,>=20.1.0
26
+ Requires-Dist: granian[reload]>=2.0.0
27
27
  Requires-Dist: httpx<1.0,>=0.25.1
28
28
  Requires-Dist: jinja2<4.0,>=3.1.2
29
29
  Requires-Dist: lazy-loader>=0.4
@@ -44,7 +44,6 @@ Requires-Dist: tomlkit<1.0,>=0.12.4
44
44
  Requires-Dist: twine<7.0,>=4.0.0
45
45
  Requires-Dist: typer<1.0,>=0.15.1
46
46
  Requires-Dist: typing-extensions>=4.6.0
47
- Requires-Dist: uvicorn>=0.20.0
48
47
  Requires-Dist: wheel<1.0,>=0.42.0
49
48
  Requires-Dist: wrapt<2.0,>=1.17.0
50
49
  Description-Content-Type: text/markdown
@@ -60,6 +59,7 @@ Description-Content-Type: text/markdown
60
59
  [![PyPI version](https://badge.fury.io/py/reflex.svg)](https://badge.fury.io/py/reflex)
61
60
  ![versions](https://img.shields.io/pypi/pyversions/reflex.svg)
62
61
  [![Documentation](https://img.shields.io/badge/Documentation%20-Introduction%20-%20%23007ec6)](https://reflex.dev/docs/getting-started/introduction)
62
+ [![PyPI Downloads](https://static.pepy.tech/badge/reflex)](https://pepy.tech/projects/reflex)
63
63
  [![Discord](https://img.shields.io/discord/1029853095527727165?color=%237289da&label=Discord)](https://discord.gg/T5WSbC2YtQ)
64
64
 
65
65
  </div>
@@ -2,18 +2,17 @@ reflex/__init__.py,sha256=64HB9b6MKesl3Yv6aZMsozdMKKpgnxirKk-aeN45UYY,10341
2
2
  reflex/__init__.pyi,sha256=j4ZkO-mKKw5dFBhJVbaOg7AlncO-JCckV2cHENPiLG0,11303
3
3
  reflex/__main__.py,sha256=6cVrGEyT3j3tEvlEVUatpaYfbB5EF3UVY-6vc_Z7-hw,108
4
4
  reflex/admin.py,sha256=wu_vYqB0rU2njYBJSI0XZgVEkAFVZNQNUkUUXrlFbZc,343
5
- reflex/app.py,sha256=okGLgq549WgvKtjyOg2QwKwp95ORwLKqWyeBDCJSKJ4,68740
6
- reflex/app_module_for_backend.py,sha256=iuEYcJNRai59vReNUIZgixtYlFHYcYp_LNFB9DPQnKs,1134
5
+ reflex/app.py,sha256=JCh2PGvwfTpYSfZJeMRANGT-6NdWw718gAFWX-hOq_U,69385
7
6
  reflex/assets.py,sha256=PLTKAMYPKMZq8eWXKX8uco6NZ9IiPGWal0bOPLUmU7k,3364
8
7
  reflex/base.py,sha256=T1sY7SJJOpTsOveEiFxp-K39EoIQtRLgqdFZhsdM0DE,3976
9
- reflex/config.py,sha256=SQmFoj5DvVrXmWZ2IbXhzQ41MdL2qgmJ2LTKzDPkHjY,34980
8
+ reflex/config.py,sha256=LWl45MJv-fh4fMr-cpQWffO0HgQU-UiMEeqiqJC_Gto,35100
10
9
  reflex/event.py,sha256=EX-9X-c8gIudZjRDG8qSrVAbegcaGkYXxLLRWg-7IOA,60758
11
10
  reflex/model.py,sha256=k6qCweATPW1YRB_qcHwa5X35btJmtIlB4zEQ63FaW3w,17527
12
11
  reflex/page.py,sha256=qEt8n5EtawSywCzdsiaNQJWhC8ie-vg8ig0JGuVavPI,2386
13
12
  reflex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- reflex/reflex.py,sha256=gmYVXXASlx_4kpyYuqEASS2qNdG2lWbmVpNQ_LYeOJQ,20399
13
+ reflex/reflex.py,sha256=BM9oQ4VaobBa__BLgf38DJ2BYhTdobTRDZCpaZkOBHk,20920
15
14
  reflex/route.py,sha256=nn_hJwtQdjiqH_dHXfqMGWKllnyPQZTSR-KWdHDhoOs,4210
16
- reflex/state.py,sha256=sBMo_mMdzgQRlbD9U5RyqG77m96PYsIjcJcBVuMUMwc,141801
15
+ reflex/state.py,sha256=XBryDsRkXVLxYYiU_TL2B_riU7FySJsg3aU0ywUSWJs,141886
17
16
  reflex/style.py,sha256=dilXPn8de80NzsXT53GPJrmjELC5nPYIlCgongyq1zM,13145
18
17
  reflex/testing.py,sha256=IpjUHBNOJDAQtu6HnazfWIacEO5cIJvyHlCr6dYpr38,35585
19
18
  reflex/.templates/apps/blank/assets/favicon.ico,sha256=baxxgDAQ2V4-G5Q4S2yK5uUJTUGkv-AOWBQ0xd6myUo,4286
@@ -59,13 +58,13 @@ reflex/app_mixins/lifespan.py,sha256=fwtaa9NnyENdDa8_RUHUsT8L9qnZKxcpL-mEGLTGldo
59
58
  reflex/app_mixins/middleware.py,sha256=_B33NZdbuI16IUPTENyfCmEfJ6OxFHm6rObl3JdIs-Y,3286
60
59
  reflex/app_mixins/mixin.py,sha256=si0Pa0U1EtJc-a6iZntqU9B7_NrPILwrGFxk9mKHBCE,317
61
60
  reflex/compiler/__init__.py,sha256=r8jqmDSFf09iV2lHlNhfc9XrTLjNxfDNwPYlxS4cmHE,27
62
- reflex/compiler/compiler.py,sha256=zuEAhMw-0dXIb54hLhYEWLVkbhvgE2E_zCga3oSPPwI,25948
61
+ reflex/compiler/compiler.py,sha256=Dr7eitYfwfJlcun7v88E7FEELAnkbKp8ja4-r41_6co,25870
63
62
  reflex/compiler/templates.py,sha256=NX3YUMVGGyDsy2JuDv-AmklMM0pKJHLPsIpdqamgqRQ,5854
64
63
  reflex/compiler/utils.py,sha256=w8KcAXneXQxPOSei7BvAfA9MzR4jQWjDlxm8ahN1UM0,16083
65
64
  reflex/components/__init__.py,sha256=zbIXThv1WPI0FdIGf9G9RAmGoCRoGy7nHcSZ8K5D5bA,624
66
65
  reflex/components/__init__.pyi,sha256=qoj1zIWaitcZOGcJ6k7wuGJk_GAJCE9Xtx8CeRVrvoE,861
67
66
  reflex/components/component.py,sha256=ksc0Vc3hgZkTEyDZksarETCrjA-pvDMnd7T11F01ecg,88766
68
- reflex/components/dynamic.py,sha256=W-jNbNzll1w6YoMcoF-LMjHqP4UtoQHPV-NamXs-eAY,7220
67
+ reflex/components/dynamic.py,sha256=DdlFbtciytsEbVdFHm-obmE4FFkSR6x2DH0BzYI_4C4,7150
69
68
  reflex/components/literals.py,sha256=hogLnwTJxFJODIvqihg-GD9kFZVsEBDoYzaRit56Nuk,501
70
69
  reflex/components/props.py,sha256=8F2ZNeF16BDiTh-E4F-U_vks41BMJgmkTM7xbjGvfOA,2593
71
70
  reflex/components/base/__init__.py,sha256=QIOxOPT87WrSE4TSHAsZ-358VzvUXAe1w8vWogQ3Uuo,730
@@ -98,12 +97,12 @@ reflex/components/core/auto_scroll.pyi,sha256=4oTNr6ixo5D0rzmIQVXGCEUpHSSa7KdrkW
98
97
  reflex/components/core/banner.py,sha256=OB8HlnytknFQotVoBNsbCHxE6_3W96JCCtvxS6R3QKU,18473
99
98
  reflex/components/core/banner.pyi,sha256=owB8dbiHh2YMeJsggCM5zQ3kpfIcGXoxTWQdscakpwQ,24586
100
99
  reflex/components/core/breakpoints.py,sha256=fDtfDoZqJnAOnBvpp0640FCKbuMyC9dVoSf0-RE7n6Y,2756
101
- reflex/components/core/client_side_routing.py,sha256=z2WD2jT9U-xDOyHTLjCs0mf5HkwSEJpMmUeXzl4S7ZU,1897
100
+ reflex/components/core/client_side_routing.py,sha256=CgW29H4Kiy5V4AKdJlFT9gSWaFUfki3f3Prf9twbbqA,1881
102
101
  reflex/components/core/client_side_routing.pyi,sha256=XODI3H05ccxhAo-_SvFJjKFxTAkWbRsA-wmTe9uBxRo,4182
103
102
  reflex/components/core/clipboard.py,sha256=WH2pagKO0H5G7BaIT1kChRzrMV-aP5ENv1lIKbkRzJ8,3354
104
103
  reflex/components/core/clipboard.pyi,sha256=PiCieOSgQWwN5M7uHl11_kSqUXBN8J3glred4TH-yiQ,3041
105
104
  reflex/components/core/colors.py,sha256=-hzVGLEq3TiqroqzMi_YzGBCPXMvkNsen3pS_NzIQNk,590
106
- reflex/components/core/cond.py,sha256=Pa9kbunY5Gq000yPXxMex0zsUeAiffDsUrqSIFVLoqs,5372
105
+ reflex/components/core/cond.py,sha256=j5V-CGjoB6W_Ch0blQYzLuepjLqtX4vXb8e6qvEL_3I,5685
107
106
  reflex/components/core/debounce.py,sha256=qZsnu-7xfxz3NJS4-UnA_2YQz2P8SznJyuwZz98nEwE,4961
108
107
  reflex/components/core/debounce.pyi,sha256=QjyPCR1eVGxTdDFnFPD9Ir9QbJLk-2xU8f-hSMZHcfU,2883
109
108
  reflex/components/core/foreach.py,sha256=s2wgxcgEBc_8PfTKolDjbRz1pgdKZCdq8tqk87t3QVQ,5827
@@ -159,7 +158,7 @@ reflex/components/gridjs/__init__.py,sha256=xJwDm1AZ70L5-t9LLqZwGUtDpijbf1KuMYDT
159
158
  reflex/components/gridjs/datatable.py,sha256=Q2P2lFbPEujVa6bfetV0w4Oc81APX9YgVKGnKCvhSxY,4197
160
159
  reflex/components/gridjs/datatable.pyi,sha256=zVZTSTTQJY5jjrCTnyah3XM6aySyhq_mbhBWnbFg6Io,4859
161
160
  reflex/components/lucide/__init__.py,sha256=EggTK2MuQKQeOBLKW-mF0VaDK9zdWBImu1HO2dvHZbE,73
162
- reflex/components/lucide/icon.py,sha256=jwwQGyoUXItWh991O2l4Jh2j9Caocmt5J0KXJwdaZ3o,33849
161
+ reflex/components/lucide/icon.py,sha256=lRmzrsoT2_zvC8mKAZx0b1SJW_6ebEmRjWop2ggGHwM,33600
163
162
  reflex/components/lucide/icon.pyi,sha256=DZkrFkUSq-BT23taozAIDfPuHAF3V9AsVYwaEeOCgm0,35884
164
163
  reflex/components/markdown/__init__.py,sha256=Dfl1At5uYoY7H4ufZU_RY2KOGQDLtj75dsZ2BTqqAns,87
165
164
  reflex/components/markdown/markdown.py,sha256=9VoxHQNZe44pVDoT_2b897dTn7vcel7oglBF-iTu0aM,16186
@@ -352,7 +351,7 @@ reflex/constants/state.py,sha256=6Mfr7xVcAZOj5aSy7kp0W6r8oTs7K30URgGDAAFLfPQ,294
352
351
  reflex/constants/style.py,sha256=EPgRYHhAlcrPUBc2HkDTdTj-Q0uDAXHlq8Sp6D35Zf4,475
353
352
  reflex/constants/utils.py,sha256=GJhFj1uba54CDPEm70tWs8B5iS2siHgeNi--oGCjeRc,759
354
353
  reflex/custom_components/__init__.py,sha256=R4zsvOi4dfPmHc18KEphohXnQFBPnUCb50cMR5hSLDE,36
355
- reflex/custom_components/custom_components.py,sha256=62mG6tCj6nt6t_UYFu8Vcjsv7PiqABiJRQ-xsYGYsX0,33376
354
+ reflex/custom_components/custom_components.py,sha256=Ho3W97wfStsEVh6BuTEORhc3bd4UoCYzY3yB4YdVBUI,33592
356
355
  reflex/experimental/__init__.py,sha256=bvJ6qFeO3xT3L-8IBtk4ecoi5rda3EDvblgNP60yhEo,2206
357
356
  reflex/experimental/client_state.py,sha256=p_Toz94fNQGMbHY1WlwfQ-i_M01f1ExA9t1iokSvdLc,9880
358
357
  reflex/experimental/hooks.py,sha256=CHYGrAE5t8riltrJmDFgJ4D2Vhmhw-y3B3MSGNlOQow,2366
@@ -375,15 +374,15 @@ reflex/utils/compat.py,sha256=aSJH_M6iomgHPQ4onQ153xh1MWqPi3HSYDzE68N6gZM,2635
375
374
  reflex/utils/console.py,sha256=slDTb_5QXfSvdflFiwJauc874R2zqo8BOSh37JUhYLY,9469
376
375
  reflex/utils/decorator.py,sha256=EYdAjPdfgFjqjYSmLlc9qzOYnoihzavG5T4tgVgzsw4,1171
377
376
  reflex/utils/exceptions.py,sha256=Wwu7Ji2xgq521bJKtU2NgjwhmFfnG8erirEVN2h8S-g,8884
378
- reflex/utils/exec.py,sha256=-Y60GJ5R2UzJDS-r5qq3FMszqWf1Zu25jSnmyEc7No4,17155
377
+ reflex/utils/exec.py,sha256=YVYsUYNKSm2uYVGRLNOhC-zQMW7NrGjLmRxmzpTnVfk,19054
379
378
  reflex/utils/export.py,sha256=bcJA0L8lBbjij-5PU93ka2c1d_yJqrIurp5u4mN5f68,2537
380
379
  reflex/utils/format.py,sha256=a8em_yzqp9pLTrPXRsdzFWSO1qL2x25BpJXOf9DV1t8,20638
381
380
  reflex/utils/imports.py,sha256=-EkUt9y5U3qmImjfpsXwYh7JI9qJHd_L6X9y12EPJew,3921
382
381
  reflex/utils/lazy_loader.py,sha256=-3DcwIqHNft2fb1ikgDYAMiEwNfbiWfrTBAf1gEVX2o,1367
383
382
  reflex/utils/net.py,sha256=0Yd9OLK8R_px2sqnqrDkTky6hYHtG2pEDvvilOjDfjc,1219
384
383
  reflex/utils/path_ops.py,sha256=idGxUSJRKwYLLi7ppXkq3eV6rvAytJoO-n-FuLkwl3o,7604
385
- reflex/utils/prerequisites.py,sha256=GqpzAu-RAtInXzsGPBIoOYfiJwa-Oi4STUMbt0A_J-Y,61006
386
- reflex/utils/processes.py,sha256=aLdE5YzMYRuAhuSsoRWVT7yMlQczReNvNx-ItLKTnSY,13575
384
+ reflex/utils/prerequisites.py,sha256=_lgbA4udXoTPgBUYaSRW3zqyHJ3jrObgS7GXrQvgf3M,61526
385
+ reflex/utils/processes.py,sha256=B5F8m1smqGCvvwcGBO9poqQddfNBLy7uafO0J0PDcMo,13538
387
386
  reflex/utils/pyi_generator.py,sha256=cKdssbtAtGj2deOSDos9OF96w10qte8JM-TlfbzSdtw,41602
388
387
  reflex/utils/redir.py,sha256=kTqY2WSouF5_ftOe5bnvPEyU3SLpg3pcysTcxFH1UxI,1505
389
388
  reflex/utils/registry.py,sha256=6DPfYc64GodbhwdAZ113_zBsvMNdbFTIJ94qDH1N6wc,1410
@@ -391,15 +390,15 @@ reflex/utils/serializers.py,sha256=K8-erpNIjJNIKif0cDFExa9f5DEVuQUq0j5v5VH6aBI,1
391
390
  reflex/utils/telemetry.py,sha256=qwJBwjdtAV-OGKgO4h-NWhgTvfC3gbduBdn1UB8Ikes,5608
392
391
  reflex/utils/types.py,sha256=nGX44Q_Jp33wIaxf2vxANwBWe1743V2B8RRS8H9yV4c,33449
393
392
  reflex/vars/__init__.py,sha256=2Kv6Oh9g3ISZFESjL1al8KiO7QBZUXmLKGMCBsP-DoY,1243
394
- reflex/vars/base.py,sha256=ugAwqusvYHT3FfPsCsXkxxNgvtd9SQH-hAwqjQqEGcY,101926
393
+ reflex/vars/base.py,sha256=EjerSG-V8BwXhAcB___I0jNA_0T9t81EUxff7LpjyFg,101870
395
394
  reflex/vars/datetime.py,sha256=WOEzQF6qjMjYvCat80XxgB_4hmVNHwIIZNMBSmfu0PM,5790
396
395
  reflex/vars/dep_tracking.py,sha256=kluvF4Pfbpdqf0GcpmYHjT1yP-D1erAzaSQP6qIxjB0,13846
397
396
  reflex/vars/function.py,sha256=2sVnhgetPSwtor8VFtAiYJdzZ9IRNzAKdsUJG6dXQcE,14461
398
397
  reflex/vars/number.py,sha256=hacFEtv-63tMQN-oeVDWkrLFQL4utYG-b3ahYTb8b4M,29573
399
398
  reflex/vars/object.py,sha256=-fGqHThozjxAAuQL-wTwEItPiFI-ps53P2bKoSlW_As,17081
400
- reflex/vars/sequence.py,sha256=wxyiTV-_-N6FJNufIatubsnhEpXHFShcw3GEEEpYy3M,55893
401
- reflex-0.7.4a0.dist-info/METADATA,sha256=HUqbkhffmwIj_1bWqiE0sPZMbIFHRtLOFTjV7-JOT5Y,12090
402
- reflex-0.7.4a0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
403
- reflex-0.7.4a0.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
404
- reflex-0.7.4a0.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
405
- reflex-0.7.4a0.dist-info/RECORD,,
399
+ reflex/vars/sequence.py,sha256=PzFF1SC6r9iONV3t3nh5GKmToCTEm4B2GL3zGcNerl4,58099
400
+ reflex-0.7.4a1.dist-info/METADATA,sha256=Rl6X3yhbKBHSA5mBr88GK3STyGTmV3PSfO4RN78fcyc,12153
401
+ reflex-0.7.4a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
402
+ reflex-0.7.4a1.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
403
+ reflex-0.7.4a1.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
404
+ reflex-0.7.4a1.dist-info/RECORD,,
@@ -1,33 +0,0 @@
1
- """Shims the real reflex app module for running backend server (uvicorn or gunicorn).
2
- Only the app attribute is explicitly exposed.
3
- """
4
-
5
- from concurrent.futures import ThreadPoolExecutor
6
-
7
- from reflex import constants
8
- from reflex.utils.exec import is_prod_mode
9
- from reflex.utils.prerequisites import get_and_validate_app
10
-
11
- if constants.CompileVars.APP != "app":
12
- raise AssertionError("unexpected variable name for 'app'")
13
-
14
- app, app_module = get_and_validate_app(reload=False)
15
- # For py3.9 compatibility when redis is used, we MUST add any decorator pages
16
- # before compiling the app in a thread to avoid event loop error (REF-2172).
17
- app._apply_decorated_pages()
18
- compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)
19
- compile_future.add_done_callback(
20
- # Force background compile errors to print eagerly
21
- lambda f: f.result()
22
- )
23
- # Wait for the compile to finish in prod mode to ensure all optional endpoints are mounted.
24
- if is_prod_mode():
25
- compile_future.result()
26
-
27
- # ensure only "app" is exposed.
28
- del app_module
29
- del compile_future
30
- del get_and_validate_app
31
- del is_prod_mode
32
- del constants
33
- del ThreadPoolExecutor