reflex 0.8.15a1__py3-none-any.whl → 0.8.15a2__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.

@@ -529,6 +529,14 @@ export const connect = async (
529
529
  navigate,
530
530
  params,
531
531
  ) => {
532
+ // Socket already allocated, just reconnect it if needed.
533
+ if (socket.current) {
534
+ if (!socket.current.connected) {
535
+ socket.current.reconnect();
536
+ }
537
+ return;
538
+ }
539
+
532
540
  // Get backend URL object from the endpoint.
533
541
  const endpoint = getBackendURL(EVENTURL);
534
542
  const on_hydrated_queue = [];
@@ -540,7 +548,9 @@ export const connect = async (
540
548
  protocols: [reflexEnvironment.version],
541
549
  autoUnref: false,
542
550
  query: { token: getToken() },
551
+ reconnection: false, // Reconnection will be handled manually.
543
552
  });
553
+ socket.current.wait_connect = !socket.current.connected;
544
554
  // Ensure undefined fields in events are sent as null instead of removed
545
555
  socket.current.io.encoder.replacer = (k, v) => (v === undefined ? null : v);
546
556
  socket.current.io.decoder.tryParse = (str) => {
@@ -550,6 +560,18 @@ export const connect = async (
550
560
  return false;
551
561
  }
552
562
  };
563
+ // Set up a reconnect helper function
564
+ socket.current.reconnect = () => {
565
+ if (
566
+ socket.current &&
567
+ !socket.current.connected &&
568
+ !socket.current.wait_connect
569
+ ) {
570
+ socket.current.wait_connect = true;
571
+ socket.current.io.opts.query = { token: getToken() }; // Update token for reconnect.
572
+ socket.current.connect();
573
+ }
574
+ };
553
575
 
