reflex 0.8.1a2__py3-none-any.whl → 0.8.2__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.

@@ -29,7 +29,6 @@ def index() -> rx.Component:
29
29
  justify="center",
30
30
  min_height="85vh",
31
31
  ),
32
- rx.logo(),
33
32
  )
34
33
 
35
34
 
reflex/app.py CHANGED
@@ -22,6 +22,7 @@ from collections.abc import (
22
22
  Sequence,
23
23
  )
24
24
  from datetime import datetime
25
+ from itertools import chain
25
26
  from pathlib import Path
26
27
  from timeit import default_timer as timer
27
28
  from types import SimpleNamespace
@@ -238,10 +239,6 @@ def default_error_boundary(*children: Component, **props) -> Component:
238
239
  )
239
240
 
240
241
 
241
- class OverlayFragment(Fragment):
242
- """Alias for Fragment, used to wrap the overlay_component."""
243
-
244
-
245
242
  @dataclasses.dataclass(frozen=True)
246
243
  class UploadFile(StarletteUploadFile):
247
244
  """A file uploaded to the server.
@@ -369,6 +366,11 @@ class App(MiddlewareMixin, LifespanMixin):
369
366
  )
370
367
  )
371
368
 
369
+ # Extra app wraps to be applied to the whole app.
370
+ extra_app_wraps: dict[tuple[int, str], Callable[[bool], Component | None]] = (
371
+ dataclasses.field(default_factory=dict)
372
+ )
373
+
372
374
  # Components to add to the head of every page.
373
375
  head_components: list[Component] = dataclasses.field(default_factory=list)
374
376
 
@@ -515,8 +517,8 @@ class App(MiddlewareMixin, LifespanMixin):
515
517
  async_mode="asgi",
516
518
  cors_allowed_origins=(
517
519
  "*"
518
- if config.cors_allowed_origins == ["*"]
519
- else config.cors_allowed_origins
520
+ if config.cors_allowed_origins == ("*",)
521
+ else list(config.cors_allowed_origins)
520
522
  ),
521
523
  cors_credentials=True,
522
524
  max_http_buffer_size=environment.REFLEX_SOCKET_MAX_HTTP_BUFFER_SIZE.get(),
@@ -1031,25 +1033,31 @@ class App(MiddlewareMixin, LifespanMixin):
1031
1033
  # By default, compile the app.
1032
1034
  return True
1033
1035
 
1034
- def _add_overlay_to_component(self, component: Component) -> Component:
1035
- if self.overlay_component is None:
1036
- return component
1037
-
1036
+ def _add_overlay_to_component(
1037
+ self, component: Component, overlay_component: Component
1038
+ ) -> Component:
1038
1039
  children = component.children
1039
- overlay_component = self._generate_component(self.overlay_component)
1040
1040
 
1041
1041
  if children[0] == overlay_component:
1042
1042
  return component
1043
1043
 
1044
- # recreate OverlayFragment with overlay_component as first child
1045
- return OverlayFragment.create(overlay_component, *children)
1044
+ return Fragment.create(overlay_component, *children)
1046
1045
 
1047
1046
  def _setup_overlay_component(self):
1048
1047
  """If a State is not used and no overlay_component is specified, do not render the connection modal."""
1049
- if self._state is None and self.overlay_component is default_overlay_component:
1050
- self.overlay_component = None
1048
+ if self.overlay_component is None:
1049
+ return
1050
+ console.deprecate(
1051
+ feature_name="overlay_component",
1052
+ reason="Use `extra_app_wraps` to add the overlay component instead.",
1053
+ deprecation_version="0.8.2",
1054
+ removal_version="0.9.0",
1055
+ )
1056
+ overlay_component = self._generate_component(self.overlay_component)
1051
1057
  for k, component in self._pages.items():
1052
- self._pages[k] = self._add_overlay_to_component(component)
1058
+ self._pages[k] = self._add_overlay_to_component(
1059
+ component, overlay_component
1060
+ )
1053
1061
 
1054
1062
  def _setup_sticky_badge(self):
1055
1063
  """Add the sticky badge to the app."""
@@ -1261,7 +1269,10 @@ class App(MiddlewareMixin, LifespanMixin):
1261
1269
  app_wrappers[(1, "ToasterProvider")] = toast_provider
1262
1270
 
1263
1271
  # Add the app wraps to the app.
1264
- for key, app_wrap in self.app_wraps.items():
1272
+ for key, app_wrap in chain(
1273
+ self.app_wraps.items(), self.extra_app_wraps.items()
1274
+ ):
1275
+ # If the app wrap is a callable, generate the component
1265
1276
  component = app_wrap(self._state is not None)
1266
1277
  if component is not None:
1267
1278
  app_wrappers[key] = component
@@ -2076,9 +2087,20 @@ class EventNamespace(AsyncNamespace):
2076
2087
  # Get the client IP
2077
2088
  try:
2078
2089
  client_ip = environ["asgi.scope"]["client"][0]
2090
+ headers["asgi-scope-client"] = client_ip
2079
2091
  except (KeyError, IndexError):
2080
2092
  client_ip = environ.get("REMOTE_ADDR", "0.0.0.0")
2081
2093
 
2094
+ # Unroll reverse proxy forwarded headers.
2095
+ client_ip = (
2096
+ headers.get(
2097
+ "x-forwarded-for",
2098
+ client_ip,
2099
+ )
2100
+ .partition(",")[0]
2101
+ .strip()
2102
+ )
2103
+
2082
2104
  async with contextlib.aclosing(
2083
2105
  process(self.app, event, sid, headers, client_ip)
2084
2106
  ) as updates_gen:
@@ -828,10 +828,9 @@ def compile_unevaluated_page(
828
828
  enable_state = True
829
829
  break
830
830
 
831
- from reflex.app import OverlayFragment
832
831
  from reflex.utils.format import make_default_page_title
833
832
 
834
- component = OverlayFragment.create(component)
833
+ component = Fragment.create(component)
835
834
 
836
835
  meta_args = {
837
836
  "title": (
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from reflex.components.base.bare import Bare
6
6
  from reflex.components.el import elements
7
+ from reflex.components.el.elements.metadata import Meta as Meta # for compatibility
7
8
  from reflex.vars.base import Var
8
9
 
9
10
 
@@ -26,13 +27,6 @@ class Title(elements.Title):
26
27
  return super().render()
27
28
 
28
29
 
29
- class Meta(elements.Meta):
30
- """A component that displays metadata for the current page."""
31
-
32
- # The type of metadata value.
33
- property: Var[str]
34
-
35
-
36
30
  class Description(elements.Meta):
37
31
  """A component that displays the title of the current page."""
38
32
 
@@ -73,6 +73,9 @@ class Meta(BaseHTML): # Inherits common attributes from BaseHTML
73
73
  # Specifies a name for the metadata
74
74
  name: Var[str]
75
75
 
76
+ # The type of metadata value.
77
+ property: Var[str]
78
+
76
79
 
77
80
  class Title(Element):
78
81
  """Display the title element."""
reflex/config.py CHANGED
@@ -1,21 +1,18 @@
1
1
  """The Reflex config."""
2
2
 
3
- from __future__ import annotations
4
-
3
+ import dataclasses
5
4
  import importlib
6
5
  import os
7
6
  import sys
8
7
  import threading
9
8
  import urllib.parse
9
+ from collections.abc import Sequence
10
10
  from importlib.util import find_spec
11
11
  from pathlib import Path
12
12
  from types import ModuleType
13
- from typing import Any, ClassVar
14
-
15
- import pydantic.v1 as pydantic
13
+ from typing import TYPE_CHECKING, Any, ClassVar
16
14
 
17
15
  from reflex import constants
18
- from reflex.base import Base
19
16
  from reflex.constants.base import LogLevel
20
17
  from reflex.environment import EnvironmentVariables as EnvironmentVariables
21
18
  from reflex.environment import EnvVar as EnvVar
@@ -28,12 +25,13 @@ from reflex.environment import (
28
25
  from reflex.environment import env_var as env_var
29
26
  from reflex.environment import environment as environment
30
27
  from reflex.plugins import Plugin
28
+ from reflex.plugins.sitemap import SitemapPlugin
31
29
  from reflex.utils import console
32
30
  from reflex.utils.exceptions import ConfigError
33
- from reflex.utils.types import true_type_for_pydantic_field
34
31
 
35
32
 
36
- class DBConfig(Base):
33
+ @dataclasses.dataclass(kw_only=True)
34
+ class DBConfig:
37
35
  """Database config."""
38
36
 
39
37
  engine: str
@@ -51,7 +49,7 @@ class DBConfig(Base):
51
49
  password: str | None = None,
52
50
  host: str | None = None,
53
51
  port: int | None = 5432,
54
- ) -> DBConfig:
52
+ ) -> "DBConfig":
55
53
  """Create an instance with postgresql engine.
