reflex 0.5.0a3__py3-none-any.whl → 0.5.1__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 (42) hide show
  1. reflex/.templates/web/utils/state.js +7 -2
  2. reflex/app.py +68 -50
  3. reflex/app_module_for_backend.py +3 -0
  4. reflex/base.py +5 -2
  5. reflex/components/component.py +49 -13
  6. reflex/components/core/__init__.py +7 -1
  7. reflex/components/core/banner.py +79 -6
  8. reflex/components/core/banner.pyi +130 -0
  9. reflex/components/core/cond.py +10 -4
  10. reflex/components/core/debounce.py +2 -4
  11. reflex/components/core/foreach.py +11 -0
  12. reflex/components/core/upload.py +9 -10
  13. reflex/components/el/elements/forms.py +12 -6
  14. reflex/components/el/elements/media.py +19 -0
  15. reflex/components/el/elements/media.pyi +3 -1
  16. reflex/components/gridjs/datatable.py +4 -2
  17. reflex/components/props.py +30 -0
  18. reflex/components/radix/themes/components/tabs.py +1 -1
  19. reflex/components/sonner/toast.py +102 -35
  20. reflex/components/sonner/toast.pyi +27 -14
  21. reflex/config.py +5 -3
  22. reflex/constants/compiler.py +3 -3
  23. reflex/constants/installer.py +1 -1
  24. reflex/event.py +38 -24
  25. reflex/experimental/__init__.py +4 -0
  26. reflex/experimental/client_state.py +198 -0
  27. reflex/state.py +59 -21
  28. reflex/style.py +3 -3
  29. reflex/testing.py +28 -9
  30. reflex/utils/exceptions.py +64 -8
  31. reflex/utils/format.py +73 -2
  32. reflex/utils/prerequisites.py +68 -23
  33. reflex/utils/processes.py +34 -4
  34. reflex/utils/telemetry.py +42 -16
  35. reflex/utils/types.py +16 -0
  36. reflex/vars.py +104 -61
  37. reflex/vars.pyi +7 -6
  38. {reflex-0.5.0a3.dist-info → reflex-0.5.1.dist-info}/METADATA +1 -1
  39. {reflex-0.5.0a3.dist-info → reflex-0.5.1.dist-info}/RECORD +42 -40
  40. {reflex-0.5.0a3.dist-info → reflex-0.5.1.dist-info}/LICENSE +0 -0
  41. {reflex-0.5.0a3.dist-info → reflex-0.5.1.dist-info}/WHEEL +0 -0
  42. {reflex-0.5.0a3.dist-info → reflex-0.5.1.dist-info}/entry_points.txt +0 -0
reflex/state.py CHANGED
@@ -279,11 +279,13 @@ class EventHandlerSetVar(EventHandler):
279
279
 
280
280
  Raises:
281
281
  AttributeError: If the given Var name does not exist on the state.
282
- ValueError: If the given Var name is not a str
282
+ EventHandlerValueError: If the given Var name is not a str
283
283
  """
284
+ from reflex.utils.exceptions import EventHandlerValueError
285
+
284
286
  if args:
285
287
  if not isinstance(args[0], str):
286
- raise ValueError(
288
+ raise EventHandlerValueError(
287
289
  f"Var name must be passed as a string, got {args[0]!r}"
288
290
  )
289
291
  # Check that the requested Var setter exists on the State at compile time.
@@ -357,6 +359,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
357
359
  # Whether the state has ever been touched since instantiation.
358
360
  _was_touched: bool = False
359
361
 
362
+ # Whether this state class is a mixin and should not be instantiated.
363
+ _mixin: ClassVar[bool] = False
364
+
360
365
  # A special event handler for setting base vars.
361
366
  setvar: ClassVar[EventHandler]
362
367
 
@@ -380,10 +385,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
380
385
  **kwargs: The kwargs to pass to the Pydantic init method.
381
386
 
382
387
  Raises:
383
- RuntimeError: If the state is instantiated directly by end user.
388
+ ReflexRuntimeError: If the state is instantiated directly by end user.
384
389
  """
390
+ from reflex.utils.exceptions import ReflexRuntimeError
391
+
385
392
  if not _reflex_internal_init and not is_testing_env():
