reflex 0.8.6a0__py3-none-any.whl → 0.8.6a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

reflex/app.py CHANGED
@@ -604,6 +604,11 @@ class App(MiddlewareMixin, LifespanMixin):
604
604
 
605
605
  self._compile(prerender_routes=is_prod_mode())
606
606
 
607
+ config = get_config()
608
+
609
+ for plugin in config.plugins:
610
+ plugin.post_compile(app=self)
611
+
607
612
  # We will not be making more vars, so we can clear the global cache to free up memory.
608
613
  GLOBAL_CACHE.clear()
609
614
 
@@ -1926,6 +1931,13 @@ def upload(app: App):
1926
1931
  )
1927
1932
  )
1928
1933
 
1934
+ for file in files:
1935
+ if not isinstance(file, StarletteUploadFile):
1936
+ raise UploadValueError(
1937
+ "Uploaded file is not an UploadFile." + str(file)
1938
+ )
1939
+ await file.close()
1940
+
1929
1941
  event = Event(
1930
1942
  token=token,
1931
1943
  name=handler,
@@ -136,6 +136,8 @@ class ErrorBoundary(Component):
136
136
  height="100%",
137
137
  width="100%",
138
138
  position="absolute",
139
+ background_color="#fff",
140
+ color="#000",
139
141
  display="flex",
140
142
  align_items="center",
141
143
  justify_content="center",
reflex/istate/manager.py CHANGED
@@ -143,6 +143,8 @@ class StateManagerMemory(StateManager):
143
143
  token: The token to set the state for.
144
144
  state: The state to set.
145
145
  """
146
+ token = _split_substate_key(token)[0]
147
+ self.states[token] = state
146
148
 
147
149
  @override
148
150
  @contextlib.asynccontextmanager
@@ -165,7 +167,6 @@ class StateManagerMemory(StateManager):
165
167
  async with self._states_locks[token]:
166
168
  state = await self.get_state(token)
167
169
  yield state
168
- await self.set_state(token, state)
169
170
 
170
171
 
171
172
  def _default_token_expiration() -> int:
@@ -1,5 +1,6 @@
1
1
  """Reflex Plugin System."""
2
2
 
3
+ from ._screenshot import ScreenshotPlugin as _ScreenshotPlugin
3
4
  from .base import CommonContext, Plugin, PreCompileContext
4
5
  from .sitemap import SitemapPlugin
5
6
  from .tailwind_v3 import TailwindV3Plugin
@@ -12,4 +13,5 @@ __all__ = [
12
13
  "SitemapPlugin",
13
14
  "TailwindV3Plugin",
14
15
  "TailwindV4Plugin",
16
+ "_ScreenshotPlugin",
15
17
  ]
@@ -0,0 +1,144 @@
1
+ """Plugin to enable screenshot functionality."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from reflex.plugins.base import Plugin as BasePlugin
6
+
7
+ if TYPE_CHECKING:
8
+ from starlette.requests import Request
9
+ from starlette.responses import Response
10
+ from typing_extensions import Unpack
11
+
12
+ from reflex.app import App
13
+ from reflex.plugins.base import PostCompileContext
14
+ from reflex.state import BaseState
15
+
16
+ ACTIVE_CONNECTIONS = "/_active_connections"
17
+ CLONE_STATE = "/_clone_state"
18
+
19
+
20
+ def _deep_copy(state: "BaseState") -> "BaseState":
21
+ """Create a deep copy of the state.
22
+
23
+ Args:
24
+ state: The state to copy.
25
+
26
+ Returns:
27
+ A deep copy of the state.
28
+ """
29
+ import copy
30
+
31
+ copy_of_state = copy.deepcopy(state)
32
+
33
+ def copy_substate(substate: "BaseState") -> "BaseState":
34
+ substate_copy = _deep_copy(substate)
35
+
36
+ substate_copy.parent_state = copy_of_state
37
+
38
+ return substate_copy
39
+
40
+ copy_of_state.substates = {
41
+ substate_name: copy_substate(substate)
42
+ for substate_name, substate in state.substates.items()
43
+ }
44
+
45
+ return copy_of_state
46
+
47
+
48
+ class ScreenshotPlugin(BasePlugin):
49
+ """Plugin to handle screenshot functionality."""
50
+
51
+ def post_compile(self, **context: "Unpack[PostCompileContext]") -> None:
52
+ """Called after the compilation of the plugin.
53
+
54
+ Args:
55
+ context: The context for the plugin.
56
+ """
57
+ app = context["app"]
58
+ self._add_active_connections_endpoint(app)
59
+ self._add_clone_state_endpoint(app)
60
+
61
+ @staticmethod
62
+ def _add_active_connections_endpoint(app: "App") -> None:
63
+ """Add an endpoint to the app that returns the active connections.
64
+
65
+ Args:
66
+ app: The application instance to which the endpoint will be added.
67
+ """
68
+ if not app._api:
69
+ return
70
+
71
+ async def active_connections(_request: "Request") -> "Response":
72
+ from starlette.responses import JSONResponse
73
+
74
+ if not app.event_namespace:
75
+ return JSONResponse({})
76
+
77
+ return JSONResponse(app.event_namespace.token_to_sid)
78
+
79
+ app._api.add_route(
80
+ ACTIVE_CONNECTIONS,
81
+ active_connections,
82
+ methods=["GET"],
83
+ )
84
+
85
+ @staticmethod
86
+ def _add_clone_state_endpoint(app: "App") -> None:
87
+ """Add an endpoint to the app that clones the current state.
88
+
89
+ Args:
90
+ app: The application instance to which the endpoint will be added.
91
+ """
92
+ if not app._api:
93
+ return
94
+
95
+ async def clone_state(request: "Request") -> "Response":
96
+ import uuid
97
+
98
+ from starlette.responses import JSONResponse
99
+
100
+ from reflex.state import _substate_key
101
+
102
+ if not app.event_namespace:
103
+ return JSONResponse({})
104
+
105
+ token_to_clone = await request.json()
106
+
107
+ if not isinstance(token_to_clone, str):
108
+ return JSONResponse(
109
+ {"error": "Token to clone must be a string."}, status_code=400
110
+ )
111
+
112
+ old_state = await app.state_manager.get_state(token_to_clone)
113
+
114
+ new_state = _deep_copy(old_state)
115
+
116
+ new_token = uuid.uuid4().hex
117
+
118
+ all_states = [new_state]
119
+
120
+ found_new = True
121
+
122
+ while found_new:
123
+ found_new = False
124
+
125
+ for state in all_states:
126
+ for substate in state.substates.values():
127
+ substate._was_touched = True
128
+
129
+ if substate not in all_states:
130
+ all_states.append(substate)
131
+
132
+ found_new = True
133
+
134
+ await app.state_manager.set_state(
135
+ _substate_key(new_token, new_state), new_state
136
+ )
137
+
138
+ return JSONResponse(new_token)
139
+
140
+ app._api.add_route(
141
+ CLONE_STATE,
142
+ clone_state,
143
+ methods=["POST"],
144
+ )
reflex/plugins/base.py CHANGED
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, ParamSpec, Protocol, TypedDict
7
7
  from typing_extensions import Unpack
8
8
 
9
9
  if TYPE_CHECKING:
10
- from reflex.app import UnevaluatedPage
10
+ from reflex.app import App, UnevaluatedPage
11
11
 
12
12
 
13
13
  class CommonContext(TypedDict):
@@ -44,6 +44,12 @@ class PreCompileContext(CommonContext):
44
44
  unevaluated_pages: Sequence["UnevaluatedPage"]
45
45
 
46
46
 
47
+ class PostCompileContext(CommonContext):
48
+ """Context for post-compile hooks."""
49
+
50
+ app: "App"
51
+
52
+
47
53
  class Plugin:
48
54
  """Base class for all plugins."""
49
55
 
@@ -104,6 +110,13 @@ class Plugin:
104
110
  context: The context for the plugin.
105
111
  """
106
112
 
113
+ def post_compile(self, **context: Unpack[PostCompileContext]) -> None:
114
+ """Called after the compilation of the plugin.
115
+
116
+ Args:
117
+ context: The context for the plugin.
118
+ """
119
+
107
120
  def __repr__(self):
108
121
  """Return a string representation of the plugin.
109
122
 
reflex/state.py CHANGED
@@ -509,7 +509,7 @@ class BaseState(EvenMoreBasicBaseState):
509
509
 
510
510
  new_backend_vars = {
511
511
  name: value
512
- for name, value in cls.__dict__.items()
512
+ for name, value in list(cls.__dict__.items())
513
513
  if types.is_backend_base_variable(name, cls)
514
514
  }
515
515
  # Add annotated backend vars that may not have a default value.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.8.6a0
3
+ Version: 0.8.6a1
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,7 +2,7 @@ reflex/__init__.py,sha256=_1PVYjDeA6_JyfXvL6OuKjjO6AX2oMiNcAq8AEHf6xw,10161
2
2
  reflex/__init__.pyi,sha256=0D46kHVUJPE_kgYL-BjraERu-MXNCPsQTZQShrijmeQ,10148
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=2Jay0aocK1_rzR9Gbao2dxugXGdEuo_IoCQgCKQfdgY,77374
5
+ reflex/app.py,sha256=g5bpc29jekySAvA0a3advQ1dUM-y4cKAM8vMB7oNAsM,77731
6
6
  reflex/assets.py,sha256=l5O_mlrTprC0lF7Rc_McOe3a0OtSLnRdNl_PqCpDCBA,3431
7
7
  reflex/base.py,sha256=Oh664QL3fZEHErhUasFqP7fE4olYf1y-9Oj6uZI2FCU,1173
8
8
  reflex/config.py,sha256=iL5hbdel04GzTtNiPwoNQ6xACJO1SkuvwzAyodDv028,19875
@@ -13,7 +13,7 @@ reflex/page.py,sha256=ssCbMVFuIy60vH-YhJUzN0OxzUwXFCCD3ej56dVjp3g,3525
13
13
  reflex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  reflex/reflex.py,sha256=BkHnxKp3uZEllLWBbH8Y_EfSu80L4_0FEbSbZkg7JyQ,22506
15
15
  reflex/route.py,sha256=ZRlXl1HeKWM3W-yFCnRFIA_6I3W9JBqe2CKzCpWX7Vg,7699
16
- reflex/state.py,sha256=BunDGopplAH9qUagCv8j80m-IHnVX2LfF-bYo2aDspg,93055
16
+ reflex/state.py,sha256=V84fFvC8yzs0hoqgAgF1BMK81yWm6UZMGUxSJBQPf6o,93061
17
17
  reflex/style.py,sha256=JxbXXA4MTnXrk0XHEoMBoNC7J-M2oL5Hl3W_QmXvmBg,13222
18
18
  reflex/testing.py,sha256=MgYxW3MLjd8TX5Lv7EWW-go3Fu_gL07i9HpxPJpBUDw,39951
19
19
  reflex/.templates/apps/blank/assets/favicon.ico,sha256=baxxgDAQ2V4-G5Q4S2yK5uUJTUGkv-AOWBQ0xd6myUo,4286
@@ -82,7 +82,7 @@ reflex/components/base/body.py,sha256=KLPOhxVsKyjPwrY9AziCOOG_c9ckOqIhI4n2i3_Q3N
82
82
  reflex/components/base/body.pyi,sha256=VG8N-0ocwgFOJR9mXfu6a5BtIY_jR7mK9lX3Y22g9GQ,8655
83
83
  reflex/components/base/document.py,sha256=Fr7y22NbeKeiz8kWPH2q5BpFjKdq-AmY-sxZilee_H8,636
84
84
  reflex/components/base/document.pyi,sha256=PlvclB2vyGcQ8zLJpFac_37LHeXLwpe8Qh0UqzXAPfE,11788
85
- reflex/components/base/error_boundary.py,sha256=hGp0yjFjBLutbplZkI3fkHBjIWBlYOoZ_1ISth7jEV0,6351
85
+ reflex/components/base/error_boundary.py,sha256=sp6W7G6uFWdls6nzl17XSizSSm6zS2OAu3ZqTE2W1CE,6438
86
86
  reflex/components/base/error_boundary.pyi,sha256=SvKnZ09jwIL_4-J5BKVZuATclrB4o3Eh457iTmmoNb4,2926
87
87
  reflex/components/base/fragment.py,sha256=ys7wkokq-N8WBxa9fqkEaNIrBlSximyD7vqlFVe02hQ,342
88
88
  reflex/components/base/fragment.pyi,sha256=1vl8p6SCWd0_QzYVAb-Em70IX6FwIWFvJLxMl5OJTwU,2401
@@ -352,15 +352,16 @@ reflex/experimental/hooks.py,sha256=CHYGrAE5t8riltrJmDFgJ4D2Vhmhw-y3B3MSGNlOQow,
352
352
  reflex/istate/__init__.py,sha256=afq_pCS5B_REC-Kl3Rbaa538uWi59xNz4INeuENcWnk,2039
353
353
  reflex/istate/data.py,sha256=KrANpAQFTZcfFWnxyykgK1FRNuXTwTin1WYpyqymV-U,7547
354
354
  reflex/istate/dynamic.py,sha256=xOQ9upZVPf6ngqcLQZ9HdAAYmoWwJ8kRFPH34Q5HTiM,91
355
- reflex/istate/manager.py,sha256=te5uQN6n-ctOGrf6NbZUVy6BKgWapLlMtCzDaO6RthU,30412
355
+ reflex/istate/manager.py,sha256=8EezUlCGkkg_UIs1xa460kZFdJ9qHo6naazphT4jqns,30446
356
356
  reflex/istate/proxy.py,sha256=Q8JrV1m6drVcTNJL9JuN-nKUXclazs96OHl_fhR0UBk,25928
357
357
  reflex/istate/storage.py,sha256=gCPoiZxuG-Rw0y-Pz3OC7rv4o08dQ_jK1fE2u8Jhxqg,4339
358
358
  reflex/istate/wrappers.py,sha256=p8uuioXRbR5hperwbOJHUcWdu7hukLikQdoR7qrnKsI,909
359
359
  reflex/middleware/__init__.py,sha256=x7xTeDuc73Hjj43k1J63naC9x8vzFxl4sq7cCFBX7sk,111
360
360
  reflex/middleware/hydrate_middleware.py,sha256=1ch7bx2ZhojOR15b-LHD2JztrWCnpPJjTe8MWHJe-5Y,1510
361
361
  reflex/middleware/middleware.py,sha256=p5VVoIgQ_NwOg_GOY6g0S4fmrV76_VE1zt-HiwbMw-s,1158
362
- reflex/plugins/__init__.py,sha256=aARE63iAH2pNVdGmFU4qkXDiPoeBxINfkFzwk-NZe0w,351
363
- reflex/plugins/base.py,sha256=Jgj7Xrpk0ztM57kl0hN3PUHYrGSXu7oj7OPyIWQJUcE,3059
362
+ reflex/plugins/__init__.py,sha256=jrMWQqMxCwDwgwQYTygeR_pIewMcvIFwAnngPbjSumQ,439
363
+ reflex/plugins/_screenshot.py,sha256=CAOaRpbrpTTIswwCXqhv7WYShB86Ao9MVv6dcXJzRb4,3958
364
+ reflex/plugins/base.py,sha256=5BgzCM7boj9kJ6FGzVzVlgQk-crJuVmOLCl1PXvv4-E,3372
364
365
  reflex/plugins/shared_tailwind.py,sha256=UXUndEEcYBZ02klymw-vSZv01IZVLJG3oSaBHpQ617U,6426
365
366
  reflex/plugins/sitemap.py,sha256=Jj47uSMnkxndl7awkl48EhlQylBfY00WuMBNyTBcZHA,6186
366
367
  reflex/plugins/tailwind_v3.py,sha256=7bXI-zsGoS1pW27-_gskxGaUOQ7NQMPcYkoI5lnmIMA,4819
@@ -399,8 +400,8 @@ reflex/vars/number.py,sha256=tO7pnvFaBsedq1HWT4skytnSqHWMluGEhUbjAUMx8XQ,28190
399
400
  reflex/vars/object.py,sha256=BDmeiwG8v97s_BnR1Egq3NxOKVjv9TfnREB3cz0zZtk,17322
400
401
  reflex/vars/sequence.py,sha256=1kBrqihspyjyQ1XDqFPC8OpVGtZs_EVkOdIKBro5ilA,55249
401
402
  scripts/hatch_build.py,sha256=-4pxcLSFmirmujGpQX9UUxjhIC03tQ_fIQwVbHu9kc0,1861
402
- reflex-0.8.6a0.dist-info/METADATA,sha256=2RaX5LhTj99Yn0i6IbUON1L_nKWz5EHINSBMcYHeGpY,12541
403
- reflex-0.8.6a0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
404
- reflex-0.8.6a0.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
405
- reflex-0.8.6a0.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
406
- reflex-0.8.6a0.dist-info/RECORD,,
403
+ reflex-0.8.6a1.dist-info/METADATA,sha256=teDs-RrGAMBksliapUSw7JC4U6yaHZoVfD25pKGzFzY,12541
404
+ reflex-0.8.6a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
405
+ reflex-0.8.6a1.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
406
+ reflex-0.8.6a1.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
407
+ reflex-0.8.6a1.dist-info/RECORD,,