reflex 0.8.0a3__py3-none-any.whl → 0.8.0a5__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 (35) hide show
  1. reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
  2. reflex/.templates/web/app/routes.js +3 -3
  3. reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
  4. reflex/.templates/web/utils/client_side_routing.js +1 -1
  5. reflex/.templates/web/utils/state.js +32 -21
  6. reflex/.templates/web/vite.config.js +6 -0
  7. reflex/app.py +50 -46
  8. reflex/compiler/compiler.py +26 -10
  9. reflex/compiler/utils.py +4 -2
  10. reflex/components/base/meta.py +4 -15
  11. reflex/components/core/foreach.py +2 -2
  12. reflex/components/core/match.py +3 -3
  13. reflex/components/core/upload.py +2 -1
  14. reflex/components/datadisplay/code.py +12 -7
  15. reflex/components/datadisplay/shiki_code_block.py +5 -3
  16. reflex/components/markdown/markdown.py +5 -3
  17. reflex/components/plotly/plotly.py +12 -6
  18. reflex/components/radix/themes/color_mode.py +5 -6
  19. reflex/components/recharts/cartesian.py +9 -2
  20. reflex/constants/compiler.py +7 -0
  21. reflex/constants/route.py +13 -6
  22. reflex/environment.py +6 -4
  23. reflex/route.py +159 -71
  24. reflex/state.py +21 -4
  25. reflex/utils/exec.py +10 -10
  26. reflex/utils/format.py +1 -5
  27. reflex/utils/misc.py +40 -0
  28. reflex/utils/prerequisites.py +6 -11
  29. reflex/utils/pyi_generator.py +23 -40
  30. reflex/utils/types.py +1 -1
  31. {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/METADATA +2 -2
  32. {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/RECORD +35 -34
  33. {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/WHEEL +0 -0
  34. {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/entry_points.txt +0 -0
  35. {reflex-0.8.0a3.dist-info → reflex-0.8.0a5.dist-info}/licenses/LICENSE +0 -0
reflex/environment.py CHANGED
@@ -69,7 +69,7 @@ def interpret_boolean_env(value: str, field_name: str) -> bool:
69
69
  return True
70
70
  if value.lower() in false_values:
71
71
  return False
72
- msg = f"Invalid boolean value: {value} for {field_name}"
72
+ msg = f"Invalid boolean value: {value!r} for {field_name}"
73
73
  raise EnvironmentVarValueError(msg)
74
74
 
75
75
 
@@ -89,7 +89,7 @@ def interpret_int_env(value: str, field_name: str) -> int:
89
89
  try:
90
90
  return int(value)
91
91
  except ValueError as ve:
92
- msg = f"Invalid integer value: {value} for {field_name}"
92
+ msg = f"Invalid integer value: {value!r} for {field_name}"
93
93
  raise EnvironmentVarValueError(msg) from ve
94
94
 
95
95
 
@@ -108,7 +108,7 @@ def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
108
108
  """
109
109
  path = Path(value)
110
110
  if not path.exists():
111
- msg = f"Path does not exist: {path} for {field_name}"
111
+ msg = f"Path does not exist: {path!r} for {field_name}"
112
112
  raise EnvironmentVarValueError(msg)
113
113
  return path
114
114
 
@@ -143,7 +143,7 @@ def interpret_enum_env(value: str, field_type: GenericType, field_name: str) ->
143
143
  try:
144
144
  return field_type(value)
145
145
  except ValueError as ve:
146
- msg = f"Invalid enum value: {value} for {field_name}"
146
+ msg = f"Invalid enum value: {value!r} for {field_name}"
147
147
  raise EnvironmentVarValueError(msg) from ve
148
148
 
149
149
 
@@ -169,6 +169,8 @@ def interpret_env_var_value(
169
169
  msg = f"Union types are not supported for environment variables: {field_name}."
170
170
  raise ValueError(msg)
171
171
 
172
+ value = value.strip()
173
+
172
174
  if field_type is bool:
173
175
  return interpret_boolean_env(value, field_name)
174
176
  if field_type is str:
reflex/route.py CHANGED
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import re
6
+ from collections.abc import Callable
6
7
 
7
8
  from reflex import constants
8
9
 
@@ -16,13 +17,39 @@ def verify_route_validity(route: str) -> None:
16
17
  Raises:
17
18
  ValueError: If the route is invalid.
18
19
  """
19
- pattern = catchall_in_route(route)
20
- if pattern:
21
- if pattern != "[[...splat]]":
22
- msg = f"Catchall pattern `{pattern}` is not valid. Only `[[...splat]]` is allowed."
20
+ route_parts = route.removeprefix("/").split("/")
21
+ for i, part in enumerate(route_parts):
22
+ if constants.RouteRegex.SLUG.fullmatch(part):
23
+ continue
24
+ if not part.startswith("[") or not part.endswith("]"):
25
+ msg = (
26
+ f"Route part `{part}` is not valid. Reflex only supports "
27
+ "alphabetic characters, underscores, and hyphens in route parts. "
28
+ )
23
29
  raise ValueError(msg)
24
- if not route.endswith(pattern):
25
- msg = f"Catchall pattern `{pattern}` must be at the end of the route."
30
+ if part.startswith(("[[...", "[...")):
31
+ if part != constants.RouteRegex.SPLAT_CATCHALL:
32
+ msg = f"Catchall pattern `{part}` is not valid. Only `{constants.RouteRegex.SPLAT_CATCHALL}` is allowed."
33
+ raise ValueError(msg)
34
+ if i != len(route_parts) - 1:
35
+ msg = f"Catchall pattern `{part}` must be at the end of the route."
36
+ raise ValueError(msg)
37
+ continue
38
+ if part.startswith("[["):
39
+ if constants.RouteRegex.OPTIONAL_ARG.fullmatch(part):
40
+ continue
41
+ msg = (
42
+ f"Route part `{part}` with optional argument is not valid. "
43
+ "Reflex only supports optional arguments that start with an alphabetic character or underscore, "
44
+ "followed by alphanumeric characters or underscores."
45
+ )
46
+ raise ValueError(msg)
47
+ if not constants.RouteRegex.ARG.fullmatch(part):
48
+ msg = (
49
+ f"Route part `{part}` with argument is not valid. "
50
+ "Reflex only supports argument names that start with an alphabetic character or underscore, "
51
+ "followed by alphanumeric characters or underscores."
52
+ )
26
53
  raise ValueError(msg)
27
54
 
28
55
 
@@ -37,98 +64,159 @@ def get_route_args(route: str) -> dict[str, str]:
37
64
  """
38
65
  args = {}
39
66
 
40
- def add_route_arg(match: re.Match[str], type_: str):
41
- """Add arg from regex search result.
42
-
43
- Args:
44
- match: Result of a regex search
45
- type_: The assigned type for this arg
46
-
47
- Raises:
48
- ValueError: If the route is invalid.
49
- """
50
- arg_name = match.groups()[0]
67
+ def _add_route_arg(arg_name: str, type_: str):
51
68
  if arg_name in args:
52
- msg = f"Arg name [{arg_name}] is used more than once in this URL"
69
+ msg = (
70
+ f"Arg name `{arg_name}` is used more than once in the route `{route}`."
71
+ )
53
72
  raise ValueError(msg)
54
73
  args[arg_name] = type_
55
74
 
56
75
  # Regex to check for route args.
57
- check = constants.RouteRegex.ARG
58
- check_strict_catchall = constants.RouteRegex.STRICT_CATCHALL
59
- check_opt_catchall = constants.RouteRegex.OPT_CATCHALL
76
+ argument_regex = constants.RouteRegex.ARG
77
+ optional_argument_regex = constants.RouteRegex.OPTIONAL_ARG
60
78
 
61
79
  # Iterate over the route parts and check for route args.
62
80
  for part in route.split("/"):
63
- match_opt = check_opt_catchall.match(part)
64
- if match_opt:
65
- add_route_arg(match_opt, constants.RouteArgType.LIST)
81
+ if part == constants.RouteRegex.SPLAT_CATCHALL:
82
+ _add_route_arg("splat", constants.RouteArgType.LIST)
66
83
  break
67
84
 
68
- match_strict = check_strict_catchall.match(part)
69
- if match_strict:
70
- add_route_arg(match_strict, constants.RouteArgType.LIST)
71
- break
85
+ optional_argument = optional_argument_regex.match(part)
86
+ if optional_argument:
87
+ _add_route_arg(optional_argument.group(1), constants.RouteArgType.SINGLE)
88
+ continue
89
+
90
+ argument = argument_regex.match(part)
91
+ if argument:
92
+ _add_route_arg(argument.group(1), constants.RouteArgType.SINGLE)
93
+ continue
72
94
 
73
- match = check.match(part)
74
- if match:
75
- # Add the route arg to the list.
76
- add_route_arg(match, constants.RouteArgType.SINGLE)
77
95
  return args
78
96
 
79
97
 
80
- def catchall_in_route(route: str) -> str:
81
- """Extract the catchall part from a route.
98
+ def replace_brackets_with_keywords(input_string: str) -> str:
99
+ """Replace brackets and everything inside it in a string with a keyword.
82
100
 
83
101
  Example:
84
- >>> catchall_in_route("/posts/[...slug]")
85
- '[...slug]'
86
- >>> catchall_in_route("/posts/[[...slug]]")
87
- '[[...slug]]'
88
- >>> catchall_in_route("/posts/[slug]")
89
- ''
102
+ >>> replace_brackets_with_keywords("/posts")
103
+ '/posts'
104
+ >>> replace_brackets_with_keywords("/posts/[slug]")
105
+ '/posts/__SINGLE_SEGMENT__'
106
+ >>> replace_brackets_with_keywords("/posts/[slug]/comments")
107
+ '/posts/__SINGLE_SEGMENT__/comments'
108
+ >>> replace_brackets_with_keywords("/posts/[[slug]]")
109
+ '/posts/__DOUBLE_SEGMENT__'
110
+ >>> replace_brackets_with_keywords("/posts/[[...splat]]")
111
+ '/posts/__DOUBLE_CATCHALL_SEGMENT__'
90
112
 
91
113
  Args:
92
- route: the route from which to extract
114
+ input_string: String to replace.
93
115
 
94
116
  Returns:
95
- str: the catchall part of the URI
117
+ new string containing keywords.
96
118
  """
97
- match_ = constants.RouteRegex.CATCHALL.search(route)
98
- return match_.group() if match_ else ""
119
+ # Replace [<slug>] with __SINGLE_SEGMENT__
120
+ return constants.RouteRegex.ARG.sub(
121
+ constants.RouteRegex.SINGLE_SEGMENT,
122
+ # Replace [[slug]] with __DOUBLE_SEGMENT__
123
+ constants.RouteRegex.OPTIONAL_ARG.sub(
124
+ constants.RouteRegex.DOUBLE_SEGMENT,
125
+ # Replace [[...splat]] with __DOUBLE_CATCHALL_SEGMENT__
126
+ input_string.replace(
127
+ constants.RouteRegex.SPLAT_CATCHALL,
128
+ constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
129
+ ),
130
+ ),
131
+ )
99
132
 
100
133
 
101
- def replace_brackets_with_keywords(input_string: str) -> str:
102
- """Replace brackets and everything inside it in a string with a keyword.
134
+ def route_specifity(keyworded_route: str) -> tuple[int, int, int]:
135
+ """Get the specificity of a route with keywords.
136
+
137
+ The smaller the number, the more specific the route is.
103
138
 
104
139
  Args:
105
- input_string: String to replace.
140
+ keyworded_route: The route with keywords.
106
141
 
107
142
  Returns:
108
- new string containing keywords.
143
+ A tuple containing the counts of double catchall segments,
144
+ double segments, and single segments in the route.
109
145
  """
110
- # /posts -> /post
111
- # /posts/[slug] -> /posts/__SINGLE_SEGMENT__
112
- # /posts/[slug]/comments -> /posts/__SINGLE_SEGMENT__/comments
113
- # /posts/[[slug]] -> /posts/__DOUBLE_SEGMENT__
114
- # / posts/[[...slug2]]-> /posts/__DOUBLE_CATCHALL_SEGMENT__
115
- # /posts/[...slug3]-> /posts/__SINGLE_CATCHALL_SEGMENT__
116
-
117
- # Replace [[...<slug>]] with __DOUBLE_CATCHALL_SEGMENT__
118
- output_string = re.sub(
119
- r"\[\[\.\.\..+?\]\]",
120
- constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
121
- input_string,
122
- )
123
- # Replace [...<slug>] with __SINGLE_CATCHALL_SEGMENT__
124
- output_string = re.sub(
125
- r"\[\.\.\..+?\]",
126
- constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
127
- output_string,
146
+ return (
147
+ keyworded_route.count(constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT),
148
+ keyworded_route.count(constants.RouteRegex.DOUBLE_SEGMENT),
149
+ keyworded_route.count(constants.RouteRegex.SINGLE_SEGMENT),
128
150
  )
129
- # Replace [[<slug>]] with __DOUBLE_SEGMENT__
130
- output_string = re.sub(
131
- r"\[\[.+?\]\]", constants.RouteRegex.DOUBLE_SEGMENT, output_string
151
+
152
+
153
+ def get_route_regex(keyworded_route: str) -> re.Pattern:
154
+ """Get the regex pattern for a route with keywords.
155
+
156
+ Args:
157
+ keyworded_route: The route with keywords.
158
+
159
+ Returns:
160
+ A compiled regex pattern for the route.
161
+ """
162
+ if keyworded_route == "index":
163
+ return re.compile(re.escape("/"))
164
+ path_parts = keyworded_route.split("/")
165
+ regex_parts = []
166
+ for part in path_parts:
167
+ if part == constants.RouteRegex.SINGLE_SEGMENT:
168
+ # Match a single segment (/slug)
169
+ regex_parts.append(r"/[^/]*")
170
+ elif part == constants.RouteRegex.DOUBLE_SEGMENT:
171
+ # Match a single optional segment (/slug or nothing)
172
+ regex_parts.append(r"(/[^/]+)?")
173
+ elif part == constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT:
174
+ regex_parts.append(".*")
175
+ else:
176
+ regex_parts.append(re.escape("/" + part))
177
+ # Join the regex parts and compile the regex
178
+ regex_pattern = "".join(regex_parts)
179
+ regex_pattern = f"^{regex_pattern}/?$"
180
+ return re.compile(regex_pattern)
181
+
182
+
183
+ def get_router(routes: list[str]) -> Callable[[str], str | None]:
184
+ """Get a function that computes the route for a given path.
185
+
186
+ Args:
187
+ routes: A list of routes to match against.
188
+
189
+ Returns:
190
+ A function that takes a path and returns the first matching route,
191
+ or None if no match is found.
192
+ """
193
+ keyworded_routes = {
194
+ replace_brackets_with_keywords(route): route for route in routes
195
+ }
196
+ sorted_routes_by_specifity = sorted(
197
+ keyworded_routes.items(),
198
+ key=lambda item: route_specifity(item[0]),
132
199
  )
133
- # Replace [<slug>] with __SINGLE_SEGMENT__
134
- return re.sub(r"\[.+?\]", constants.RouteRegex.SINGLE_SEGMENT, output_string)
200
+ regexed_routes = [
201
+ (get_route_regex(keyworded_route), original_route)
202
+ for keyworded_route, original_route in sorted_routes_by_specifity
203
+ ]
204
+
205
+ def get_route(path: str) -> str | None:
206
+ """Get the first matching route for a given path.
207
+
208
+ Args:
209
+ path: The path to match against the routes.
210
+
211
+ Returns:
212
+ The first matching route, or None if no match is found.
213
+ """
214
+ path = "/" + path.removeprefix("/").removesuffix("/")
215
+ if path == "/index":
216
+ path = "/"
217
+ for regex, original_route in regexed_routes:
218
+ if regex.fullmatch(path):
219
+ return original_route
220
+ return None
221
+
222
+ return get_route
reflex/state.py CHANGED
@@ -2053,11 +2053,28 @@ class BaseState(EvenMoreBasicBaseState):
2053
2053
 
2054
2054
  Returns:
2055
2055
  The value of the field.
2056
+
2057
+ Raises:
2058
+ TypeError: If the key is not a string or MutableProxy.
2056
2059
  """
2057
- value = getattr(self, key)
2058
- if isinstance(value, MutableProxy):
2059
- return value.__wrapped__
2060
- return value
2060
+ if isinstance(key, MutableProxy):
2061
+ # Legacy behavior from v0.7.14: handle non-string keys with deprecation warning
2062
+ from reflex.utils import console
2063
+
2064
+ console.deprecate(
2065
+ feature_name="Non-string keys in get_value",
2066
+ reason="Passing non-string keys to get_value is deprecated and will no longer be supported",
2067
+ deprecation_version="0.8.0",
2068
+ removal_version="0.9.0",
2069
+ )
2070
+
2071
+ return key.__wrapped__
2072
+
2073
+ if isinstance(key, str):
2074
+ return getattr(self, key)
2075
+
2076
+ msg = f"Invalid key type: {type(key)}. Expected str."
2077
+ raise TypeError(msg)
2061
2078
 
2062
2079
  def dict(
2063
2080
  self, include_computed: bool = True, initial: bool = False, **kwargs
reflex/utils/exec.py CHANGED
@@ -23,6 +23,7 @@ from reflex.constants.base import LogLevel
23
23
  from reflex.environment import environment
24
24
  from reflex.utils import console, path_ops
25
25
  from reflex.utils.decorator import once
26
+ from reflex.utils.misc import get_module_path
26
27
  from reflex.utils.prerequisites import get_web_dir
27
28
 
28
29
  # For uvicorn windows bug fix (#2335)
@@ -323,15 +324,12 @@ def get_app_file() -> Path:
323
324
  if current_working_dir not in sys.path:
324
325
  # Add the current working directory to sys.path
325
326
  sys.path.insert(0, current_working_dir)
326
- module_spec = importlib.util.find_spec(get_app_module())
327
- if module_spec is None:
328
- msg = f"Module {get_app_module()} not found. Make sure the module is installed."
327
+ app_module = get_app_module()
328
+ module_path = get_module_path(app_module)
329
+ if module_path is None:
330
+ msg = f"Module {app_module} not found. Make sure the module is installed."
329
331
  raise ImportError(msg)
330
- file_name = module_spec.origin
331
- if file_name is None:
332
- msg = f"Module {get_app_module()} not found. Make sure the module is installed."
333
- raise ImportError(msg)
334
- return Path(file_name).resolve()
332
+ return module_path
335
333
 
336
334
 
337
335
  def get_app_instance_from_file() -> str:
@@ -396,8 +394,10 @@ def get_reload_paths() -> Sequence[Path]:
396
394
  """
397
395
  config = get_config()
398
396
  reload_paths = [Path.cwd()]
399
- if (spec := importlib.util.find_spec(config.module)) is not None and spec.origin:
400
- module_path = Path(spec.origin).resolve().parent
397
+ app_module = config.module
398
+ module_path = get_module_path(app_module)
399
+ if module_path is not None:
400
+ module_path = module_path.parent
401
401
 
402
402
  while module_path.parent.name and _has_child_file(module_path, "__init__.py"):
403
403
  if _has_child_file(module_path, "rxconfig.py"):
reflex/utils/format.py CHANGED
@@ -310,20 +310,16 @@ def format_var(var: Var) -> str:
310
310
  return str(var)
311
311
 
312
312
 
313
- def format_route(route: str, format_case: bool = True) -> str:
313
+ def format_route(route: str) -> str:
314
314
  """Format the given route.
315
315
 
316
316
  Args:
317
317
  route: The route to format.
318
- format_case: whether to format case to kebab case.
319
318
 
320
319
  Returns:
321
320
  The formatted route.
322
321
  """
323
322
  route = route.strip("/")
324
- # Strip the route and format casing.
325
- if format_case:
326
- route = to_kebab_case(route)
327
323
 
328
324
  # If the route is empty, return the index route.
329
325
  if route == "":
reflex/utils/misc.py CHANGED
@@ -9,6 +9,46 @@ from pathlib import Path
9
9
  from typing import Any
10
10
 
11
11
 
12
+ def get_module_path(module_name: str) -> Path | None:
13
+ """Check if a module exists and return its path.
14
+
15
+ This function searches for a module by navigating through the module hierarchy
16
+ in each path of sys.path, checking for both .py files and packages with __init__.py.
17
+
18
+ Args:
19
+ module_name: The name of the module to search for (e.g., "package.submodule").
20
+
21
+ Returns:
22
+ The path to the module file if found, None otherwise.
23
+ """
24
+ parts = module_name.split(".")
25
+
26
+ # Check each path in sys.path
27
+ for path in sys.path:
28
+ current_path = Path(path)
29
+
30
+ # Navigate through the module hierarchy
31
+ for i, part in enumerate(parts):
32
+ potential_file = current_path / (part + ".py")
33
+ potential_dir = current_path / part
34
+ potential_init = current_path / part / "__init__.py"
35
+
36
+ if potential_file.is_file():
37
+ # We encountered a file, but we can't continue deeper
38
+ if i == len(parts) - 1:
39
+ return potential_file
40
+ return None # Can't continue deeper
41
+ if potential_dir.is_dir() and potential_init.is_file():
42
+ # It's a package, so we can continue deeper
43
+ current_path = potential_dir
44
+ else:
45
+ break # Path doesn't exist, break out of the loop
46
+ else:
47
+ return current_path / "__init__.py" # Made it through all parts
48
+
49
+ return None
50
+
51
+
12
52
  async def run_in_thread(func: Callable) -> Any:
13
53
  """Run a function in a separate thread.
14
54
 
@@ -40,6 +40,7 @@ from reflex.config import Config, get_config
40
40
  from reflex.environment import environment
41
41
  from reflex.utils import console, net, path_ops, processes, redir
42
42
  from reflex.utils.exceptions import SystemPackageMissingError
43
+ from reflex.utils.misc import get_module_path
43
44
  from reflex.utils.registry import get_npm_registry
44
45
 
45
46
  if typing.TYPE_CHECKING:
@@ -348,14 +349,11 @@ def _check_app_name(config: Config):
348
349
  )
349
350
  raise RuntimeError(msg)
350
351
 
351
- from reflex.utils.misc import with_cwd_in_syspath
352
+ from reflex.utils.misc import get_module_path, with_cwd_in_syspath
352
353
 
353
354
  with with_cwd_in_syspath():
354
- try:
355
- mod_spec = importlib.util.find_spec(config.module)
356
- except ModuleNotFoundError:
357
- mod_spec = None
358
- if mod_spec is None:
355
+ module_path = get_module_path(config.module)
356
+ if module_path is None:
359
357
  msg = f"Module {config.module} not found. "
360
358
  if config.app_module_import is not None:
361
359
  msg += f"Ensure app_module_import='{config.app_module_import}' in rxconfig.py matches your folder structure."
@@ -740,14 +738,11 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
740
738
  sys.path.insert(0, str(Path.cwd()))
741
739
 
742
740
  config = get_config()
743
- module_path = importlib.util.find_spec(config.module)
741
+ module_path = get_module_path(config.module)
744
742
  if module_path is None:
745
743
  console.error(f"Could not find module {config.module}.")
746
744
  raise click.exceptions.Exit(1)
747
745
 
748
- if not module_path.origin:
749
- console.error(f"Could not find origin for module {config.module}.")
750
- raise click.exceptions.Exit(1)
751
746
  console.info(f"Renaming app directory to {new_app_name}.")
752
747
  process_directory(
753
748
  Path.cwd(),
@@ -756,7 +751,7 @@ def rename_app(new_app_name: str, loglevel: constants.LogLevel):
756
751
  exclude_dirs=[constants.Dirs.WEB, constants.Dirs.APP_ASSETS],
757
752
  )
758
753
 
759
- rename_path_up_tree(Path(module_path.origin), config.app_name, new_app_name)
754
+ rename_path_up_tree(module_path, config.app_name, new_app_name)
760
755
 
761
756
  console.success(f"App directory renamed to [bold]{new_app_name}[/bold].")
762
757
 
@@ -13,7 +13,6 @@ import subprocess
13
13
  import sys
14
14
  import typing
15
15
  from collections.abc import Callable, Iterable, Sequence
16
- from fileinput import FileInput
17
16
  from hashlib import md5
18
17
  from inspect import getfullargspec
19
18
  from itertools import chain
@@ -67,7 +66,6 @@ OVERWRITE_TYPES = {
67
66
  }
68
67
 
69
68
  DEFAULT_TYPING_IMPORTS = {
70
- "overload",
71
69
  "Any",
72
70
  "Callable",
73
71
  "Dict",
@@ -677,10 +675,7 @@ def _generate_component_create_functiondef(
677
675
  value=ast.Constant(value=Ellipsis),
678
676
  ),
679
677
  ],
680
- decorator_list=[
681
- ast.Name(id="overload"),
682
- *decorator_list,
683
- ],
678
+ decorator_list=list(decorator_list),
684
679
  lineno=lineno,
685
680
  returns=ast.Constant(value=clz.__name__),
686
681
  )
@@ -896,7 +891,7 @@ class StubGenerator(ast.NodeTransformer):
896
891
  The modified ImportFrom node.
897
892
  """
898
893
  if node.module == "__future__":
899
- return None # ignore __future__ imports
894
+ return None # ignore __future__ imports: https://docs.astral.sh/ruff/rules/future-annotations-in-stub/
900
895
  return self.visit_Import(node)
901
896
 
902
897
  def visit_ClassDef(self, node: ast.ClassDef) -> ast.ClassDef:
@@ -1109,9 +1104,10 @@ class PyiGenerator:
1109
1104
 
1110
1105
  def _get_init_lazy_imports(self, mod: tuple | ModuleType, new_tree: ast.AST):
1111
1106
  # retrieve the _SUBMODULES and _SUBMOD_ATTRS from an init file if present.
1112
- sub_mods = getattr(mod, "_SUBMODULES", None)
1113
- sub_mod_attrs = getattr(mod, "_SUBMOD_ATTRS", None)
1114
- pyright_ignore_imports = getattr(mod, "_PYRIGHT_IGNORE_IMPORTS", [])
1107
+ sub_mods: set[str] | None = getattr(mod, "_SUBMODULES", None)
1108
+ sub_mod_attrs: dict[str, list[str | tuple[str, str]]] | None = getattr(
1109
+ mod, "_SUBMOD_ATTRS", None
1110
+ )
1115
1111
 
1116
1112
  if not sub_mods and not sub_mod_attrs:
1117
1113
  return None
@@ -1119,31 +1115,34 @@ class PyiGenerator:
1119
1115
  sub_mod_attrs_imports = []
1120
1116
 
1121
1117
  if sub_mods:
1122
- sub_mods_imports = [
1123
- f"from . import {mod} as {mod}" for mod in sorted(sub_mods)
1124
- ]
1118
+ sub_mods_imports = [f"from . import {mod}" for mod in sorted(sub_mods)]
1125
1119
  sub_mods_imports.append("")
1126
1120
 
1127
1121
  if sub_mod_attrs:
1128
- sub_mod_attrs = {
1129
- attr: mod for mod, attrs in sub_mod_attrs.items() for attr in attrs
1122
+ flattened_sub_mod_attrs = {
1123
+ imported: module
1124
+ for module, attrs in sub_mod_attrs.items()
1125
+ for imported in attrs
1130
1126
  }
1131
1127
  # construct the import statement and handle special cases for aliases
1132
1128
  sub_mod_attrs_imports = [
1133
- f"from .{path} import {mod if not isinstance(mod, tuple) else mod[0]} as {mod if not isinstance(mod, tuple) else mod[1]}"
1129
+ f"from .{module} import "
1134
1130
  + (
1135
- " # type: ignore"
1136
- if mod in pyright_ignore_imports
1137
- else " # noqa: F401" # ignore ruff formatting here for cases like rx.list.
1138
- if isinstance(mod, tuple)
1139
- else ""
1131
+ (
1132
+ (imported[0] + " as " + imported[1])
1133
+ if imported[0] != imported[1]
1134
+ else imported[0]
1135
+ )
1136
+ if isinstance(imported, tuple)
1137
+ else imported
1140
1138
  )
1141
- for mod, path in sub_mod_attrs.items()
1139
+ for imported, module in flattened_sub_mod_attrs.items()
1142
1140
  ]
1143
1141
  sub_mod_attrs_imports.append("")
1144
1142
 
1145
1143
  text = "\n" + "\n".join([*sub_mods_imports, *sub_mod_attrs_imports])
1146
- text += ast.unparse(new_tree) + "\n"
1144
+ text += ast.unparse(new_tree) + "\n\n"
1145
+ text += f"__all__ = {getattr(mod, '__all__', [])!r}\n"
1147
1146
  return text
1148
1147
 
1149
1148
  def _scan_file(self, module_path: Path) -> tuple[str, str] | None:
@@ -1258,10 +1257,7 @@ class PyiGenerator:
1258
1257
  if file_paths:
1259
1258
  subprocess.run(["ruff", "format", *file_paths])
1260
1259
  subprocess.run(["ruff", "check", "--fix", *file_paths])
1261
-
1262
- # For some reason, we need to format the __init__.pyi files again after fixing...
1263
- init_files = [f for f in file_paths if "/__init__.pyi" in f]
1264
- subprocess.run(["ruff", "format", *init_files])
1260
+ subprocess.run(["ruff", "format", *file_paths])
1265
1261
 
1266
1262
  if use_json:
1267
1263
  if file_paths and changed_files is None:
@@ -1327,19 +1323,6 @@ class PyiGenerator:
1327
1323
  json.dumps(pyi_hashes, indent=2, sort_keys=True) + "\n"
1328
1324
  )
1329
1325
 
1330
- # Post-process the generated pyi files to add hacky type: ignore comments
1331
- for file_path in file_paths:
1332
- with FileInput(file_path, inplace=True) as f:
1333
- for line in f:
1334
- # Hack due to ast not supporting comments in the tree.
1335
- if (
1336
- "def create(" in line
1337
- or "Var[Figure]" in line
1338
- or "Var[Template]" in line
1339
- ):
1340
- line = line.rstrip() + " # type: ignore\n"
1341
- print(line, end="") # noqa: T201
1342
-
1343
1326
 
1344
1327
  if __name__ == "__main__":
1345
1328
  import argparse
reflex/utils/types.py CHANGED
@@ -495,7 +495,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
495
495
  return list[
496
496
  get_attribute_access_type(
497
497
  attr.target_class,
498
- attr.remote_attr.key, # type: ignore[attr-defined]
498
+ attr.remote_attr.key, # pyright: ignore [reportAttributeAccessIssue]
499
499
  )
500
500
  ]
501
501
  elif isinstance(cls, type) and not is_generic_alias(cls) and issubclass(cls, Model):