56
54
 
57
55
  Args:
@@ -81,7 +79,7 @@ class DBConfig(Base):
81
79
  password: str | None = None,
82
80
  host: str | None = None,
83
81
  port: int | None = 5432,
84
- ) -> DBConfig:
82
+ ) -> "DBConfig":
85
83
  """Create an instance with postgresql+psycopg engine.
86
84
 
87
85
  Args:
@@ -107,7 +105,7 @@ class DBConfig(Base):
107
105
  def sqlite(
108
106
  cls,
109
107
  database: str,
110
- ) -> DBConfig:
108
+ ) -> "DBConfig":
111
109
  """Create an instance with sqlite engine.
112
110
 
113
111
  Args:
@@ -145,32 +143,9 @@ class DBConfig(Base):
145
143
  _sensitive_env_vars = {"DB_URL", "ASYNC_DB_URL", "REDIS_URL"}
146
144
 
147
145
 
148
- class Config(Base):
149
- """The config defines runtime settings for the app.
150
-
151
- By default, the config is defined in an `rxconfig.py` file in the root of the app.
152
-
153
- ```python
154
- # rxconfig.py
155
- import reflex as rx
156
-
157
- config = rx.Config(
158
- app_name="myapp",
159
- api_url="http://localhost:8000",
160
- )
161
- ```
162
-
163
- Every config value can be overridden by an environment variable with the same name in uppercase.
164
- For example, `db_url` can be overridden by setting the `DB_URL` environment variable.
165
-
166
- See the [configuration](https://reflex.dev/docs/getting-started/configuration/) docs for more info.
167
- """
168
-
169
- class Config: # pyright: ignore [reportIncompatibleVariableOverride]
170
- """Pydantic config for the config."""
171
-
172
- validate_assignment = True
173
- use_enum_values = False
146
+ @dataclasses.dataclass(kw_only=True)
147
+ class BaseConfig:
148
+ """Base config for the Reflex app."""
174
149
 
175
150
  # The name of the app (should match the name of the app directory).
176
151
  app_name: str
@@ -218,13 +193,13 @@ class Config(Base):
218
193
  static_page_generation_timeout: int = 60
219
194
 
220
195
  # List of origins that are allowed to connect to the backend API.
221
- cors_allowed_origins: list[str] = ["*"]
196
+ cors_allowed_origins: Sequence[str] = dataclasses.field(default=("*",))
222
197
 
223
198
  # Whether to use React strict mode.
224
199
  react_strict_mode: bool = True
225
200
 
226
201
  # Additional frontend packages to install.
227
- frontend_packages: list[str] = []
202
+ frontend_packages: list[str] = dataclasses.field(default_factory=list)
228
203
 
229
204
  # Indicate which type of state manager to use
230
205
  state_manager_mode: constants.StateManagerMode = constants.StateManagerMode.DISK
@@ -239,7 +214,9 @@ class Config(Base):
239
214
  redis_token_expiration: int = constants.Expiration.TOKEN
240
215
 
241
216
  # Attributes that were explicitly set by the user.
242
- _non_default_attributes: set[str] = pydantic.PrivateAttr(set())
217
+ _non_default_attributes: set[str] = dataclasses.field(
218
+ default_factory=set, init=False
219
+ )
243
220
 
244
221
  # Path to file containing key-values pairs to override in the environment; Dotenv format.