386
- raise RuntimeError(
393
+ raise ReflexRuntimeError(
387
394
  "State classes should not be instantiated directly in a Reflex app. "
388
395
  "See https://reflex.dev/docs/state/ for further information."
389
396
  )
@@ -424,23 +431,30 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
424
431
  """
425
432
  return [
426
433
  v
427
- for mixin in cls.__mro__
428
- if mixin is cls or not issubclass(mixin, (BaseState, ABC))
434
+ for mixin in cls._mixins() + [cls]
429
435
  for v in mixin.__dict__.values()
430
436
  if isinstance(v, ComputedVar)
431
437
  ]
432
438
 
433
439
  @classmethod
434
- def __init_subclass__(cls, **kwargs):
440
+ def __init_subclass__(cls, mixin: bool = False, **kwargs):
435
441
  """Do some magic for the subclass initialization.
436
442
 
437
443
  Args:
444
+ mixin: Whether the subclass is a mixin and should not be initialized.
438
445
  **kwargs: The kwargs to pass to the pydantic init_subclass method.
439
446
 
440
447
  Raises:
441
- ValueError: If a substate class shadows another.
448
+ StateValueError: If a substate class shadows another.
442
449
  """
450
+ from reflex.utils.exceptions import StateValueError
451
+
443
452
  super().__init_subclass__(**kwargs)
453
+
454
+ cls._mixin = mixin
455
+ if mixin:
456
+ return
457
+
444
458
  # Event handlers should not shadow builtin state methods.
445
459
  cls._check_overridden_methods()
446
460
  # Computed vars should not shadow builtin state props.
@@ -471,7 +485,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
471
485
  else:
472
486
  # During normal operation, subclasses cannot have the same name, even if they are
473
487
  # defined in different modules.
474
- raise ValueError(
488
+ raise StateValueError(
475
489
  f"The substate class '{cls.__name__}' has been defined multiple times. "
476
490
  "Shadowing substate classes is not allowed."
477
491
  )
@@ -536,7 +550,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
536
550
  for name, value in mixin.__dict__.items():
537
551
  if isinstance(value, ComputedVar):
538
552
  fget = cls._copy_fn(value.fget)
539
- newcv = ComputedVar(fget=fget, _var_name=value._var_name)
553
+ newcv = value._replace(fget=fget)
554
+ # cleanup refs to mixin cls in var_data
555
+ newcv._var_data = None
540
556
  newcv._var_set_state(cls)
541
557
  setattr(cls, name, newcv)
542
558
  cls.computed_vars[newcv._var_name] = newcv
@@ -612,8 +628,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
612
628
  return [
613
629
  mixin
614
630
  for mixin in cls.__mro__
615
- if not issubclass(mixin, (BaseState, ABC))
616
- and mixin not in [pydantic.BaseModel, Base]
631
+ if (
632
+ mixin not in [pydantic.BaseModel, Base, cls]
633
+ and issubclass(mixin, BaseState)
634
+ and mixin._mixin is True
635
+ )
617
636
  ]
618
637
 
619
638
  @classmethod
@@ -736,7 +755,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
736
755
  parent_states = [
737
756
  base
738
757
  for base in cls.__bases__
739
- if types._issubclass(base, BaseState) and base is not BaseState
758
+ if issubclass(base, BaseState) and base is not BaseState and not base._mixin
740
759
  ]
741
760
  assert len(parent_states) < 2, "Only one parent state is allowed."
742
761
  return parent_states[0] if len(parent_states) == 1 else None # type: ignore
@@ -829,10 +848,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
829
848
  prop: The variable to initialize
830
849
 
831
850
  Raises:
832
- TypeError: if the variable has an incorrect type
851
+ VarTypeError: if the variable has an incorrect type
833
852
  """
853
+ from reflex.utils.exceptions import VarTypeError
854
+
834
855
  if not types.is_valid_var_type(prop._var_type):
835
- raise TypeError(
856
+ raise VarTypeError(
836
857
  "State vars must be primitive Python types, "
837
858
  "Plotly figures, Pandas dataframes, "
838
859
  "or subclasses of rx.Base. "
@@ -1454,6 +1475,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1454
1475
  Yields:
1455
1476
  StateUpdate object
1456
1477
  """
1478
+ from reflex.utils import telemetry
1479
+
1457
1480
  # Get the function to process the event.
1458
1481
  fn = functools.partial(handler.fn, state)
1459
1482
 
@@ -1489,9 +1512,10 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1489
1512
  yield state._as_state_update(handler, events, final=True)
1490
1513
 
1491
1514
  # If an error occurs, throw a window alert.
1492
- except Exception:
1515
+ except Exception as ex:
1493
1516
  error = traceback.format_exc()
1494
1517
  print(error)
1518
+ telemetry.send_error(ex, context="backend")
1495
1519
  yield state._as_state_update(
1496
1520
  handler,
1497
1521
  window_alert("An error occurred. See logs for details."),
@@ -1688,10 +1712,12 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1688
1712
  if initial:
1689
1713
  computed_vars = {
1690
1714
  # Include initial computed vars.
1691
- prop_name: cv._initial_value
1692
- if isinstance(cv, ComputedVar)
1693
- and not isinstance(cv._initial_value, types.Unset)
1694
- else self.get_value(getattr(self, prop_name))
1715
+ prop_name: (
1716
+ cv._initial_value
1717
+ if isinstance(cv, ComputedVar)
1718
+ and not isinstance(cv._initial_value, types.Unset)
1719
+ else self.get_value(getattr(self, prop_name))
1720
+ )
1695
1721
  for prop_name, cv in self.computed_vars.items()
1696
1722
  }
1697
1723
  elif include_computed:
@@ -1818,7 +1844,7 @@ class OnLoadInternalState(State):
1818
1844
  ]
1819
1845
 
1820
1846
 
1821
- class ComponentState(Base):
1847
+ class ComponentState(State, mixin=True):
1822
1848
  """Base class to allow for the creation of a state instance per component.
1823
1849
 
1824
1850
  This allows for the bundling of UI and state logic into a single class,
@@ -1860,6 +1886,18 @@ class ComponentState(Base):
1860
1886
  # The number of components created from this class.
1861
1887
  _per_component_state_instance_count: ClassVar[int] = 0
1862
1888
 
1889
+ @classmethod
1890
+ def __init_subclass__(cls, mixin: bool = False, **kwargs):
1891
+ """Overwrite mixin default to True.
1892
+
1893
+ Args:
1894
+ mixin: Whether the subclass is a mixin and should not be initialized.
1895
+ **kwargs: The kwargs to pass to the pydantic init_subclass method.
1896
+ """
1897
+ if ComponentState in cls.__bases__:
1898
+ mixin = True
1899
+ super().__init_subclass__(mixin=mixin, **kwargs)
1900
+
1863
1901
  @classmethod
1864
1902
  def get_component(cls, *children, **props) -> "Component":
1865
1903
  """Get the component instance.
reflex/style.py CHANGED
@@ -16,10 +16,10 @@ LIGHT_COLOR_MODE: str = "light"
16
16
  DARK_COLOR_MODE: str = "dark"
17
17
 
18
18
  # Reference the global ColorModeContext
19
- color_mode_var_data = VarData( # type: ignore
19
+ color_mode_var_data = VarData(
20
20
  imports={
21
- f"/{constants.Dirs.CONTEXTS_PATH}": {ImportVar(tag="ColorModeContext")},
22
- "react": {ImportVar(tag="useContext")},
21
+ f"/{constants.Dirs.CONTEXTS_PATH}": [ImportVar(tag="ColorModeContext")],
22
+ "react": [ImportVar(tag="useContext")],
23
23
  },
24
24
  hooks={
25
25
  f"const [ {constants.ColorMode.NAME}, {constants.ColorMode.TOGGLE} ] = useContext(ColorModeContext)": None,
reflex/testing.py CHANGED
@@ -26,6 +26,7 @@ from typing import (
26
26
  AsyncIterator,
27
27
  Callable,
28
28
  Coroutine,
29
+ List,
29
30
  Optional,
30
31
  Type,
31
32
  TypeVar,
@@ -513,12 +514,19 @@ class AppHarness:
513
514
  raise TimeoutError("Backend is not listening.")
514
515
  return backend.servers[0].sockets[0]
515
516
 
516
- def frontend(self, driver_clz: Optional[Type["WebDriver"]] = None) -> "WebDriver":
517
+ def frontend(
518
+ self,
519
+ driver_clz: Optional[Type["WebDriver"]] = None,
520
+ driver_kwargs: dict[str, Any] | None = None,
521
+ driver_option_args: List[str] | None = None,
522
+ ) -> "WebDriver":
517
523
  """Get a selenium webdriver instance pointed at the app.
518
524
 
519
525
  Args:
520
526
  driver_clz: webdriver.Chrome (default), webdriver.Firefox, webdriver.Safari,
521
527
  webdriver.Edge, etc
528
+ driver_kwargs: additional keyword arguments to pass to the webdriver constructor
529
+ driver_option_args: additional arguments for the webdriver options
522
530
 
523
531
  Returns:
524
532
  Instance of the given webdriver navigated to the frontend url of the app.
@@ -541,19 +549,30 @@ class AppHarness:
541
549
  requested_driver = os.environ.get("APP_HARNESS_DRIVER", "Chrome")
542
550
  driver_clz = getattr(webdriver, requested_driver)
543
551
  options = getattr(webdriver, f"{requested_driver}Options")()
544
- if driver_clz is webdriver.Chrome and want_headless:
552
+ if driver_clz is webdriver.Chrome:
545
553
  options = webdriver.ChromeOptions()
546
- options.add_argument("--headless=new")
547
- elif driver_clz is webdriver.Firefox and want_headless:
554
+ options.add_argument("--class=AppHarness")
555
+ if want_headless:
556
+ options.add_argument("--headless=new")
557
+ elif driver_clz is webdriver.Firefox:
548
558
  options = webdriver.FirefoxOptions()
549
- options.add_argument("-headless")
550
- elif driver_clz is webdriver.Edge and want_headless:
559
+ if want_headless:
560
+ options.add_argument("-headless")
561
+ elif driver_clz is webdriver.Edge:
551
562
  options = webdriver.EdgeOptions()
552
- options.add_argument("headless")
553
- if options and (args := os.environ.get("APP_HARNESS_DRIVER_ARGS")):
563
+ if want_headless:
564
+ options.add_argument("headless")
565
+ if options is None:
566
+ raise RuntimeError(f"Could not determine options for {driver_clz}")
567
+ if args := os.environ.get("APP_HARNESS_DRIVER_ARGS"):
554
568
  for arg in args.split(","):
555
569
  options.add_argument(arg)
556
- driver = driver_clz(options=options) # type: ignore
570
+ if driver_option_args is not None:
571
+ for arg in driver_option_args:
572
+ options.add_argument(arg)
573
+ if driver_kwargs is None:
574
+ driver_kwargs = {}
575
+ driver = driver_clz(options=options, **driver_kwargs) # type: ignore
557
576
  driver.get(self.frontend_url)
558
577
  self._frontends.append(driver)
559
578
  return driver
@@ -1,21 +1,77 @@
1
1
  """Custom Exceptions."""
2
2
 
3
3
 
4
- class InvalidStylePropError(TypeError):
5
- """Custom Type Error when style props have invalid values."""
4
+ class ReflexError(Exception):
5
+ """Base exception for all Reflex exceptions."""
6
+
7
+
8
+ class ReflexRuntimeError(ReflexError, RuntimeError):
9
+ """Custom RuntimeError for Reflex."""
10
+
11
+
12
+ class UploadTypeError(ReflexError, TypeError):
13
+ """Custom TypeError for upload related errors."""
14
+
15
+
16
+ class EnvVarValueError(ReflexError, ValueError):
17
+ """Custom ValueError raised when unable to convert env var to expected type."""
18
+
19
+
20
+ class ComponentTypeError(ReflexError, TypeError):
21
+ """Custom TypeError for component related errors."""
22
+
23
+
24
+ class EventHandlerTypeError(ReflexError, TypeError):
25
+ """Custom TypeError for event handler related errors."""
26
+
27
+
28
+ class EventHandlerValueError(ReflexError, ValueError):
29
+ """Custom ValueError for event handler related errors."""
30
+
31
+
32
+ class StateValueError(ReflexError, ValueError):
33
+ """Custom ValueError for state related errors."""
34
+
6
35
 
7
- pass
36
+ class VarNameError(ReflexError, NameError):
37
+ """Custom NameError for when a state var has been shadowed by a substate var."""
8
38
 
9
39
 
10
- class ImmutableStateError(AttributeError):
40
+ class VarTypeError(ReflexError, TypeError):
41
+ """Custom TypeError for var related errors."""
42
+
43
+
44
+ class VarValueError(ReflexError, ValueError):
45
+ """Custom ValueError for var related errors."""
46
+
47
+
48
+ class VarAttributeError(ReflexError, AttributeError):
49
+ """Custom AttributeError for var related errors."""
50
+
51
+
52
+ class UploadValueError(ReflexError, ValueError):
53
+ """Custom ValueError for upload related errors."""
54
+
55
+
56
+ class RouteValueError(ReflexError, ValueError):
57
+ """Custom ValueError for route related errors."""
58
+
59
+
60
+ class VarOperationTypeError(ReflexError, TypeError):
61
+ """Custom TypeError for when unsupported operations are performed on vars."""
62
+
63
+
64
+ class InvalidStylePropError(ReflexError, TypeError):
65
+ """Custom Type Error when style props have invalid values."""
66
+
67
+
68
+ class ImmutableStateError(ReflexError):
11
69
  """Raised when a background task attempts to modify state outside of context."""
12
70
 
13
71
 
14
- class LockExpiredError(Exception):
72
+ class LockExpiredError(ReflexError):
15
73
  """Raised when the state lock expires while an event is being processed."""
16
74
 
17
75
 
18
- class MatchTypeError(TypeError):
76
+ class MatchTypeError(ReflexError, TypeError):
19
77
  """Raised when the return types of match cases are different."""
20
-
21
- pass
reflex/utils/format.py CHANGED
@@ -6,7 +6,7 @@ import inspect
6
6
  import json
7
7
  import os
8
8
  import re
9
- from typing import TYPE_CHECKING, Any, List, Optional, Union
9
+ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
10
10
 
11
11
  from reflex import constants
12
12
  from reflex.utils import exceptions, serializers, types
@@ -15,7 +15,7 @@ from reflex.vars import BaseVar, Var
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from reflex.components.component import ComponentStyle
18
- from reflex.event import EventChain, EventHandler, EventSpec
18
+ from reflex.event import ArgsSpec, EventChain, EventHandler, EventSpec
19
19
 
20
20
  WRAP_MAP = {
21
21
  "{": "}",
@@ -590,6 +590,77 @@ def format_event_chain(
590
590
  )
591
591
 
592
592
 
593
+ def format_queue_events(
594
+ events: EventSpec
595
+ | EventHandler
596
+ | Callable
597
+ | List[EventSpec | EventHandler | Callable]
598
+ | None = None,
599
+ args_spec: Optional[ArgsSpec] = None,
600
+ ) -> Var[EventChain]:
601
+ """Format a list of event handler / event spec as a javascript callback.
602
+
603
+ The resulting code can be passed to interfaces that expect a callback
604
+ function and when triggered it will directly call queueEvents.
605
+
606
+ It is intended to be executed in the rx.call_script context, where some
607
+ existing API needs a callback to trigger a backend event handler.
608
+
609
+ Args:
610
+ events: The events to queue.
611
+ args_spec: The argument spec for the callback.
612
+
613
+ Returns:
614
+ The compiled javascript callback to queue the given events on the frontend.
615
+ """
616
+ from reflex.event import (
617
+ EventChain,
618
+ EventHandler,
619
+ EventSpec,
620
+ call_event_fn,
621
+ call_event_handler,
622
+ )
623
+
624
+ if not events:
625
+ return Var.create_safe(
626
+ "() => null", _var_is_string=False, _var_is_local=False
627
+ ).to(EventChain)
628
+
629
+ # If no spec is provided, the function will take no arguments.
630
+ def _default_args_spec():
631
+ return []
632
+
633
+ # Construct the arguments that the function accepts.
634
+ sig = inspect.signature(args_spec or _default_args_spec) # type: ignore
635
+ if sig.parameters:
636
+ arg_def = ",".join(f"_{p}" for p in sig.parameters)
637
+ arg_def = f"({arg_def})"
638
+ else:
639
+ arg_def = "()"
640
+
641
+ payloads = []
642
+ if not isinstance(events, list):
643
+ events = [events]
644
+
645
+ # Process each event/spec/lambda (similar to Component._create_event_chain).
646
+ for spec in events:
647
+ specs: list[EventSpec] = []
648
+ if isinstance(spec, (EventHandler, EventSpec)):
649
+ specs = [call_event_handler(spec, args_spec or _default_args_spec)]
650
+ elif isinstance(spec, type(lambda: None)):
651
+ specs = call_event_fn(spec, args_spec or _default_args_spec)
652
+ payloads.extend(format_event(s) for s in specs)
653
+
654
+ # Return the final code snippet, expecting queueEvents, processEvent, and socket to be in scope.
655
+ # Typically this snippet will _only_ run from within an rx.call_script eval context.
656
+ return Var.create_safe(
657
+ f"{arg_def} => {{queueEvents([{','.join(payloads)}], {constants.CompileVars.SOCKET}); "
658
+ f"processEvent({constants.CompileVars.SOCKET})}}",
659
+ _var_is_string=False,
660
+ _var_is_local=False,
661
+ ).to(EventChain)
662
+
663
+
593
664
  def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
594
665
  """Convert back query params name to python-friendly case.
595
666
 
@@ -181,7 +181,12 @@ def get_install_package_manager() -> str | None:
181
181
  Returns:
182
182
  The path to the package manager.
183
183
  """
184
- if constants.IS_WINDOWS and not is_windows_bun_supported():
184
+ if (
185
+ constants.IS_WINDOWS
186
+ and not is_windows_bun_supported()
187
+ or windows_check_onedrive_in_path()
188
+ or windows_npm_escape_hatch()
189
+ ):
185
190
  return get_package_manager()
186
191
  return get_config().bun_path
187
192
 
@@ -199,6 +204,24 @@ def get_package_manager() -> str | None:
199
204
  return npm_path
200
205
 
201
206
 
207
+ def windows_check_onedrive_in_path() -> bool:
208
+ """For windows, check if oneDrive is present in the project dir path.
209
+
210
+ Returns:
211
+ If oneDrive is in the path of the project directory.
212
+ """
213
+ return "onedrive" in str(Path.cwd()).lower()
214
+
215
+
216
+ def windows_npm_escape_hatch() -> bool:
217
+ """For windows, if the user sets REFLEX_USE_NPM, use npm instead of bun.
218
+
219
+ Returns:
220
+ If the user has set REFLEX_USE_NPM.
221
+ """
222
+ return os.environ.get("REFLEX_USE_NPM", "").lower() in ["true", "1", "yes"]
223
+
224
+
202
225
  def get_app(reload: bool = False) -> ModuleType:
203
226
  """Get the app module based on the default config.
204
227
 
@@ -211,27 +234,33 @@ def get_app(reload: bool = False) -> ModuleType:
211
234
  Raises:
212
235
  RuntimeError: If the app name is not set in the config.
213
236
  """
214
- os.environ[constants.RELOAD_CONFIG] = str(reload)
215
- config = get_config()
216
- if not config.app_name:
217
- raise RuntimeError(
218
- "Cannot get the app module because `app_name` is not set in rxconfig! "
219
- "If this error occurs in a reflex test case, ensure that `get_app` is mocked."
220
- )
221
- module = config.module
222
- sys.path.insert(0, os.getcwd())
223
- app = __import__(module, fromlist=(constants.CompileVars.APP,))
237
+ from reflex.utils import telemetry
238
+
239
+ try:
240
+ os.environ[constants.RELOAD_CONFIG] = str(reload)
241
+ config = get_config()
242
+ if not config.app_name:
243
+ raise RuntimeError(
244
+ "Cannot get the app module because `app_name` is not set in rxconfig! "
245
+ "If this error occurs in a reflex test case, ensure that `get_app` is mocked."
246
+ )
247
+ module = config.module
248
+ sys.path.insert(0, os.getcwd())
249
+ app = __import__(module, fromlist=(constants.CompileVars.APP,))
224
250
 
225
- if reload:
226
- from reflex.state import reload_state_module
251
+ if reload:
252
+ from reflex.state import reload_state_module
227
253
 
228
- # Reset rx.State subclasses to avoid conflict when reloading.
229
- reload_state_module(module=module)
254
+ # Reset rx.State subclasses to avoid conflict when reloading.
255
+ reload_state_module(module=module)
230
256
 
231
- # Reload the app module.
232
- importlib.reload(app)
257
+ # Reload the app module.
258
+ importlib.reload(app)
233
259
 
234
- return app
260
+ return app
261
+ except Exception as ex:
262
+ telemetry.send_error(ex, context="frontend")
263
+ raise
235
264
 
236
265
 
237
266
  def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
@@ -383,7 +412,7 @@ def initialize_gitignore(
383
412
  # Write files to the .gitignore file.
384
413
  with open(gitignore_file, "w", newline="\n") as f:
385
414
  console.debug(f"Creating {gitignore_file}")
386
- f.write(f"{(path_ops.join(sorted(files_to_ignore))).lstrip()}")
415
+ f.write(f"{(path_ops.join(sorted(files_to_ignore))).lstrip()}\n")
387
416
 
388
417
 
389
418
  def initialize_requirements_txt():
@@ -737,10 +766,17 @@ def install_bun():
737
766
  Raises:
738
767
  FileNotFoundError: If required packages are not found.
739
768
  """
740
- if constants.IS_WINDOWS and not is_windows_bun_supported():
741
- console.warn(
742
- "Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
743
- )
769
+ win_supported = is_windows_bun_supported()
770
+ one_drive_in_path = windows_check_onedrive_in_path()
771
+ if constants.IS_WINDOWS and not win_supported or one_drive_in_path:
772
+ if not win_supported:
773
+ console.warn(
774
+ "Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
775
+ )
776
+ if one_drive_in_path:
777
+ console.warn(
778
+ "Creating project directories in OneDrive is not recommended for bun usage on windows. This will fallback to npm."
779
+ )
744
780
 
745
781
  # Skip if bun is already installed.
746
782
  if os.path.exists(get_config().bun_path) and get_bun_version() == version.parse(
@@ -843,11 +879,13 @@ def install_frontend_packages(packages: set[str], config: Config):
843
879
  if not constants.IS_WINDOWS
844
880
  or constants.IS_WINDOWS
845
881
  and is_windows_bun_supported()
882
+ and not windows_check_onedrive_in_path()
846
883
  else None
847
884
  )
848
885
  processes.run_process_with_fallback(
849
886
  [get_install_package_manager(), "install"], # type: ignore
850
887
  fallback=fallback_command,
888
+ analytics_enabled=True,
851
889
  show_status_message="Installing base frontend packages",
852
890
  cwd=constants.Dirs.WEB,
853
891
  shell=constants.IS_WINDOWS,
@@ -863,6 +901,7 @@ def install_frontend_packages(packages: set[str], config: Config):
863
901
  *((config.tailwind or {}).get("plugins", [])),
864
902
  ],
865
903
  fallback=fallback_command,
904
+ analytics_enabled=True,
866
905
  show_status_message="Installing tailwind",
867
906
  cwd=constants.Dirs.WEB,
868
907
  shell=constants.IS_WINDOWS,
@@ -873,6 +912,7 @@ def install_frontend_packages(packages: set[str], config: Config):
873
912
  processes.run_process_with_fallback(
874
913
  [get_install_package_manager(), "add", *packages],
875
914
  fallback=fallback_command,
915
+ analytics_enabled=True,
876
916
  show_status_message="Installing frontend packages from config and components",
877
917
  cwd=constants.Dirs.WEB,
878
918
  shell=constants.IS_WINDOWS,
@@ -929,6 +969,11 @@ def needs_reinit(frontend: bool = True) -> bool:
929
969
  console.warn(
930
970
  f"""On Python < 3.12, `uvicorn==0.20.0` is recommended for improved hot reload times. Found {uvi_ver} instead."""
931
971
  )
972
+
973
+ if windows_check_onedrive_in_path():
974
+ console.warn(
975
+ "Creating project directories in OneDrive may lead to performance issues. For optimal performance, It is recommended to avoid using OneDrive for your reflex app."
976
+ )
932
977
  # No need to reinitialize if the app is already initialized.
933
978
  return False
934
979