reflex 0.6.6.post3__py3-none-any.whl → 0.6.7a1__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 (141) hide show
  1. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +5 -1
  2. reflex/.templates/web/utils/state.js +36 -28
  3. reflex/__init__.py +1 -1
  4. reflex/__init__.pyi +1 -0
  5. reflex/app.py +41 -16
  6. reflex/assets.py +2 -2
  7. reflex/base.py +8 -7
  8. reflex/compiler/templates.py +1 -0
  9. reflex/compiler/utils.py +2 -3
  10. reflex/components/base/bare.py +2 -2
  11. reflex/components/component.py +54 -29
  12. reflex/components/core/banner.py +2 -2
  13. reflex/components/core/banner.pyi +1 -1
  14. reflex/components/core/client_side_routing.py +2 -2
  15. reflex/components/core/client_side_routing.pyi +1 -1
  16. reflex/components/core/clipboard.py +11 -9
  17. reflex/components/core/clipboard.pyi +1 -1
  18. reflex/components/core/cond.py +3 -3
  19. reflex/components/core/foreach.py +1 -1
  20. reflex/components/core/html.pyi +1 -1
  21. reflex/components/core/upload.py +8 -8
  22. reflex/components/datadisplay/code.py +5 -5
  23. reflex/components/datadisplay/dataeditor.py +8 -28
  24. reflex/components/datadisplay/dataeditor.pyi +1 -1
  25. reflex/components/datadisplay/shiki_code_block.py +7 -7
  26. reflex/components/dynamic.py +2 -2
  27. reflex/components/el/elements/__init__.py +1 -1
  28. reflex/components/el/elements/__init__.pyi +1 -1
  29. reflex/components/el/elements/base.py +2 -2
  30. reflex/components/el/elements/base.pyi +1 -1
  31. reflex/components/el/elements/forms.py +40 -10
  32. reflex/components/el/elements/forms.pyi +17 -15
  33. reflex/components/el/elements/inline.py +1 -1
  34. reflex/components/el/elements/inline.pyi +28 -28
  35. reflex/components/el/elements/media.py +1 -4
  36. reflex/components/el/elements/media.pyi +25 -26
  37. reflex/components/el/elements/metadata.py +6 -6
  38. reflex/components/el/elements/metadata.pyi +4 -4
  39. reflex/components/el/elements/other.py +17 -9
  40. reflex/components/el/elements/other.pyi +7 -7
  41. reflex/components/el/elements/scripts.py +1 -2
  42. reflex/components/el/elements/scripts.pyi +3 -3
  43. reflex/components/el/elements/sectioning.py +16 -16
  44. reflex/components/el/elements/sectioning.pyi +15 -15
  45. reflex/components/el/elements/tables.py +1 -1
  46. reflex/components/el/elements/tables.pyi +10 -10
  47. reflex/components/el/elements/typography.py +1 -1
  48. reflex/components/el/elements/typography.pyi +15 -15
  49. reflex/components/markdown/markdown.py +3 -3
  50. reflex/components/next/image.py +1 -1
  51. reflex/components/next/image.pyi +1 -1
  52. reflex/components/plotly/plotly.py +2 -2
  53. reflex/components/radix/primitives/accordion.py +2 -1
  54. reflex/components/radix/primitives/form.pyi +3 -3
  55. reflex/components/radix/primitives/slider.py +1 -1
  56. reflex/components/radix/themes/base.py +4 -10
  57. reflex/components/radix/themes/color_mode.pyi +2 -2
  58. reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
  59. reflex/components/radix/themes/components/badge.pyi +1 -1
  60. reflex/components/radix/themes/components/button.pyi +1 -1
  61. reflex/components/radix/themes/components/callout.pyi +5 -5
  62. reflex/components/radix/themes/components/card.pyi +1 -1
  63. reflex/components/radix/themes/components/checkbox.pyi +3 -3
  64. reflex/components/radix/themes/components/context_menu.py +11 -0
  65. reflex/components/radix/themes/components/context_menu.pyi +155 -0
  66. reflex/components/radix/themes/components/dialog.pyi +1 -1
  67. reflex/components/radix/themes/components/hover_card.pyi +1 -1
  68. reflex/components/radix/themes/components/icon_button.py +1 -1
  69. reflex/components/radix/themes/components/icon_button.pyi +1 -1
  70. reflex/components/radix/themes/components/inset.pyi +1 -1
  71. reflex/components/radix/themes/components/popover.pyi +1 -1
  72. reflex/components/radix/themes/components/radio_group.py +2 -4
  73. reflex/components/radix/themes/components/radio_group.pyi +1 -1
  74. reflex/components/radix/themes/components/select.pyi +3 -3
  75. reflex/components/radix/themes/components/slider.pyi +1 -1
  76. reflex/components/radix/themes/components/switch.pyi +1 -1
  77. reflex/components/radix/themes/components/table.pyi +7 -7
  78. reflex/components/radix/themes/components/tabs.pyi +2 -2
  79. reflex/components/radix/themes/components/text_area.py +3 -0
  80. reflex/components/radix/themes/components/text_area.pyi +3 -1
  81. reflex/components/radix/themes/components/text_field.py +16 -1
  82. reflex/components/radix/themes/components/text_field.pyi +105 -17
  83. reflex/components/radix/themes/layout/box.pyi +1 -1
  84. reflex/components/radix/themes/layout/center.pyi +1 -1
  85. reflex/components/radix/themes/layout/flex.pyi +1 -1
  86. reflex/components/radix/themes/layout/grid.pyi +1 -1
  87. reflex/components/radix/themes/layout/list.py +0 -4
  88. reflex/components/radix/themes/layout/list.pyi +3 -8
  89. reflex/components/radix/themes/layout/section.pyi +1 -1
  90. reflex/components/radix/themes/layout/spacer.pyi +1 -1
  91. reflex/components/radix/themes/layout/stack.pyi +3 -3
  92. reflex/components/radix/themes/typography/blockquote.pyi +1 -1
  93. reflex/components/radix/themes/typography/code.pyi +1 -1
  94. reflex/components/radix/themes/typography/heading.pyi +1 -1
  95. reflex/components/radix/themes/typography/link.py +5 -1
  96. reflex/components/radix/themes/typography/link.pyi +1 -1
  97. reflex/components/radix/themes/typography/text.pyi +7 -7
  98. reflex/components/recharts/cartesian.py +1 -1
  99. reflex/components/recharts/charts.py +4 -4
  100. reflex/components/recharts/polar.py +1 -1
  101. reflex/components/recharts/polar.pyi +1 -1
  102. reflex/components/sonner/toast.py +4 -7
  103. reflex/components/suneditor/editor.py +6 -6
  104. reflex/components/suneditor/editor.pyi +6 -6
  105. reflex/config.py +25 -10
  106. reflex/constants/compiler.py +6 -0
  107. reflex/constants/config.py +2 -0
  108. reflex/constants/custom_components.py +1 -1
  109. reflex/constants/route.py +1 -1
  110. reflex/custom_components/custom_components.py +21 -21
  111. reflex/event.py +57 -22
  112. reflex/experimental/client_state.py +2 -1
  113. reflex/experimental/layout.py +0 -6
  114. reflex/model.py +125 -9
  115. reflex/reflex.py +5 -6
  116. reflex/state.py +203 -88
  117. reflex/style.py +1 -4
  118. reflex/testing.py +10 -11
  119. reflex/utils/build.py +1 -1
  120. reflex/utils/console.py +75 -6
  121. reflex/utils/exceptions.py +12 -0
  122. reflex/utils/exec.py +10 -10
  123. reflex/utils/export.py +1 -2
  124. reflex/utils/format.py +11 -8
  125. reflex/utils/path_ops.py +2 -2
  126. reflex/utils/prerequisites.py +31 -28
  127. reflex/utils/processes.py +4 -4
  128. reflex/utils/pyi_generator.py +12 -11
  129. reflex/utils/types.py +6 -3
  130. reflex/vars/__init__.py +1 -0
  131. reflex/vars/base.py +75 -38
  132. reflex/vars/datetime.py +222 -0
  133. reflex/vars/function.py +3 -3
  134. reflex/vars/number.py +3 -3
  135. reflex/vars/object.py +5 -5
  136. reflex/vars/sequence.py +7 -7
  137. {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/METADATA +2 -2
  138. {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/RECORD +141 -140
  139. {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/LICENSE +0 -0
  140. {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/WHEEL +0 -0
  141. {reflex-0.6.6.post3.dist-info → reflex-0.6.7a1.dist-info}/entry_points.txt +0 -0
reflex/utils/console.py CHANGED
@@ -20,13 +20,46 @@ _EMITTED_DEPRECATION_WARNINGS = set()
20
20
  # Info messages which have been printed.
21
21
  _EMITTED_INFO = set()
22
22
 
23
+ # Warnings which have been printed.
24
+ _EMIITED_WARNINGS = set()
25
+
26
+ # Errors which have been printed.
27
+ _EMITTED_ERRORS = set()
28
+
29
+ # Success messages which have been printed.
30
+ _EMITTED_SUCCESS = set()
31
+
32
+ # Debug messages which have been printed.
33
+ _EMITTED_DEBUG = set()
34
+
35
+ # Logs which have been printed.
36
+ _EMITTED_LOGS = set()
37
+
38
+ # Prints which have been printed.
39
+ _EMITTED_PRINTS = set()
40
+
23
41
 
24
42
  def set_log_level(log_level: LogLevel):
25
43
  """Set the log level.
26
44
 
27
45
  Args:
28
46
  log_level: The log level to set.
47
+
48
+ Raises:
49
+ ValueError: If the log level is invalid.
29
50
  """
51
+ if not isinstance(log_level, LogLevel):
52
+ deprecate(
53
+ feature_name="Passing a string to set_log_level",
54
+ reason="use reflex.constants.LogLevel enum instead",
55
+ deprecation_version="0.6.6",
56
+ removal_version="0.7.0",
57
+ )
58
+ try:
59
+ log_level = getattr(LogLevel, log_level.upper())
60
+ except AttributeError as ae:
61
+ raise ValueError(f"Invalid log level: {log_level}") from ae
62
+
30
63
  global _LOG_LEVEL
31
64
  _LOG_LEVEL = log_level
32
65
 
@@ -40,25 +73,37 @@ def is_debug() -> bool:
40
73
  return _LOG_LEVEL <= LogLevel.DEBUG
41
74
 
42
75
 
43
- def print(msg: str, **kwargs):
76
+ def print(msg: str, dedupe: bool = False, **kwargs):
44
77
  """Print a message.
45
78
 
46
79
  Args:
47
80
  msg: The message to print.
81
+ dedupe: If True, suppress multiple console logs of print message.
48
82
  kwargs: Keyword arguments to pass to the print function.
49
83
  """
84
+ if dedupe:
85
+ if msg in _EMITTED_PRINTS:
86
+ return
87
+ else:
88
+ _EMITTED_PRINTS.add(msg)
50
89
  _console.print(msg, **kwargs)
51
90
 
52
91
 
53
- def debug(msg: str, **kwargs):
92
+ def debug(msg: str, dedupe: bool = False, **kwargs):
54
93
  """Print a debug message.
55
94
 
56
95
  Args:
57
96
  msg: The debug message.
97
+ dedupe: If True, suppress multiple console logs of debug message.
58
98
  kwargs: Keyword arguments to pass to the print function.
59
99
  """
60
100
  if is_debug():
61
101
  msg_ = f"[purple]Debug: {msg}[/purple]"
102
+ if dedupe:
103
+ if msg_ in _EMITTED_DEBUG:
104
+ return
105
+ else:
106
+ _EMITTED_DEBUG.add(msg_)
62
107
  if progress := kwargs.pop("progress", None):
63
108
  progress.console.print(msg_, **kwargs)
64
109
  else:
@@ -82,25 +127,37 @@ def info(msg: str, dedupe: bool = False, **kwargs):
82
127
  print(f"[cyan]Info: {msg}[/cyan]", **kwargs)
83
128
 
84
129
 
85
- def success(msg: str, **kwargs):
130
+ def success(msg: str, dedupe: bool = False, **kwargs):
86
131
  """Print a success message.
87
132
 
88
133
  Args:
89
134
  msg: The success message.
135
+ dedupe: If True, suppress multiple console logs of success message.
90
136
  kwargs: Keyword arguments to pass to the print function.
91
137
  """
92
138
  if _LOG_LEVEL <= LogLevel.INFO:
139
+ if dedupe:
140
+ if msg in _EMITTED_SUCCESS:
141
+ return
142
+ else:
143
+ _EMITTED_SUCCESS.add(msg)
93
144
  print(f"[green]Success: {msg}[/green]", **kwargs)
94
145
 
95
146
 
96
- def log(msg: str, **kwargs):
147
+ def log(msg: str, dedupe: bool = False, **kwargs):
97
148
  """Takes a string and logs it to the console.
98
149
 
99
150
  Args:
100
151
  msg: The message to log.
152
+ dedupe: If True, suppress multiple console logs of log message.
101
153
  kwargs: Keyword arguments to pass to the print function.
102
154
  """
103
155
  if _LOG_LEVEL <= LogLevel.INFO:
156
+ if dedupe:
157
+ if msg in _EMITTED_LOGS:
158
+ return
159
+ else:
160
+ _EMITTED_LOGS.add(msg)
104
161
  _console.log(msg, **kwargs)
105
162
 
106
163
 
@@ -114,14 +171,20 @@ def rule(title: str, **kwargs):
114
171
  _console.rule(title, **kwargs)
115
172
 
116
173
 
117
- def warn(msg: str, **kwargs):
174
+ def warn(msg: str, dedupe: bool = False, **kwargs):
118
175
  """Print a warning message.
119
176
 
120
177
  Args:
121
178
  msg: The warning message.
179
+ dedupe: If True, suppress multiple console logs of warning message.
122
180
  kwargs: Keyword arguments to pass to the print function.
123
181
  """
124
182
  if _LOG_LEVEL <= LogLevel.WARNING:
183
+ if dedupe:
184
+ if msg in _EMIITED_WARNINGS:
185
+ return
186
+ else:
187
+ _EMIITED_WARNINGS.add(msg)
125
188
  print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
126
189
 
127
190
 
@@ -154,14 +217,20 @@ def deprecate(
154
217
  _EMITTED_DEPRECATION_WARNINGS.add(feature_name)
155
218
 
156
219
 
157
- def error(msg: str, **kwargs):
220
+ def error(msg: str, dedupe: bool = False, **kwargs):
158
221
  """Print an error message.
159
222
 
160
223
  Args:
161
224
  msg: The error message.
225
+ dedupe: If True, suppress multiple console logs of error message.
162
226
  kwargs: Keyword arguments to pass to the print function.
163
227
  """
164
228
  if _LOG_LEVEL <= LogLevel.ERROR:
229
+ if dedupe:
230
+ if msg in _EMITTED_ERRORS:
231
+ return
232
+ else:
233
+ _EMITTED_ERRORS.add(msg)
165
234
  print(f"[red]{msg}[/red]", **kwargs)
166
235
 
167
236
 
@@ -63,6 +63,10 @@ class UploadValueError(ReflexError, ValueError):
63
63
  """Custom ValueError for upload related errors."""
64
64
 
65
65
 
66
+ class PageValueError(ReflexError, ValueError):
67
+ """Custom ValueError for page related errors."""
68
+
69
+
66
70
  class RouteValueError(ReflexError, ValueError):
67
71
  """Custom ValueError for route related errors."""
68
72
 
@@ -155,6 +159,10 @@ class StateTooLargeError(ReflexError):
155
159
  """Raised when the state is too large to be serialized."""
156
160
 
157
161
 
162
+ class StateSerializationError(ReflexError):
163
+ """Raised when the state cannot be serialized."""
164
+
165
+
158
166
  class SystemPackageMissingError(ReflexError):
159
167
  """Raised when a system package is missing."""
160
168
 
@@ -175,3 +183,7 @@ def raise_system_package_missing_error(package: str) -> NoReturn:
175
183
  " Please install it through your system package manager."
176
184
  + (f" You can do so by running 'brew install {package}'." if IS_MACOS else "")
177
185
  )
186
+
187
+
188
+ class InvalidLockWarningThresholdError(ReflexError):
189
+ """Raised when an invalid lock warning threshold is provided."""
reflex/utils/exec.py CHANGED
@@ -24,7 +24,7 @@ from reflex.utils.prerequisites import get_web_dir
24
24
  frontend_process = None
25
25
 
26
26
 
27
- def detect_package_change(json_file_path: str) -> str:
27
+ def detect_package_change(json_file_path: Path) -> str:
28
28
  """Calculates the SHA-256 hash of a JSON file and returns it as a hexadecimal string.
29
29
 
30
30
  Args:
@@ -37,7 +37,7 @@ def detect_package_change(json_file_path: str) -> str:
37
37
  >>> detect_package_change("package.json")
38
38
  'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2'
39
39
  """
40
- with open(json_file_path, "r") as file:
40
+ with json_file_path.open("r") as file:
41
41
  json_data = json.load(file)
42
42
 
43
43
  # Calculate the hash
@@ -81,7 +81,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
81
81
  from reflex.utils import processes
82
82
 
83
83
  json_file_path = get_web_dir() / constants.PackageJson.PATH
84
- last_hash = detect_package_change(str(json_file_path))
84
+ last_hash = detect_package_change(json_file_path)
85
85
  process = None
86
86
  first_run = True
87
87
 
@@ -117,14 +117,14 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
117
117
  console.print("New packages detected: Updating app...")
118
118
  else:
119
119
  if any(
120
- [x in line for x in ("bin executable does not exist on disk",)]
120
+ x in line for x in ("bin executable does not exist on disk",)
121
121
  ):
122
122
  console.error(
123
123
  "Try setting `REFLEX_USE_NPM=1` and re-running `reflex init` and `reflex run` to use npm instead of bun:\n"
124
124
  "`REFLEX_USE_NPM=1 reflex init`\n"
125
125
  "`REFLEX_USE_NPM=1 reflex run`"
126
126
  )
127
- new_hash = detect_package_change(str(json_file_path))
127
+ new_hash = detect_package_change(json_file_path)
128
128
  if new_hash != last_hash:
129
129
  last_hash = new_hash
130
130
  kill(process.pid)
@@ -206,7 +206,9 @@ def get_granian_target():
206
206
 
207
207
  app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
208
208
 
209
- return f"{str(app_module_path)}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
209
+ return (
210
+ f"{app_module_path!s}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
211
+ )
210
212
 
211
213
 
212
214
  def run_backend(
@@ -440,10 +442,8 @@ def output_system_info():
440
442
 
441
443
  system = platform.system()
442
444
 
443
- if (
444
- system != "Windows"
445
- or system == "Windows"
446
- and prerequisites.is_windows_bun_supported()
445
+ if system != "Windows" or (
446
+ system == "Windows" and prerequisites.is_windows_bun_supported()
447
447
  ):
448
448
  dependencies.extend(
449
449
  [
reflex/utils/export.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """Export utilities."""
2
2
 
3
- import os
4
3
  from pathlib import Path
5
4
  from typing import Optional
6
5
 
@@ -15,7 +14,7 @@ def export(
15
14
  zipping: bool = True,
16
15
  frontend: bool = True,
17
16
  backend: bool = True,
18
- zip_dest_dir: str = os.getcwd(),
17
+ zip_dest_dir: str = str(Path.cwd()),
19
18
  upload_db_file: bool = False,
20
19
  api_url: Optional[str] = None,
21
20
  deploy_url: Optional[str] = None,
reflex/utils/format.py CHANGED
@@ -329,12 +329,12 @@ def format_match(
329
329
  return_value = case[-1]
330
330
 
331
331
  case_conditions = " ".join(
332
- [f"case JSON.stringify({str(condition)}):" for condition in conditions]
332
+ [f"case JSON.stringify({condition!s}):" for condition in conditions]
333
333
  )
334
- case_code = f"{case_conditions} return ({str(return_value)}); break;"
334
+ case_code = f"{case_conditions} return ({return_value!s}); break;"
335
335
  switch_code += case_code
336
336
 
337
- switch_code += f"default: return ({str(default)}); break;"
337
+ switch_code += f"default: return ({default!s}); break;"
338
338
  switch_code += "};})()"
339
339
 
340
340
  return switch_code
@@ -413,7 +413,7 @@ def format_props(*single_props, **key_value_props) -> list[str]:
413
413
  )
414
414
  for name, prop in sorted(key_value_props.items())
415
415
  if prop is not None
416
- ] + [(f"{str(LiteralVar.create(prop))}") for prop in single_props]
416
+ ] + [(f"{LiteralVar.create(prop)!s}") for prop in single_props]
417
417
 
418
418
 
419
419
  def get_event_handler_parts(handler: EventHandler) -> tuple[str, str]:
@@ -664,18 +664,22 @@ def format_library_name(library_fullname: str):
664
664
  return lib
665
665
 
666
666
 
667
- def json_dumps(obj: Any) -> str:
667
+ def json_dumps(obj: Any, **kwargs) -> str:
668
668
  """Takes an object and returns a jsonified string.
669
669
 
670
670
  Args:
671
671
  obj: The object to be serialized.
672
+ kwargs: Additional keyword arguments to pass to json.dumps.
672
673
 
673
674
  Returns:
674
675
  A string
675
676
  """
676
677
  from reflex.utils import serializers
677
678
 
678
- return json.dumps(obj, ensure_ascii=False, default=serializers.serialize)
679
+ kwargs.setdefault("ensure_ascii", False)
680
+ kwargs.setdefault("default", serializers.serialize)
681
+
682
+ return json.dumps(obj, **kwargs)
679
683
 
680
684
 
681
685
  def collect_form_dict_names(form_dict: dict[str, Any]) -> dict[str, Any]:
@@ -712,8 +716,7 @@ def format_array_ref(refs: str, idx: Var | None) -> str:
712
716
  """
713
717
  clean_ref = re.sub(r"[^\w]+", "_", refs)
714
718
  if idx is not None:
715
- # idx._var_is_local = True
716
- return f"refs_{clean_ref}[{str(idx)}]"
719
+ return f"refs_{clean_ref}[{idx!s}]"
717
720
  return f"refs_{clean_ref}"
718
721
 
719
722
 
reflex/utils/path_ops.py CHANGED
@@ -205,14 +205,14 @@ def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):
205
205
  # Read the existing json object from the file.
206
206
  json_object = {}
207
207
  if fp.stat().st_size:
208
- with open(fp) as f:
208
+ with fp.open() as f:
209
209
  json_object = json.load(f)
210
210
 
211
211
  # Update the json object with the new data.
212
212
  json_object.update(update_dict)
213
213
 
214
214
  # Write the updated json object to the file
215
- with open(fp, "w") as f:
215
+ with fp.open("w") as f:
216
216
  json.dump(json_object, f, ensure_ascii=False)
217
217
 
218
218
 
@@ -219,9 +219,8 @@ def get_install_package_manager(on_failure_return_none: bool = False) -> str | N
219
219
  Returns:
220
220
  The path to the package manager.
221
221
  """
222
- if (
223
- constants.IS_WINDOWS
224
- and not is_windows_bun_supported()
222
+ if constants.IS_WINDOWS and (
223
+ not is_windows_bun_supported()
225
224
  or windows_check_onedrive_in_path()
226
225
  or windows_npm_escape_hatch()
227
226
  ):
@@ -291,7 +290,7 @@ def get_app(reload: bool = False) -> ModuleType:
291
290
  "If this error occurs in a reflex test case, ensure that `get_app` is mocked."
292
291
  )
293
292
  module = config.module
294
- sys.path.insert(0, os.getcwd())
293
+ sys.path.insert(0, str(Path.cwd()))
295
294
  app = __import__(module, fromlist=(constants.CompileVars.APP,))
296
295
 
297
296
  if reload:
@@ -439,9 +438,11 @@ def create_config(app_name: str):
439
438
  from reflex.compiler import templates
440
439
 
441
440
  config_name = f"{re.sub(r'[^a-zA-Z]', '', app_name).capitalize()}Config"
442
- with open(constants.Config.FILE, "w") as f:
443
- console.debug(f"Creating {constants.Config.FILE}")
444
- f.write(templates.RXCONFIG.render(app_name=app_name, config_name=config_name))
441
+
442
+ console.debug(f"Creating {constants.Config.FILE}")
443
+ constants.Config.FILE.write_text(
444
+ templates.RXCONFIG.render(app_name=app_name, config_name=config_name)
445
+ )
445
446
 
446
447
 
447
448
  def initialize_gitignore(
@@ -495,14 +496,14 @@ def initialize_requirements_txt():
495
496
  console.debug(f"Detected encoding for {fp} as {encoding}.")
496
497
  try:
497
498
  other_requirements_exist = False
498
- with open(fp, "r", encoding=encoding) as f:
499
- for req in f.readlines():
499
+ with fp.open("r", encoding=encoding) as f:
500
+ for req in f:
500
501
  # Check if we have a package name that is reflex
501
502
  if re.match(r"^reflex[^a-zA-Z0-9]", req):
502
503
  console.debug(f"{fp} already has reflex as dependency.")
503
504
  return
504
505
  other_requirements_exist = True
505
- with open(fp, "a", encoding=encoding) as f:
506
+ with fp.open("a", encoding=encoding) as f:
506
507
  preceding_newline = "\n" if other_requirements_exist else ""
507
508
  f.write(
508
509
  f"{preceding_newline}{constants.RequirementsTxt.DEFAULTS_STUB}{constants.Reflex.VERSION}\n"
@@ -700,7 +701,7 @@ def _update_next_config(
700
701
  }
701
702
  if transpile_packages:
702
703
  next_config["transpilePackages"] = list(
703
- set((format_library_name(p) for p in transpile_packages))
704
+ {format_library_name(p) for p in transpile_packages}
704
705
  )
705
706
  if export:
706
707
  next_config["output"] = "export"
@@ -733,13 +734,13 @@ def download_and_run(url: str, *args, show_status: bool = False, **env):
733
734
  response.raise_for_status()
734
735
 
735
736
  # Save the script to a temporary file.
736
- script = tempfile.NamedTemporaryFile()
737
- with open(script.name, "w") as f:
738
- f.write(response.text)
737
+ script = Path(tempfile.NamedTemporaryFile().name)
738
+
739
+ script.write_text(response.text)
739
740
 
740
741
  # Run the script.
741
742
  env = {**os.environ, **env}
742
- process = processes.new_process(["bash", f.name, *args], env=env)
743
+ process = processes.new_process(["bash", str(script), *args], env=env)
743
744
  show = processes.show_status if show_status else processes.show_logs
744
745
  show(f"Installing {url}", process)
745
746
 
@@ -753,14 +754,14 @@ def download_and_extract_fnm_zip():
753
754
  # Download the zip file
754
755
  url = constants.Fnm.INSTALL_URL
755
756
  console.debug(f"Downloading {url}")
756
- fnm_zip_file = constants.Fnm.DIR / f"{constants.Fnm.FILENAME}.zip"
757
+ fnm_zip_file: Path = constants.Fnm.DIR / f"{constants.Fnm.FILENAME}.zip"
757
758
  # Function to download and extract the FNM zip release.
758
759
  try:
759
760
  # Download the FNM zip release.
760
761
  # TODO: show progress to improve UX
761
762
  response = net.get(url, follow_redirects=True)
762
763
  response.raise_for_status()
763
- with open(fnm_zip_file, "wb") as output_file:
764
+ with fnm_zip_file.open("wb") as output_file:
764
765
  for chunk in response.iter_bytes():
765
766
  output_file.write(chunk)
766
767
 
@@ -808,7 +809,7 @@ def install_node():
808
809
  )
809
810
  else: # All other platforms (Linux, MacOS).
810
811
  # Add execute permissions to fnm executable.
811
- os.chmod(constants.Fnm.EXE, stat.S_IXUSR)
812
+ constants.Fnm.EXE.chmod(stat.S_IXUSR)
812
813
  # Install node.
813
814
  # Specify arm64 arch explicitly for M1s and M2s.
814
815
  architecture_arg = (
@@ -834,7 +835,7 @@ def install_bun():
834
835
  """Install bun onto the user's system."""
835
836
  win_supported = is_windows_bun_supported()
836
837
  one_drive_in_path = windows_check_onedrive_in_path()
837
- if constants.IS_WINDOWS and not win_supported or one_drive_in_path:
838
+ if constants.IS_WINDOWS and (not win_supported or one_drive_in_path):
838
839
  if not win_supported:
839
840
  console.warn(
840
841
  "Bun for Windows is currently only available for x86 64-bit Windows. Installation will fall back on npm."
@@ -926,7 +927,7 @@ def cached_procedure(cache_file: str, payload_fn: Callable[..., str]):
926
927
 
927
928
  @cached_procedure(
928
929
  cache_file=str(get_web_dir() / "reflex.install_frontend_packages.cached"),
929
- payload_fn=lambda p, c: f"{repr(sorted(list(p)))},{c.json()}",
930
+ payload_fn=lambda p, c: f"{sorted(p)!r},{c.json()}",
930
931
  )
931
932
  def install_frontend_packages(packages: set[str], config: Config):
932
933
  """Installs the base and custom frontend packages.
@@ -946,9 +947,12 @@ def install_frontend_packages(packages: set[str], config: Config):
946
947
  get_package_manager(on_failure_return_none=True)
947
948
  if (
948
949
  not constants.IS_WINDOWS
949
- or constants.IS_WINDOWS
950
- and is_windows_bun_supported()
951
- and not windows_check_onedrive_in_path()
950
+ or (
951
+ constants.IS_WINDOWS
952
+ and (
953
+ is_windows_bun_supported() and not windows_check_onedrive_in_path()
954
+ )
955
+ )
952
956
  )
953
957
  else None
954
958
  )
@@ -1298,7 +1302,7 @@ def fetch_app_templates(version: str) -> dict[str, Template]:
1298
1302
  for tp in templates_data:
1299
1303
  if tp["hidden"] or tp["code_url"] is None:
1300
1304
  continue
1301
- known_fields = set(f.name for f in dataclasses.fields(Template))
1305
+ known_fields = {f.name for f in dataclasses.fields(Template)}
1302
1306
  filtered_templates[tp["name"]] = Template(
1303
1307
  **{k: v for k, v in tp.items() if k in known_fields}
1304
1308
  )
@@ -1324,7 +1328,7 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1324
1328
  raise typer.Exit(1) from ose
1325
1329
 
1326
1330
  # Use httpx GET with redirects to download the zip file.
1327
- zip_file_path = Path(temp_dir) / "template.zip"
1331
+ zip_file_path: Path = Path(temp_dir) / "template.zip"
1328
1332
  try:
1329
1333
  # Note: following redirects can be risky. We only allow this for reflex built templates at the moment.
1330
1334
  response = net.get(template_url, follow_redirects=True)
@@ -1334,9 +1338,8 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
1334
1338
  console.error(f"Failed to download the template: {he}")
1335
1339
  raise typer.Exit(1) from he
1336
1340
  try:
1337
- with open(zip_file_path, "wb") as f:
1338
- f.write(response.content)
1339
- console.debug(f"Downloaded the zip to {zip_file_path}")
1341
+ zip_file_path.write_bytes(response.content)
1342
+ console.debug(f"Downloaded the zip to {zip_file_path}")
1340
1343
  except OSError as ose:
1341
1344
  console.error(f"Unable to write the downloaded zip to disk {ose}")
1342
1345
  raise typer.Exit(1) from ose
reflex/utils/processes.py CHANGED
@@ -58,7 +58,9 @@ def get_process_on_port(port) -> Optional[psutil.Process]:
58
58
  The process on the given port.
59
59
  """
60
60
  for proc in psutil.process_iter(["pid", "name", "cmdline"]):
61
- try:
61
+ with contextlib.suppress(
62
+ psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess
63
+ ):
62
64
  if importlib.metadata.version("psutil") >= "6.0.0":
63
65
  conns = proc.net_connections(kind="inet") # type: ignore
64
66
  else:
@@ -66,8 +68,6 @@ def get_process_on_port(port) -> Optional[psutil.Process]:
66
68
  for conn in conns:
67
69
  if conn.laddr.port == int(port):
68
70
  return proc
69
- except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
70
- pass
71
71
  return None
72
72
 
73
73
 
@@ -364,7 +364,7 @@ def get_command_with_loglevel(command: list[str]) -> list[str]:
364
364
  npm_path = str(Path(npm_path).resolve()) if npm_path else npm_path
365
365
 
366
366
  if command[0] == npm_path:
367
- return command + ["--loglevel", "silly"]
367
+ return [*command, "--loglevel", "silly"]
368
368
  return command
369
369
 
370
370
 
@@ -24,7 +24,7 @@ from reflex.vars.base import Var
24
24
 
25
25
  logger = logging.getLogger("pyi_generator")
26
26
 
27
- PWD = Path(".").resolve()
27
+ PWD = Path.cwd()
28
28
 
29
29
  EXCLUDED_FILES = [
30
30
  "app.py",
@@ -196,12 +196,7 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
196
196
  elif isinstance(value, str):
197
197
  ev = eval(value, type_hint_globals)
198
198
  if rx_types.is_optional(ev):
199
- # hints = {
200
- # _get_type_hint(arg, type_hint_globals, is_optional=False)
201
- # for arg in ev.__args__
202
- # }
203
199
  return _get_type_hint(ev, type_hint_globals, is_optional=False)
204
- # return f"Optional[{', '.join(hints)}]"
205
200
 
206
201
  if rx_types.is_union(ev):
207
202
  res = [
@@ -260,8 +255,15 @@ def _generate_docstrings(clzs: list[Type[Component]], props: list[str]) -> str:
260
255
  # We've reached the functions, so stop.
261
256
  break
262
257
 
258
+ if line == "":
259
+ # We hit a blank line, so clear comments to avoid commented out prop appearing in next prop docs.
260
+ comments.clear()
261
+ continue
262
+
263
263
  # Get comments for prop
264
264
  if line.strip().startswith("#"):
265
+ # Remove noqa from the comments.
266
+ line = line.partition(" # noqa")[0]
265
267
  comments.append(line)
266
268
  continue
267
269
 
@@ -285,10 +287,9 @@ def _generate_docstrings(clzs: list[Type[Component]], props: list[str]) -> str:
285
287
  for line in (clz.create.__doc__ or "").splitlines():
286
288
  if "**" in line:
287
289
  indent = line.split("**")[0]
288
- for nline in [
289
- f"{indent}{n}:{' '.join(c)}" for n, c in props_comments.items()
290
- ]:
291
- new_docstring.append(nline)
290
+ new_docstring.extend(
291
+ [f"{indent}{n}:{' '.join(c)}" for n, c in props_comments.items()]
292
+ )
292
293
  new_docstring.append(line)
293
294
  return "\n".join(new_docstring)
294
295
 
@@ -835,7 +836,7 @@ class StubGenerator(ast.NodeTransformer):
835
836
  self.inserted_imports = True
836
837
  default_imports = _generate_imports(self.typing_imports)
837
838
  self.import_statements.extend(ast.unparse(i) for i in default_imports)
838
- return default_imports + [node]
839
+ return [*default_imports, node]
839
840
  return node
840
841
 
841
842
  def visit_ImportFrom(
reflex/utils/types.py CHANGED
@@ -97,7 +97,6 @@ StateIterVar = Union[list, set, tuple]
97
97
  if TYPE_CHECKING:
98
98
  from reflex.vars.base import Var
99
99
 
100
- # ArgsSpec = Callable[[Var], list[Var]]
101
100
  ArgsSpec = (
102
101
  Callable[[], Sequence[Var]]
103
102
  | Callable[[Var], Sequence[Var]]
@@ -331,7 +330,11 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
331
330
  type_ = field.outer_type_
332
331
  if isinstance(type_, ModelField):
333
332
  type_ = type_.type_
334
- if not field.required and field.default is None:
333
+ if (
334
+ not field.required
335
+ and field.default is None
336
+ and field.default_factory is None
337
+ ):
335
338
  # Ensure frontend uses null coalescing when accessing.
336
339
  type_ = Optional[type_]
337
340
  return type_
@@ -783,7 +786,7 @@ def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
783
786
  )
784
787
  value_str = f"'{value}'" if isinstance(value, str) else value
785
788
  raise ValueError(
786
- f"prop value for {str(key)} of the `{comp_name}` component should be one of the following: {allowed_value_str}. Got {value_str} instead"
789
+ f"prop value for {key!s} of the `{comp_name}` component should be one of the following: {allowed_value_str}. Got {value_str} instead"
787
790
  )
788
791
 
789
792
 
reflex/vars/__init__.py CHANGED
@@ -9,6 +9,7 @@ from .base import get_unique_variable_name as get_unique_variable_name
9
9
  from .base import get_uuid_string_var as get_uuid_string_var
10
10
  from .base import var_operation as var_operation
11
11
  from .base import var_operation_return as var_operation_return
12
+ from .datetime import DateTimeVar as DateTimeVar
12
13
  from .function import FunctionStringVar as FunctionStringVar
13
14
  from .function import FunctionVar as FunctionVar
14
15
  from .function import VarOperationCall as VarOperationCall