245
222
  env_file: str | None = None
@@ -257,21 +234,58 @@ class Config(Base):
257
234
  extra_overlay_function: str | None = None
258
235
 
259
236
  # List of plugins to use in the app.
260
- plugins: list[Plugin] = []
237
+ plugins: list[Plugin] = dataclasses.field(default_factory=list)
238
+
239
+ # List of fully qualified import paths of plugins to disable in the app (e.g. reflex.plugins.sitemap.SitemapPlugin).
240
+ disable_plugins: list[str] = dataclasses.field(default_factory=list)
261
241
 
262
242
  _prefixes: ClassVar[list[str]] = ["REFLEX_"]
263
243
 
264
- def __init__(self, *args, **kwargs):
265
- """Initialize the config values.
244
+
245
+ _PLUGINS_ENABLED_BY_DEFAULT = [
246
+ SitemapPlugin,
247
+ ]
248
+
249
+
250
+ @dataclasses.dataclass(kw_only=True, init=False)
251
+ class Config(BaseConfig):
252
+ """The config defines runtime settings for the app.
253
+
254
+ By default, the config is defined in an `rxconfig.py` file in the root of the app.
255
+
256
+ ```python
257
+ # rxconfig.py
258
+ import reflex as rx
259
+
260
+ config = rx.Config(
261
+ app_name="myapp",
262
+ api_url="http://localhost:8000",
263
+ )
264
+ ```
265
+
266
+ Every config value can be overridden by an environment variable with the same name in uppercase and a REFLEX_ prefix.
267
+ For example, `db_url` can be overridden by setting the `REFLEX_DB_URL` environment variable.
268
+
269
+ See the [configuration](https://reflex.dev/docs/getting-started/configuration/) docs for more info.
270
+ """
271
+
272
+ def _post_init(self, **kwargs):
273
+ """Post-initialization method to set up the config.
274
+
275
+ This method is called after the config is initialized. It sets up the
276
+ environment variables, updates the config from the environment, and
277
+ replaces default URLs if ports were set.
266
278
 
267
279
  Args:
268
- *args: The args to pass to the Pydantic init method.
269
- **kwargs: The kwargs to pass to the Pydantic init method.
280
+ **kwargs: The kwargs passed to the Pydantic init method.
270
281
 
271
282
  Raises:
272
283
  ConfigError: If some values in the config are invalid.
273
284
  """
274
- super().__init__(*args, **kwargs)
285
+ class_fields = self.class_fields()
286
+ for key, value in kwargs.items():
287
+ if key not in class_fields:
288
+ setattr(self, key, value)
275
289
 
276
290
  # Clean up this code when we remove plain envvar in 0.8.0
277
291
  env_loglevel = os.environ.get("REFLEX_LOGLEVEL")
@@ -285,9 +299,12 @@ class Config(Base):
285
299
  for key, env_value in env_kwargs.items():
286
300
  setattr(self, key, env_value)
287
301
 
302
+ # Add builtin plugins if not disabled.
303
+ self._add_builtin_plugins()
304
+
288
305
  # Update default URLs if ports were set
289
306
  kwargs.update(env_kwargs)
290
- self._non_default_attributes.update(kwargs)
307
+ self._non_default_attributes = set(kwargs.keys())
291
308
  self._replace_defaults(**kwargs)
292
309
 
293
310
  if (
@@ -297,6 +314,74 @@ class Config(Base):
297
314
  msg = f"{self._prefixes[0]}REDIS_URL is required when using the redis state manager."
298
315
  raise ConfigError(msg)
299
316
 
317
+ def _add_builtin_plugins(self):
318
+ """Add the builtin plugins to the config."""
319
+ for plugin in _PLUGINS_ENABLED_BY_DEFAULT:
320
+ plugin_name = plugin.__module__ + "." + plugin.__qualname__
321
+ if plugin_name not in self.disable_plugins:
322
+ if not any(isinstance(p, plugin) for p in self.plugins):
323
+ console.warn(
324
+ f"`{plugin_name}` plugin is enabled by default, but not explicitly added to the config. "
325
+ "If you want to use it, please add it to the `plugins` list in your config inside of `rxconfig.py`. "
326
+ f"To disable this plugin, set `disable_plugins` to `{[plugin_name, *self.disable_plugins]!r}`.",
327
+ )
328
+ self.plugins.append(plugin())
329
+ else:
330
+ if any(isinstance(p, plugin) for p in self.plugins):
331
+ console.warn(
332
+ f"`{plugin_name}` is disabled in the config, but it is still present in the `plugins` list. "
333
+ "Please remove it from the `plugins` list in your config inside of `rxconfig.py`.",
334
+ )
335
+
336
+ for disabled_plugin in self.disable_plugins:
337
+ if not isinstance(disabled_plugin, str):
338
+ console.warn(
339
+ f"reflex.Config.disable_plugins should only contain strings, but got {disabled_plugin!r}. "
340
+ )
341
+ if not any(
342
+ plugin.__module__ + "." + plugin.__qualname__ == disabled_plugin
343
+ for plugin in _PLUGINS_ENABLED_BY_DEFAULT
344
+ ):
345
+ console.warn(
346
+ f"`{disabled_plugin}` is disabled in the config, but it is not a built-in plugin. "
347
+ "Please remove it from the `disable_plugins` list in your config inside of `rxconfig.py`.",
348
+ )
349
+
350
+ @classmethod
351
+ def class_fields(cls) -> set[str]:
352
+ """Get the fields of the config class.
353
+
354
+ Returns:
355
+ The fields of the config class.
356
+ """
357
+ return {field.name for field in dataclasses.fields(cls)}
358
+
359
+ if not TYPE_CHECKING:
360
+
361
+ def __init__(self, **kwargs):
362
+ """Initialize the config values.
363
+
364
+ Args:
365
+ **kwargs: The kwargs to pass to the Pydantic init method.
366
+
367
+ # noqa: DAR101 self
368
+ """
369
+ class_fields = self.class_fields()
370
+ super().__init__(**{k: v for k, v in kwargs.items() if k in class_fields})
371
+ self._post_init(**kwargs)
372
+
373
+ def json(self) -> str:
374
+ """Get the config as a JSON string.
375
+
376
+ Returns:
377
+ The config as a JSON string.
378
+ """
379
+ import json
380
+
381
+ from reflex.utils.serializers import serialize
382
+
383
+ return json.dumps(self, default=serialize)
384
+
300
385
  @property
301
386
  def app_module(self) -> ModuleType | None:
302
387
  """Return the app module if `app_module_import` is set.
@@ -333,11 +418,13 @@ class Config(Base):
333
418
 
334
419
  updated_values = {}
335
420
  # Iterate over the fields.
336
- for key, field in self.__fields__.items():
421
+ for field in dataclasses.fields(self):
337
422
  # The env var name is the key in uppercase.
338
423
  environment_variable = None
339
424
  for prefix in self._prefixes:
340
- if environment_variable := os.environ.get(f"{prefix}{key.upper()}"):
425
+ if environment_variable := os.environ.get(
426
+ f"{prefix}{field.name.upper()}"
427
+ ):
341
428
  break
342
429
 
343
430
  # If the env var is set, override the config value.
@@ -345,19 +432,19 @@ class Config(Base):
345
432
  # Interpret the value.
346
433
  value = interpret_env_var_value(
347
434
  environment_variable,
348
- true_type_for_pydantic_field(field),
435
+ field.type,
349
436
  field.name,
350
437
  )
351
438
 
352
439
  # Set the value.
353
- updated_values[key] = value
440
+ updated_values[field.name] = value
354
441
 
355
- if key.upper() in _sensitive_env_vars:
442
+ if field.name.upper() in _sensitive_env_vars:
356
443
  environment_variable = "***"
357
444
 
358
- if value != getattr(self, key):
445
+ if value != getattr(self, field.name):
359
446
  console.debug(
360
- f"Overriding config value {key} with env var {key.upper()}={environment_variable}",
447
+ f"Overriding config value {field.name} with env var {field.name.upper()}={environment_variable}",
361
448
  dedupe=True,
362
449
  )
363
450
  return updated_values
@@ -14,7 +14,7 @@ class Bun(SimpleNamespace):
14
14
  """Bun constants."""
15
15
 
16
16
  # The Bun version.
17
- VERSION = "1.2.17"
17
+ VERSION = "1.2.18"
18
18
 
19
19
  # Min Bun Version
20
20
  MIN_VERSION = "1.2.17"
@@ -104,7 +104,7 @@ class PackageJson(SimpleNamespace):
104
104
  class Commands(SimpleNamespace):
105
105
  """The commands to define in package.json."""
106
106
 
107
- DEV = "react-router dev"
107
+ DEV = "react-router dev --host"
108
108
  EXPORT = "react-router build"
109
109
  PROD = "serve ./build/client"
110
110
 
@@ -143,10 +143,11 @@ class PackageJson(SimpleNamespace):
143
143
  "postcss-import": "16.1.1",
144
144
  "@react-router/dev": _react_router_version,
145
145
  "@react-router/fs-routes": _react_router_version,
146
- "rolldown-vite": "7.0.5",
146
+ "rolldown-vite": "7.0.8",
147
147
  }