554
576
  function checkVisibility() {
555
577
  if (document.visibilityState === "visible") {
@@ -565,7 +587,7 @@ export const connect = async (
565
587
  );
566
588
  } else if (!socket.current.connected) {
567
589
  console.log("Socket is disconnected, attempting to reconnect ");
568
- socket.current.connect();
590
+ socket.current.reconnect();
569
591
  } else {
570
592
  console.log("Socket is reconnected ");
571
593
  }
@@ -588,6 +610,7 @@ export const connect = async (
588
610
 
589
611
  // Once the socket is open, hydrate the page.
590
612
  socket.current.on("connect", async () => {
613
+ socket.current.wait_connect = false;
591
614
  setConnectErrors([]);
592
615
  window.addEventListener("pagehide", pagehideHandler);
593
616
  window.addEventListener("beforeunload", disconnectTrigger);
@@ -599,16 +622,33 @@ export const connect = async (
599
622
  });
600
623
 
601
624
  socket.current.on("connect_error", (error) => {
602
- setConnectErrors((connectErrors) => [connectErrors.slice(-9), error]);
625
+ socket.current.wait_connect = false;
626
+ let n_connect_errors = 0;
627
+ setConnectErrors((connectErrors) => {
628
+ const new_errors = [...connectErrors.slice(-9), error];
629
+ n_connect_errors = new_errors.length;
630
+ return new_errors;
631
+ });
632
+ window.setTimeout(() => {
633
+ if (socket.current && !socket.current.connected) {
634
+ socket.current.reconnect();
635
+ }
636
+ }, 200 * n_connect_errors); // Incremental backoff
603
637
  });
604
638
 
605
639
  // When the socket disconnects reset the event_processing flag
606
- socket.current.on("disconnect", () => {
607
- socket.current = null; // allow reconnect to occur automatically
640
+ socket.current.on("disconnect", (reason, details) => {
641
+ socket.current.wait_connect = false;
642
+ const try_reconnect =
643
+ reason !== "io server disconnect" && reason !== "io client disconnect";
608
644
  event_processing = false;
609
645
  window.removeEventListener("unload", disconnectTrigger);
610
646
  window.removeEventListener("beforeunload", disconnectTrigger);
611
647
  window.removeEventListener("pagehide", pagehideHandler);
648
+ if (try_reconnect) {
649
+ // Attempt to reconnect transient non-intentional disconnects.
650
+ socket.current.reconnect();
651
+ }
612
652
  });
613
653
 
614
654
  // On each received message, queue the updates and events.
@@ -785,6 +825,7 @@ export const useEventLoop = (
785
825
  const [searchParams] = useSearchParams();
786
826
  const [connectErrors, setConnectErrors] = useState([]);
787
827
  const params = useRef(paramsR);
828
+ const mounted = useRef(false);
788
829
 
789
830
  useEffect(() => {
790
831
  const { "*": splat, ...remainingParams } = paramsR;
@@ -796,11 +837,16 @@ export const useEventLoop = (
796
837
  }, [paramsR]);
797
838
 
798
839
  const ensureSocketConnected = useCallback(async () => {
840
+ if (!mounted.current) {
841
+ // During hot reload, some components may still have a reference to
842
+ // addEvents, so avoid reconnecting the socket of an unmounted event loop.
843
+ return;
844
+ }
799
845
  // only use websockets if state is present and backend is not disabled (reflex cloud).
800
846
  if (
801
847
  Object.keys(initialState).length > 1 &&
802
848
  !isBackendDisabled() &&
803
- !socket.current
849
+ !socket.current?.connected
804
850
  ) {
805
851
  // Initialize the websocket connection.
806
852
  await connect(
@@ -813,13 +859,23 @@ export const useEventLoop = (
813
859
  () => params.current,
814
860
  );
815
861
  }
816
- }, [socket, dispatch, setConnectErrors, client_storage, navigate, params]);
862
+ }, [
863
+ socket,
864
+ dispatch,
865
+ setConnectErrors,
866
+ client_storage,
867
+ navigate,
868
+ params,
869
+ mounted,
870
+ ]);
817
871
 
818
872
  // Function to add new events to the event queue.
819
873
  const addEvents = useCallback((events, args, event_actions) => {
820
874
  const _events = events.filter((e) => e !== undefined && e !== null);
821
-
822
- ensureSocketConnected();
875
+ if (!event_actions?.temporal) {
876
+ // Reconnect socket if needed for non-temporal events.
877
+ ensureSocketConnected();
878
+ }
823
879
 
824
880
  if (!(args instanceof Array)) {
825
881
  args = [args];
@@ -914,12 +970,16 @@ export const useEventLoop = (
914
970
  // Handle socket connect/disconnect.
915
971
  useEffect(() => {
916
972
  // Initialize the websocket connection.
973
+ mounted.current = true;
917
974
  ensureSocketConnected();
918
975
 
919
976
  // Cleanup function.
920
977
  return () => {
978
+ mounted.current = false;
921
979
  if (socket.current) {
922
980
  socket.current.disconnect();
981
+ socket.current.off();
982
+ socket.current = null;
923
983
  }
924
984
  };
925
985
  }, []);
reflex/app.py CHANGED
@@ -1781,16 +1781,16 @@ async def process(
1781
1781
  name=f"reflex_emit_reload|{event.name}|{time.time()}|{event.token}",
1782
1782
  )
1783
1783
  return
1784
+ router_data[constants.RouteVar.PATH] = "/" + (
1785
+ app.router(path) or "404"
1786
+ if (path := router_data.get(constants.RouteVar.PATH))
1787
+ else "404"
1788
+ ).removeprefix("/")
1784
1789
  # re-assign only when the value is different
1785
1790
  if state.router_data != router_data:
1786
1791
  # assignment will recurse into substates and force recalculation of
1787
1792
  # dependent ComputedVar (dynamic route variables)
1788
1793
  state.router_data = router_data
1789
- router_data[constants.RouteVar.PATH] = "/" + (
1790
- app.router(path) or "404"
1791
- if (path := router_data.get(constants.RouteVar.PATH))
1792
- else "404"
1793
- ).removeprefix("/")
1794
1794
  state.router = RouterData.from_router_data(router_data)
1795
1795
 
1796
1796
  # Preprocess the event.
@@ -26,11 +26,40 @@ def redirect_script() -> str:
26
26
  const thisUrl = new URL(window.location.href);
27
27
  const params = new URLSearchParams(thisUrl.search)
28
28
 
29
+ function sameHostnameDifferentPort(one, two) {{
30
+ const hostnameOne = one.hostname;
31
+ const hostnameTwo = two.hostname;
32
+ const partsOne = hostnameOne.split(".");
33
+ const partsTwo = hostnameTwo.split(".");
34
+ if (partsOne.length !== partsTwo.length) {{ return false; }}
35
+ for (let i = 1; i < partsOne.length; i++) {{
36
+ if (partsOne[i] !== partsTwo[i]) {{ return false; }}
37
+ }}
38
+ const uniqueNameOne = partsOne[0];
39
+ const uniqueNameTwo = partsTwo[0];
40
+ const uniqueNamePartsOne = uniqueNameOne.split("-");
41
+ const uniqueNamePartsTwo = uniqueNameTwo.split("-");
42
+ if (uniqueNamePartsOne.length !== uniqueNamePartsTwo.length) {{ return false; }}
43
+ for (let i = 0; i < uniqueNamePartsOne.length - 1; i++) {{
44
+ if (uniqueNamePartsOne[i] !== uniqueNamePartsTwo[i]) {{ return false; }}
45
+ }}
46
+ return true;
47
+ }}
48
+
29
49
  function doRedirect(url) {{
30
50
  if (!window.sessionStorage.getItem("authenticated_github_codespaces")) {{
31
51
  const a = document.createElement("a");
32
52
  if (params.has("redirect_to")) {{
33
- a.href = params.get("redirect_to")
53
+ const redirect_to = new URL(params.get("redirect_to"));
54
+ if (!sameHostnameDifferentPort(thisUrl, redirect_to)) {{
55
+ console.warn("Reflex: Not redirecting to different hostname");
56
+ return;
57
+ }}
58
+ if (!redirect_to.hostname.endsWith(".app.github.dev")) {{
59
+ console.warn("Reflex: Not redirecting to non .app.github.dev hostname");
60
+ return;
61
+ }}
62
+ a.href = redirect_to.href;
34
63
  }} else if (!window.location.href.startsWith(url)) {{
35
64
  a.href = url + `?redirect_to=${{window.location.href}}`
36
65
  }} else {{
reflex/utils/types.py CHANGED
@@ -443,6 +443,13 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
443
443
 
444
444
  from reflex.model import Model
445
445
 
446
+ if find_spec("sqlmodel"):
447
+ from sqlmodel import SQLModel
448
+
449
+ sqlmodel_types = (Model, SQLModel)
450
+ else:
451
+ sqlmodel_types = (Model,)
452
+
446
453
  if isinstance(cls, type) and issubclass(cls, DeclarativeBase):
447
454
  insp = sqlalchemy.inspect(cls)
448
455
  if name in insp.columns:
@@ -486,7 +493,7 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
486
493
  elif (
487
494
  isinstance(cls, type)
488
495
  and not is_generic_alias(cls)
489
- and issubclass(cls, Model)
496
+ and issubclass(cls, sqlmodel_types)
490
497
  ):
491
498
  # Check in the annotations directly (for sqlmodel.Relationship)
492
499
  hints = get_type_hints(cls) # pyright: ignore [reportArgumentType]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reflex
3
- Version: 0.8.15a1
3
+ Version: 0.8.15a2
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=7iJASSyU1dxLM-l6q6gFAkw6FniXvawAekgfwN5zKjM,10328
2
2
  reflex/__init__.pyi,sha256=Yy3exOO_7-O7fCjTKO1VDFbjPyeMM7F12WBnEXWx_tk,10428
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=qBwr17eLnpz64CXLrGD5pvWVqaC9m5XxFDkmA4lJOh4,79240
5
+ reflex/app.py,sha256=RYOqnOFpik1DVi2Yk5b6CdB6OaoPW7pXtVCtIYZGhME,79220
6
6
  reflex/assets.py,sha256=l5O_mlrTprC0lF7Rc_McOe3a0OtSLnRdNl_PqCpDCBA,3431
7
7
  reflex/base.py,sha256=B3DLpG0oEKIYC-PfWE9rnHPCKYrOB6bi08MyZQDSi1s,2326
8
8
  reflex/config.py,sha256=LsHAtdH4nkSn3q_Ie-KNdOGdflLXrFICUQov29oFjVk,21229
@@ -30,7 +30,7 @@ reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js,sha2
30
30
  reflex/.templates/web/components/shiki/code.js,sha256=4Es1pxsr-lX4hTQ5mglrwwC6O_SI-z-O60k03z8VFzQ,1144
31
31
  reflex/.templates/web/styles/__reflex_style_reset.css,sha256=qbC6JIT643YEsvSQ0D7xBmWE5vXy94JGrKNihRuEjnA,8913
32
32
  reflex/.templates/web/utils/react-theme.js,sha256=Aa-RND3ooGCXW6Zavzitc-v0ciKlcQDTFlDtE4mPkFI,2713
33
- reflex/.templates/web/utils/state.js,sha256=RdNHwQzS_iP0Z_fx-H4pjVjl-qUoz31JLvwPwcp0F-s,33046
33
+ reflex/.templates/web/utils/state.js,sha256=7gRWc8ABMOKY3KMF6SpaVEhNILW03wivd7eENVC7ADg,34858
34
34
  reflex/.templates/web/utils/helpers/dataeditor.js,sha256=pG6MgsHuStDR7-qPipzfiK32j9bKDBa-4hZ0JSUo4JM,1623
35
35
  reflex/.templates/web/utils/helpers/debounce.js,sha256=xGhtTRtS_xIcaeqnYVvYJNseLgQVk-DW-eFiHJYO9As,528
36
36
  reflex/.templates/web/utils/helpers/paste.js,sha256=ef30HsR83jRzzvZnl8yV79yqFP8TC_u8SlN99cCS_OM,1799
@@ -350,7 +350,7 @@ reflex/plugins/tailwind_v3.py,sha256=jCEZ5UYdr706Mw48L-WSHOUB6O55o1C3uG6AMwXqZoI
350
350
  reflex/plugins/tailwind_v4.py,sha256=8G7iXj6wYRRJlCWlce_vtCgs1YittFvLucyUP-sPXhc,5230
351
351
  reflex/utils/__init__.py,sha256=y-AHKiRQAhk2oAkvn7W8cRVTZVK625ff8tTwvZtO7S4,24
352
352
  reflex/utils/build.py,sha256=j-OY90O7gMP_bclVt_6J3Q2GFgOHQH_uFpTfdaWmuqU,9746
353
- reflex/utils/codespaces.py,sha256=kEQ-j-jclTukFpXDlYgNp95kYMGDrQmP3VNEoYGZ1u4,3052
353
+ reflex/utils/codespaces.py,sha256=mR7jqbr16WAdiQyaaqA9Pf0g2lclSenQdHM6ngvLGBI,4345
354
354
  reflex/utils/compat.py,sha256=LlFZg8j5Nyul_OR0pMYTDCcaoCR8aEsg8rFpTIGLtvU,3024
355
355
  reflex/utils/console.py,sha256=W41Ogj1Jk8tEOhXXy9dy4KCLYp5rn0NZQwbBqXbkwSI,13668
356
356
  reflex/utils/decorator.py,sha256=QUZntENupeW5FA5mNRTx0I1GzGKFQXhMjVg24_IIM5o,3957
@@ -376,7 +376,7 @@ reflex/utils/serializers.py,sha256=18cnbx5rMftrh6T0bj0L2LqMKHPcFTSiKZ8bx_20Ris,1
376
376
  reflex/utils/telemetry.py,sha256=E_qV7hHKybnWu9MfMSTbJ7-RJebyA1Csh_x6GaCzI_4,10740
377
377
  reflex/utils/templates.py,sha256=uZMK4yilPanRRRKB5VTR2WO88q-d3QjUOOBdF2hu31o,14108
378
378
  reflex/utils/token_manager.py,sha256=ZtrYR0X8tTs8FpQHtMb09-H2V1xSoLWwVH8jW8OCrU8,7445
379
- reflex/utils/types.py,sha256=HWXa4q0cLmqVuvLS11mcSqM8dMLoOoQoQlLkDuRQtfg,38595
379
+ reflex/utils/types.py,sha256=1QqDJuw6v8CvkR8Na-6P4bRjJzQI27ONy19JZl4dVtc,38781
380
380
  reflex/vars/__init__.py,sha256=pUzFFkY-brpEoqYHQc41VefaOdPQG6xzjer1RJy9IKo,1264
381
381
  reflex/vars/base.py,sha256=kZDZV43kpGE-YmX7W4XQePs4iFEESJBcI3c7uyMacyo,112829
382
382
  reflex/vars/color.py,sha256=KxmeEl6wt7Ck7yiyX8gxiRHNgpN3n9LrEqOo0ofOrVw,4898
@@ -387,8 +387,8 @@ reflex/vars/number.py,sha256=Cejba-47shtQt-j0uD_HRfTGOm1IF1uZ1WwpWSrcLSE,28865
387
387
  reflex/vars/object.py,sha256=q_MTTgWEHjtpwDdyfR16kNf3pXcDEbxSjWdB1AtMdVY,17913
388
388
  reflex/vars/sequence.py,sha256=dHtznvmX4CRxIIOsMQ88iQoQ06BFbMqcGvRYwfg0gCg,52440
389
389
  scripts/hatch_build.py,sha256=-4pxcLSFmirmujGpQX9UUxjhIC03tQ_fIQwVbHu9kc0,1861
390
- reflex-0.8.15a1.dist-info/METADATA,sha256=jiwZ8DBuuK87loeGo-l32qw3za3Igh8uBEfB2mz6H6s,12562
391
- reflex-0.8.15a1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
- reflex-0.8.15a1.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
393
- reflex-0.8.15a1.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
394
- reflex-0.8.15a1.dist-info/RECORD,,
390
+ reflex-0.8.15a2.dist-info/METADATA,sha256=4_TNPjSvQ6D2LzcLBsQiM9GHhRdrGcmYqOfMSjGaRiY,12562
391
+ reflex-0.8.15a2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
392
+ reflex-0.8.15a2.dist-info/entry_points.txt,sha256=Rxt4dXc7MLBNt5CSHTehVPuSe9Xqow4HLX55nD9tQQ0,45
393
+ reflex-0.8.15a2.dist-info/licenses/LICENSE,sha256=dw3zLrp9f5ObD7kqS32vWfhcImfO52PMmRqvtxq_YEE,11358
394
+ reflex-0.8.15a2.dist-info/RECORD,,