148
148
  OVERRIDES = {
149
149
  # This should always match the `react` version in DEPENDENCIES for recharts compatibility.
150
150
  "react-is": _react_version,
151
151
  "cookie": "1.0.2",
152
+ "rollup": "4.44.2",
152
153
  }
reflex/environment.py CHANGED
@@ -10,7 +10,7 @@ import inspect
10
10
  import multiprocessing
11
11
  import os
12
12
  import platform
13
- from collections.abc import Callable
13
+ from collections.abc import Callable, Sequence
14
14
  from functools import lru_cache
15
15
  from pathlib import Path
16
16
  from typing import (
@@ -227,7 +227,7 @@ def interpret_env_var_value(
227
227
  return interpret_existing_path_env(value, field_name)
228
228
  if field_type is Plugin:
229
229
  return interpret_plugin_env(value, field_name)
230
- if get_origin(field_type) is list:
230
+ if get_origin(field_type) in (list, Sequence):
231
231
  return [
232
232
  interpret_env_var_value(
233
233
  v,
reflex/istate/data.py CHANGED
@@ -202,8 +202,8 @@ class RouterData:
202
202
  The PageData object.
203
203
  """
204
204
  console.deprecate(
205
- "RouterData.page",
206
- "Use RouterData.url instead",
205
+ feature_name="RouterData.page",
206
+ reason="Use RouterData.url instead",
207
207
  deprecation_version="0.8.1",
208
208
  removal_version="0.9.0",
209
209
  )
@@ -1,8 +1,15 @@
1
1
  """Reflex Plugin System."""
2
2
 
3
- from .base import CommonContext as CommonContext
4
- from .base import Plugin as Plugin
5
- from .base import PreCompileContext as PreCompileContext
6
- from .sitemap import Plugin as SitemapPlugin
7
- from .tailwind_v3 import TailwindV3Plugin as TailwindV3Plugin
8
- from .tailwind_v4 import TailwindV4Plugin as TailwindV4Plugin
3
+ from .base import CommonContext, Plugin, PreCompileContext
4
+ from .sitemap import SitemapPlugin
5
+ from .tailwind_v3 import TailwindV3Plugin
6
+ from .tailwind_v4 import TailwindV4Plugin
7
+
8
+ __all__ = [
9
+ "CommonContext",
10
+ "Plugin",
11
+ "PreCompileContext",
12
+ "SitemapPlugin",
13
+ "TailwindV3Plugin",
14
+ "TailwindV4Plugin",
15
+ ]
reflex/plugins/sitemap.py CHANGED
@@ -193,7 +193,7 @@ def sitemap_task(unevaluated_pages: Sequence["UnevaluatedPage"]) -> tuple[str, s
193
193
  )
194
194
 
195
195
 
196
- class Plugin(PluginBase):
196
+ class SitemapPlugin(PluginBase):
197
197
  """Sitemap plugin for Reflex."""
198
198
 
199
199
  def pre_compile(self, **context):
@@ -204,3 +204,6 @@ class Plugin(PluginBase):
204
204
  """
205
205
  unevaluated_pages = context.get("unevaluated_pages", [])
206
206
  context["add_save_task"](sitemap_task, unevaluated_pages)
207
+
208
+
209
+ Plugin = SitemapPlugin
reflex/utils/console.py CHANGED
@@ -79,7 +79,7 @@ def is_debug() -> bool:
79
79
  return _LOG_LEVEL <= LogLevel.DEBUG
80
80
 
81
81
 
82
- def print(msg: str, dedupe: bool = False, **kwargs):
82
+ def print(msg: str, *, dedupe: bool = False, **kwargs):
83
83
  """Print a message.
84
84
 
85
85
  Args:
@@ -128,7 +128,7 @@ def should_use_log_file_console() -> bool:
128
128
  return environment.REFLEX_ENABLE_FULL_LOGGING.get()
129
129
 
130
130
 
131
- def print_to_log_file(msg: str, dedupe: bool = False, **kwargs):
131
+ def print_to_log_file(msg: str, *, dedupe: bool = False, **kwargs):
132
132
  """Print a message to the log file.
133
133
 
134
134
  Args:
@@ -139,7 +139,7 @@ def print_to_log_file(msg: str, dedupe: bool = False, **kwargs):
139
139
  log_file_console().print(msg, **kwargs)
140
140
 
141
141
 
142
- def debug(msg: str, dedupe: bool = False, **kwargs):
142
+ def debug(msg: str, *, dedupe: bool = False, **kwargs):
143
143
  """Print a debug message.
144
144
 
145
145
  Args:
@@ -161,7 +161,7 @@ def debug(msg: str, dedupe: bool = False, **kwargs):
161
161
  print_to_log_file(f"[purple]Debug: {msg}[/purple]", **kwargs)
162
162
 
163
163
 
164
- def info(msg: str, dedupe: bool = False, **kwargs):
164
+ def info(msg: str, *, dedupe: bool = False, **kwargs):
165
165
  """Print an info message.
166
166
 
167
167
  Args:
@@ -179,7 +179,7 @@ def info(msg: str, dedupe: bool = False, **kwargs):
179
179
  print_to_log_file(f"[cyan]Info: {msg}[/cyan]", **kwargs)
180
180
 
181
181
 
182
- def success(msg: str, dedupe: bool = False, **kwargs):
182
+ def success(msg: str, *, dedupe: bool = False, **kwargs):
183
183
  """Print a success message.
184
184
 
185
185
  Args:
@@ -197,7 +197,7 @@ def success(msg: str, dedupe: bool = False, **kwargs):
197
197
  print_to_log_file(f"[green]Success: {msg}[/green]", **kwargs)
198
198
 
199
199
 
200
- def log(msg: str, dedupe: bool = False, **kwargs):
200
+ def log(msg: str, *, dedupe: bool = False, **kwargs):
201
201
  """Takes a string and logs it to the console.
202
202
 
203
203
  Args:
@@ -225,7 +225,7 @@ def rule(title: str, **kwargs):
225
225
  _console.rule(title, **kwargs)
226
226
 
227
227
 
228
- def warn(msg: str, dedupe: bool = False, **kwargs):
228
+ def warn(msg: str, *, dedupe: bool = False, **kwargs):
229
229
  """Print a warning message.
230
230
 
231
231
  Args:
@@ -269,6 +269,7 @@ def _get_first_non_framework_frame() -> FrameType | None:
269
269
 
270
270
 
271
271
  def deprecate(
272
+ *,
272
273
  feature_name: str,
273
274
  reason: str,
274
275
  deprecation_version: str,
@@ -311,7 +312,7 @@ def deprecate(
311
312
  _EMITTED_DEPRECATION_WARNINGS.add(dedupe_key)
312
313
 
313
314
 
314
- def error(msg: str, dedupe: bool = False, **kwargs):
315
+ def error(msg: str, *, dedupe: bool = False, **kwargs):
315
316
  """Print an error message.
316
317
 
317
318
  Args:
reflex/utils/processes.py CHANGED
@@ -53,7 +53,7 @@ def get_num_workers() -> int:
53
53
  return (os.cpu_count() or 1) * 2 + 1
54
54
 
55
55
 
56
- def _is_address_responsive(
56
+ def _can_bind_at_port(
57
57
  address_family: socket.AddressFamily | int, address: str, port: int
58
58
  ) -> bool:
59
59
  """Check if a given address and port are responsive.
@@ -68,9 +68,14 @@ def _is_address_responsive(
68
68
  """
69
69
  try:
70
70
  with closing(socket.socket(address_family, socket.SOCK_STREAM)) as sock:
71
- return sock.connect_ex((address, port)) == 0
71
+ sock.bind((address, port))
72
+ except OverflowError:
73
+ return False
74
+ except PermissionError:
75
+ return False
72
76
  except OSError:
73
77
  return False
78
+ return True
74
79
 
75
80
 
76
81
  def is_process_on_port(port: int) -> bool:
@@ -82,9 +87,9 @@ def is_process_on_port(port: int) -> bool:
82
87
  Returns:
83
88
  Whether a process is running on the given port.
84
89
  """
85
- return _is_address_responsive( # Test IPv4 localhost (127.0.0.1)
90
+ return not _can_bind_at_port( # Test IPv4 localhost (127.0.0.1)
86
91
  socket.AF_INET, "127.0.0.1", port
87
- ) or _is_address_responsive(
92
+ ) or not _can_bind_at_port(
88
93
  socket.AF_INET6, "::1", port
89
94
  ) # Test IPv6 localhost (::1)
90
95
 
@@ -99,8 +104,15 @@ def change_port(port: int, _type: str) -> int:
99
104
  Returns:
100
105
  The new port.
101
106
 
107
+ Raises:
108
+ Exit: If the port is invalid or if the new port is occupied.
102
109
  """
103
110
  new_port = port + 1
111
+ if new_port < 0 or new_port > 65535:
112
+ console.error(
113
+ f"The {_type} port: {port} is invalid. It must be between 0 and 65535."
114
+ )
115
+ raise click.exceptions.Exit(1)
104
116
  if is_process_on_port(new_port):
105
117
  return change_port(new_port, _type)
106
118
  console.info(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.8.1a2
3
+ Version: 0.8.2
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
@@ -2,11 +2,11 @@ reflex/__init__.py,sha256=S_nJPFaTYwyxSzfOpACFxlSe2Vc8rQGr5ZDeyRSDiYQ,10264
2
2
  reflex/__init__.pyi,sha256=LHVB-SgdoFyB5aLiqiLEezMAd-vJje79ndxDxt9HWz8,10003
3
3
  reflex/__main__.py,sha256=6cVrGEyT3j3tEvlEVUatpaYfbB5EF3UVY-6vc_Z7-hw,108
4
4
  reflex/admin.py,sha256=Nbc38y-M8iaRBvh1W6DQu_D3kEhO8JFvxrog4q2cB_E,434
5
- reflex/app.py,sha256=2H_MpJvHkfMlfKp6j5ieydCrftM_k_uVwvLB6LmvNJw,75020
5
+ reflex/app.py,sha256=AibNjgSeaL5h2P3lGT7SJkqNhRFU2wmdkl2cRRHl15Y,75677
6
6
  reflex/assets.py,sha256=l5O_mlrTprC0lF7Rc_McOe3a0OtSLnRdNl_PqCpDCBA,3431
7
7
  reflex/base.py,sha256=Oh664QL3fZEHErhUasFqP7fE4olYf1y-9Oj6uZI2FCU,1173
8
- reflex/config.py,sha256=tEUaW4oJbkCDtQ1SgsT4APtLpQ9-VknVWdqwFoYorvg,15729
9
- reflex/environment.py,sha256=bYikxi9VANTrA1EDWtygVT2WyoYx_Q6xFX7P8CeoIbM,23205
8
+ reflex/config.py,sha256=HgJ57Op-glTro23GoQKmyXwUplvGYgZFKjvClYpD27s,19359
9
+ reflex/environment.py,sha256=PQF1QSLgu_tpUUP0vXWdvuC4t3wFts94nw2urt9Id8o,23227
10
10
  reflex/event.py,sha256=JdhRwJO1MYni4YfZ13NPSv0AWYHpLwk3L4WltH07L2c,70070
11
11
  reflex/model.py,sha256=xED7blemoiKxPFaOkCMrSayjjon7AJp8t5g459p7dGs,17646
12
12
  reflex/page.py,sha256=Bn8FTlUtjjKwUtpQA5r5eaWE_ZUb8i4XgrQi8FWbzNA,1880
@@ -18,7 +18,7 @@ reflex/style.py,sha256=JxbXXA4MTnXrk0XHEoMBoNC7J-M2oL5Hl3W_QmXvmBg,13222
18
18
  reflex/testing.py,sha256=6EXQN9K0tYfzEDe2aSRh4xLM_Jb_oIrI_qH2F_e0KXc,39423
19
19
  reflex/.templates/apps/blank/assets/favicon.ico,sha256=baxxgDAQ2V4-G5Q4S2yK5uUJTUGkv-AOWBQ0xd6myUo,4286
20
20
  reflex/.templates/apps/blank/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- reflex/.templates/apps/blank/code/blank.py,sha256=UmGz7aDxGULYINwLgotQoj-9qqpy7EOfAEpLmOT7C_k,917
21
+ reflex/.templates/apps/blank/code/blank.py,sha256=wry9E3VjC7qtt_gzqNOyo4KZAAlzVyNp3uhFkcLZmM0,898
22
22
  reflex/.templates/jinja/app/rxconfig.py.jinja2,sha256=ywlCgdKvAcUzmzyW-TGIiYCF5yqIH641PD0tWr6tOTc,170
23
23
  reflex/.templates/jinja/custom_components/README.md.jinja2,sha256=qA4XZDxOTc2gRIG7CO1VvVawOgThwZqU2RZvRTPhXwE,127
24
24
  reflex/.templates/jinja/custom_components/__init__.py.jinja2,sha256=z5n2tvoS7iNDaM6mUGKETdpGlC0oA1_rrYURu7O_xpk,32
@@ -63,7 +63,7 @@ reflex/app_mixins/lifespan.py,sha256=9rrdVUY0nHyk3gj31WJm7mw6I7KHBNtvSs9tZdwUm4I
63
63
  reflex/app_mixins/middleware.py,sha256=BKhe0jUFO1_TylEC48LUZyaeYyPmAYW-NV4H5Rw221k,2848
64
64
  reflex/app_mixins/mixin.py,sha256=R1YncalqDrbdPZvpKVbm72ZKmQZxYAWfuFq9JknzTqQ,305
65
65
  reflex/compiler/__init__.py,sha256=r8jqmDSFf09iV2lHlNhfc9XrTLjNxfDNwPYlxS4cmHE,27
66
- reflex/compiler/compiler.py,sha256=p-8xacPH7kCjE6tpW1AzbfNQJsF0033yXMpu03Exti4,29923
66
+ reflex/compiler/compiler.py,sha256=DjhNeh3u_OJ-fOYEmtNlAsNHKuzlYMkZU0wy5WdxHSU,29869
67
67
  reflex/compiler/templates.py,sha256=mQifVgvE7cKyzMFL9F5jxdJb9KFxucWJa7nuOp09ZUo,6002
68
68
  reflex/compiler/utils.py,sha256=v2LGz6WAFOSRpn3H0l215E94R7r7hB3s9vseWrQiXUQ,19045
69
69
  reflex/components/__init__.py,sha256=eWpgWFbSQDj2TpGp6StEbxU7roQgzY7ZM0XIcIc5RE8,588
@@ -88,7 +88,7 @@ reflex/components/base/fragment.py,sha256=ys7wkokq-N8WBxa9fqkEaNIrBlSximyD7vqlFV
88
88
  reflex/components/base/fragment.pyi,sha256=GrpnNRRNJLEVrOeBwZmVwT7OjsiDqxF2x1wvd9vwRPY,2531
89
89
  reflex/components/base/link.py,sha256=QZ4B5iWlw3REDBfOvLhnzu6BYwfbn2lpzwuPqaXU618,937
90
90
  reflex/components/base/link.pyi,sha256=W92leRCCsMkDOeE-YLC8uRPz8eDJPbZd1pbMbxVUyHg,18034
91
- reflex/components/base/meta.py,sha256=1WeeIn6Yo35-3LsraerhY5nMFy-OrURFCI0b3thU6lc,1280
91
+ reflex/components/base/meta.py,sha256=5pMGQCZwxDI5jYofPaAgmLPO2DBX0UbV35SxhgMKtZs,1211
92
92
  reflex/components/base/meta.pyi,sha256=ciEerqP0Y8cw9Aw2oFvy-PgUHZUqgieEqaJBrJDHelc,29035
93
93
  reflex/components/base/script.py,sha256=SqH4UIE9sZgFwvoc239qeLwzktRvNBPZbiGFRj4h9nM,2374
94
94
  reflex/components/base/script.pyi,sha256=tE8_KuihvLyhhsi_PPmImdMFSMvlPAGDq8npHZgLfUI,10653
@@ -148,7 +148,7 @@ reflex/components/el/elements/inline.py,sha256=q3Ku_x8L9NaXrYQovCfkWwZ5AfXG0VyhG
148
148
  reflex/components/el/elements/inline.pyi,sha256=mWgJ4a_2De__0ZpailJXDpDonEE0861zrIO9Bc039iU,235400
149
149
  reflex/components/el/elements/media.py,sha256=3f3zJRDDp0UCzaUIJHZV4RzLrboGhAgAMsOkxgjuVGk,13647
150
150
  reflex/components/el/elements/media.pyi,sha256=4JZS_NrxMskQZjyJLrKliI6XlbJJUpGnTafyabwms4Y,234523
151
- reflex/components/el/elements/metadata.py,sha256=Uh3DEultI8j-xfbe0CB_sCJPtkX5mvbpbzxeoS9Bj-M,2258
151
+ reflex/components/el/elements/metadata.py,sha256=Vf0D0dXqvwt76FkrvDQQpESJmxDh6e6Qxnk2GIRhOlM,2316
152
152
  reflex/components/el/elements/metadata.pyi,sha256=ml22lHUDH_K6C7kWXiNqh4eLRYg7sDM850WPlG-_KAc,40357
153
153
  reflex/components/el/elements/other.py,sha256=WON35QviPNYsBeLQTNbeN7a6m6ixLYIVa4WsDzo9YBY,1378
154
154
  reflex/components/el/elements/other.pyi,sha256=47cT9eZ4Vd0CLZoJpvYX5ERUQ9-MJmLWtrfjQ0n7WS8,59076
@@ -342,7 +342,7 @@ reflex/constants/compiler.py,sha256=VoA1vWZpl-2EdIOhAiOLwSX4S-bFLbkiESlNBmN08NQ,
342
342
  reflex/constants/config.py,sha256=8OIjiBdZZJrRVHsNBheMwopE9AwBFFzau0SXqXKcrPg,1715
343
343
  reflex/constants/custom_components.py,sha256=joJt4CEt1yKy7wsBH6vYo7_QRW0O_fWXrrTf0VY2q14,1317
344
344
  reflex/constants/event.py,sha256=tgoynWQi2L0_Kqc3XhXo7XXL76A-OKhJGHRrNjm7gFw,2885
345
- reflex/constants/installer.py,sha256=2y0n82IYalTr_XIWySQV0qrtlD3eX4Do9dWMYoh4wi4,4106
345
+ reflex/constants/installer.py,sha256=OjgMqQl8EFoKdsW-cqkbAaU6LSyLB0iyrKyuhaVvOHs,4141
346
346
  reflex/constants/route.py,sha256=UBjqaAOxiUxlDZCSY4O2JJChKvA4MZrhUU0E5rNvKbM,2682
347
347
  reflex/constants/state.py,sha256=uF_7-M9Gid-P3DjAOq4F1ERplyZhiNccowo_jLrdJrg,323
348
348
  reflex/constants/utils.py,sha256=e1ChEvbHfmE_V2UJvCSUhD_qTVAIhEGPpRJSqdSd6PA,780
@@ -352,7 +352,7 @@ reflex/experimental/__init__.py,sha256=P8fe8S2e2gy2HCwHFGQzr3lPMmh7qN5Ii2e8ukoPH
352
352
  reflex/experimental/client_state.py,sha256=1VOe6rYhpOBOZi7-tZwfOnSNPPdX3tsXzlfgNs7aDrg,10020
353
353
  reflex/experimental/hooks.py,sha256=CHYGrAE5t8riltrJmDFgJ4D2Vhmhw-y3B3MSGNlOQow,2366
354
354
  reflex/istate/__init__.py,sha256=afq_pCS5B_REC-Kl3Rbaa538uWi59xNz4INeuENcWnk,2039
355
- reflex/istate/data.py,sha256=oMaBzjpgxnHdU9Pp9tdstxQ7EdkJ8caS5nuu0zoDPLI,7267
355
+ reflex/istate/data.py,sha256=Q8HGjViQu3teVcwo1V1X6uWBxGr05ABTqAdeCoLUxic,7287
356
356
  reflex/istate/dynamic.py,sha256=xOQ9upZVPf6ngqcLQZ9HdAAYmoWwJ8kRFPH34Q5HTiM,91
357
357
  reflex/istate/manager.py,sha256=te5uQN6n-ctOGrf6NbZUVy6BKgWapLlMtCzDaO6RthU,30412
358
358
  reflex/istate/proxy.py,sha256=Q8JrV1m6drVcTNJL9JuN-nKUXclazs96OHl_fhR0UBk,25928
@@ -361,17 +361,17 @@ reflex/istate/wrappers.py,sha256=p8uuioXRbR5hperwbOJHUcWdu7hukLikQdoR7qrnKsI,909
361
361
  reflex/middleware/__init__.py,sha256=x7xTeDuc73Hjj43k1J63naC9x8vzFxl4sq7cCFBX7sk,111
362
362
  reflex/middleware/hydrate_middleware.py,sha256=1ch7bx2ZhojOR15b-LHD2JztrWCnpPJjTe8MWHJe-5Y,1510
363
363
  reflex/middleware/middleware.py,sha256=p5VVoIgQ_NwOg_GOY6g0S4fmrV76_VE1zt-HiwbMw-s,1158
364
- reflex/plugins/__init__.py,sha256=RQOhlDgFTIFbaBzyKQwaLlftO9bUfWSOEsZiagzr0bk,339
364
+ reflex/plugins/__init__.py,sha256=aARE63iAH2pNVdGmFU4qkXDiPoeBxINfkFzwk-NZe0w,351
365
365
  reflex/plugins/base.py,sha256=fVez3g3OVlu0NZ-ldMMAYFxpj1avyxBoJSNH1wUdJYE,3057
366
366
  reflex/plugins/shared_tailwind.py,sha256=UXUndEEcYBZ02klymw-vSZv01IZVLJG3oSaBHpQ617U,6426
367
- reflex/plugins/sitemap.py,sha256=dbbqIWaim4zl1LEU6Su-gyxd0BgM3E6S8gCExDfkTRo,6154
367
+ reflex/plugins/sitemap.py,sha256=Jj47uSMnkxndl7awkl48EhlQylBfY00WuMBNyTBcZHA,6186
368
368
  reflex/plugins/tailwind_v3.py,sha256=7bXI-zsGoS1pW27-_gskxGaUOQ7NQMPcYkoI5lnmIMA,4819
369
369
  reflex/plugins/tailwind_v4.py,sha256=gDzQd9M1F03n6sU0xSKhNZZ3xFO5SJMBmSXL-dPteOM,5239
370
370
  reflex/utils/__init__.py,sha256=y-AHKiRQAhk2oAkvn7W8cRVTZVK625ff8tTwvZtO7S4,24
371
371
  reflex/utils/build.py,sha256=lk8hE69onG95dv-LxRhjtEugct1g-KcWPUDorzqeGIE,7964
372
372
  reflex/utils/codespaces.py,sha256=kEQ-j-jclTukFpXDlYgNp95kYMGDrQmP3VNEoYGZ1u4,3052
373
373
  reflex/utils/compat.py,sha256=aSJH_M6iomgHPQ4onQ153xh1MWqPi3HSYDzE68N6gZM,2635
374
- reflex/utils/console.py,sha256=OFyXqnyhpAgXCDM7m5lokoUMjvXMohc2ftgrmcf62nc,11500
374
+ reflex/utils/console.py,sha256=ZgRJdsjfvZ-WXvFB1zfCE0NDS1TCMYZ_FWocMOr-RAU,11531
375
375
  reflex/utils/decorator.py,sha256=DVrlVGljV5OchMs-5_y1CbbqnCWlH6lv-dFko8yHxVY,1738
376
376
  reflex/utils/exceptions.py,sha256=Wwu7Ji2xgq521bJKtU2NgjwhmFfnG8erirEVN2h8S-g,8884
377
377
  reflex/utils/exec.py,sha256=LpCwG34VymgJMfwuWpkImU7Bqh0E4i9SDeGbSHZXkoM,21562
@@ -383,7 +383,7 @@ reflex/utils/misc.py,sha256=zbYIl7mI08is9enr851sj7PnDaNeVVvq5jDmQ4wdlCE,2879
383
383
  reflex/utils/net.py,sha256=HEHA8L5g7L9s0fFG4dTiZzB9PFO_0WRrlbMgpZr_GAQ,4093
384
384
  reflex/utils/path_ops.py,sha256=_RS17IQDNr5vcoLLGZx2-z1E5WP-JgDHvaRAOgqrZiU,8154
385
385
  reflex/utils/prerequisites.py,sha256=L2tCFqqiYqygRbQ0JMMBduMdsMkKJLDvzGKZnvI1Enc,66001
386
- reflex/utils/processes.py,sha256=lvr44qQpSsJvZjqoE1h1HBLjseEifM1goXrU-UUpAKA,16571
386
+ reflex/utils/processes.py,sha256=lhUWsk1vJWoc0d62W6FN4ngArlqJNxlVvWnEJKpiPEo,16936
387
387
  reflex/utils/pyi_generator.py,sha256=HdmUVs50Bk7MAMFSvpATRhH--_50w-9URMFnjLlwT40,46086
388
388
  reflex/utils/redir.py,sha256=3JG0cRdfIZnFIBHHN32ynD5cfbUZa7gLRxzrxRGGl5I,1751
389
389
  reflex/utils/registry.py,sha256=DEF7csYQ5VnrZhy6ULVfMlceh7XVH0pks96lIyyThuc,1882
@@ -399,8 +399,8 @@ reflex/vars/number.py,sha256=tO7pnvFaBsedq1HWT4skytnSqHWMluGEhUbjAUMx8XQ,28190
399
399
  reflex/vars/object.py,sha256=BDmeiwG8v97s_BnR1Egq3NxOKVjv9TfnREB3cz0zZtk,17322
400
400
  reflex/vars/sequence.py,sha256=1kBrqihspyjyQ1XDqFPC8OpVGtZs_EVkOdIKBro5ilA,55249
401
401
  scripts/hatch_build.py,sha256=-4pxcLSFmirmujGpQX9UUxjhIC03tQ_fIQwVbHu9kc0,1861
402
- reflex-0.8.1a2.dist-info/METADATA,sha256=ai5RBpLST_c6C4EyqE9VBloSls3Bhd0EK_GfFeus7dU,12371
403
- reflex-0.8.1a2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
404
- reflex-0.8.1a2.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
405
- reflex-0.8.1a2.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
406
- reflex-0.8.1a2.dist-info/RECORD,,
402
+ reflex-0.8.2.dist-info/METADATA,sha256=AGSasSirTNHbH9T6PMhFJvRITRJ-ZaYBDqlBwDjwZA4,12369
403
+ reflex-0.8.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
404
+ reflex-0.8.2.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
405
+ reflex-0.8.2.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
406
+ reflex-0.8.2.dist-info/RECORD,,