streamlit-nightly 1.35.1.dev20240616__py2.py3-none-any.whl → 1.36.1.dev20240620__py2.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.
@@ -435,7 +435,9 @@ class AppSession:
435
435
  return True
436
436
 
437
437
  def _on_source_file_changed(self, filepath: str | None = None) -> None:
438
- """One of our source files changed. Clear the cache and schedule a rerun if appropriate."""
438
+ """One of our source files changed. Clear the cache and schedule a rerun if
439
+ appropriate.
440
+ """
439
441
  self._script_cache.clear()
440
442
 
441
443
  if filepath is not None and not self._should_rerun_on_file_change(filepath):
@@ -449,11 +451,13 @@ class AppSession:
449
451
  def _on_secrets_file_changed(self, _) -> None:
450
452
  """Called when `secrets.file_change_listener` emits a Signal."""
451
453
 
452
- # NOTE: At the time of writing, this function only calls `_on_source_file_changed`.
453
- # The reason behind creating this function instead of just passing `_on_source_file_changed`
454
- # to `connect` / `disconnect` directly is that every function that is passed to `connect` / `disconnect`
455
- # must have at least one argument for `sender` (in this case we don't really care about it, thus `_`),
456
- # and introducing an unnecessary argument to `_on_source_file_changed` just for this purpose sounded finicky.
454
+ # NOTE: At the time of writing, this function only calls
455
+ # `_on_source_file_changed`. The reason behind creating this function instead of
456
+ # just passing `_on_source_file_changed` to `connect` / `disconnect` directly is
457
+ # that every function that is passed to `connect` / `disconnect` must have at
458
+ # least one argument for `sender` (in this case we don't really care about it,
459
+ # thus `_`), and introducing an unnecessary argument to
460
+ # `_on_source_file_changed` just for this purpose sounded finicky.
457
461
  self._on_source_file_changed()
458
462
 
459
463
  def _on_pages_changed(self, _) -> None:
@@ -25,6 +25,7 @@ from typing import TYPE_CHECKING, Any, Callable, Protocol, TypeVar, overload
25
25
  from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
26
26
  from streamlit.runtime.metrics_util import gather_metrics
27
27
  from streamlit.runtime.scriptrunner import get_script_run_ctx
28
+ from streamlit.runtime.scriptrunner.exec_code import exec_func_with_error_handling
28
29
  from streamlit.time_util import time_to_seconds
29
30
 
30
31
  if TYPE_CHECKING:
@@ -100,8 +101,9 @@ def _fragment(
100
101
  ) -> Callable[[F], F] | F:
101
102
  """Contains the actual fragment logic.
102
103
 
103
- This function should be used by our internal functions that use fragments under-the-hood,
104
- so that fragment metrics are not tracked for those elements (note that the @gather_metrics annotation is only on the publicly exposed function)
104
+ This function should be used by our internal functions that use fragments
105
+ under-the-hood, so that fragment metrics are not tracked for those elements
106
+ (note that the @gather_metrics annotation is only on the publicly exposed function)
105
107
  """
106
108
 
107
109
  if func is None:
@@ -191,7 +193,15 @@ def _fragment(
191
193
  msg.auto_rerun.fragment_id = fragment_id
192
194
  ctx.enqueue(msg)
193
195
 
194
- return wrapped_fragment()
196
+ # Wrap the fragment function in the same try-except block as in a normal
197
+ # script_run so that for a main-app run (this execution) and a fragment-rerun
198
+ # the same execution and error-handling logic is used. This makes errors in the
199
+ # fragment appear in the fragment path also for the first execution here in
200
+ # context of a full app run.
201
+ result, _, _, _ = exec_func_with_error_handling(
202
+ wrapped_fragment, ctx, reraise_rerun_exception=True
203
+ )
204
+ return result
195
205
 
196
206
  with contextlib.suppress(AttributeError):
197
207
  # Make this a well-behaved decorator by preserving important function
@@ -362,7 +362,7 @@ def gather_metrics(name: str, func: F | None = None) -> Callable[[F], F] | F:
362
362
  exec_start = timer()
363
363
  # Local imports to prevent circular dependencies
364
364
  from streamlit.runtime.scriptrunner import get_script_run_ctx
365
- from streamlit.runtime.scriptrunner.script_runner import RerunException
365
+ from streamlit.runtime.scriptrunner.exceptions import RerunException
366
366
 
367
367
  ctx = get_script_run_ctx(suppress_warning=True)
368
368
 
@@ -12,18 +12,14 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from streamlit.runtime.scriptrunner.exceptions import RerunException, StopException
15
16
  from streamlit.runtime.scriptrunner.script_requests import RerunData
16
17
  from streamlit.runtime.scriptrunner.script_run_context import (
17
18
  ScriptRunContext,
18
19
  add_script_run_ctx,
19
20
  get_script_run_ctx,
20
21
  )
21
- from streamlit.runtime.scriptrunner.script_runner import (
22
- RerunException,
23
- ScriptRunner,
24
- ScriptRunnerEvent,
25
- StopException,
26
- )
22
+ from streamlit.runtime.scriptrunner.script_runner import ScriptRunner, ScriptRunnerEvent
27
23
 
28
24
  __all__ = [
29
25
  "RerunData",
@@ -0,0 +1,45 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from streamlit.runtime.scriptrunner.script_requests import RerunData
16
+ from streamlit.util import repr_
17
+
18
+
19
+ class ScriptControlException(Exception):
20
+ """Base exception for ScriptRunner."""
21
+
22
+ pass
23
+
24
+
25
+ class StopException(ScriptControlException):
26
+ """Silently stop the execution of the user's script."""
27
+
28
+ pass
29
+
30
+
31
+ class RerunException(ScriptControlException):
32
+ """Silently stop and rerun the user's script."""
33
+
34
+ def __init__(self, rerun_data: RerunData):
35
+ """Construct a RerunException
36
+
37
+ Parameters
38
+ ----------
39
+ rerun_data : RerunData
40
+ The RerunData that should be used to rerun the script
41
+ """
42
+ self.rerun_data = rerun_data
43
+
44
+ def __repr__(self) -> str:
45
+ return repr_(self)
@@ -0,0 +1,126 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2024)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any, Callable
18
+
19
+ from streamlit.error_util import handle_uncaught_app_exception
20
+ from streamlit.runtime.scriptrunner.exceptions import RerunException, StopException
21
+
22
+ if TYPE_CHECKING:
23
+ from streamlit.runtime.scriptrunner.script_requests import RerunData
24
+ from streamlit.runtime.scriptrunner.script_run_context import ScriptRunContext
25
+
26
+
27
+ def exec_func_with_error_handling(
28
+ func: Callable[[], None],
29
+ ctx: ScriptRunContext,
30
+ *,
31
+ reraise_rerun_exception: bool = False,
32
+ ) -> tuple[Any | None, bool, RerunData | None, bool]:
33
+ """Execute the passed function wrapped in a try/except block.
34
+
35
+ This function is called by the script runner to execute the user's script or
36
+ fragment reruns, but also for the execution of fragment code in context of a normal
37
+ app run. This wrapper ensures that handle_uncaught_exception messages show up in the
38
+ correct context.
39
+
40
+ Parameters
41
+ ----------
42
+ func : callable
43
+ The function to execute wrapped in the try/except block.
44
+ ctx : ScriptRunContext
45
+ The context in which the script is being run.
46
+ reraise_rerun_exception : bool, default False
47
+ If True, an occuring RerunException will be raised instead of handled. This can
48
+ be used if this function is called outside of the script_run context and we want
49
+ the script_runner to react on the rerun exception.
50
+
51
+ Returns
52
+ -------
53
+ tuple
54
+ A tuple containing:
55
+ - The result of the passed function.
56
+ - A boolean indicating whether the script ran without errors (RerunException and
57
+ StopException don't count as errors).
58
+ - The RerunData instance belonging to a RerunException if the script was
59
+ interrupted by a RerunException.
60
+ - A boolean indicating whether the script was stopped prematurely (False for
61
+ RerunExceptions, True for all other exceptions).
62
+ """
63
+
64
+ # Avoid circular imports
65
+ from streamlit.delta_generator import dg_stack, get_default_dg_stack
66
+
67
+ run_without_errors = True
68
+
69
+ # This will be set to a RerunData instance if our execution
70
+ # is interrupted by a RerunException.
71
+ rerun_exception_data: RerunData | None = None
72
+
73
+ # Saving and restoring our original cursors/dg_stack is needed
74
+ # specifically to handle the case where a RerunException is raised while
75
+ # running a fragment. In this case, we need to restore both to their states
76
+ # at the start of the script run to ensure that we write to the correct
77
+ # places in the app during the rerun (without this, ctx.cursors and dg_stack
78
+ # will still be set to the snapshots they were restored from when running
79
+ # the fragment).
80
+ original_cursors = ctx.cursors
81
+ original_dg_stack = dg_stack.get()
82
+
83
+ # If the script stops early, we don't want to remove unseen widgets,
84
+ # so we track this to potentially skip session state cleanup later.
85
+ premature_stop: bool = False
86
+
87
+ # The result of the passed function
88
+ result: Any | None = None
89
+
90
+ try:
91
+ result = func()
92
+ except RerunException as e:
93
+ if reraise_rerun_exception:
94
+ raise e
95
+
96
+ rerun_exception_data = e.rerun_data
97
+ if rerun_exception_data.fragment_id_queue:
98
+ # This is a fragment-specific rerun, so we need to restore the stack
99
+ ctx.cursors = original_cursors
100
+ dg_stack.set(original_dg_stack)
101
+ else:
102
+ # If it is a full-app rerun, the stack needs to be refreshed.
103
+ # We should land here when `st.rerun` is called from within a
104
+ # fragment. Since we re-use the same thread, we have to clear the
105
+ # stack or otherwise we might render the main app in the old
106
+ # fragment's dg_stack.
107
+ ctx.cursors.clear()
108
+ dg_stack.set(get_default_dg_stack())
109
+ # Interruption due to a rerun is usually from `st.rerun()`, which
110
+ # we want to count as a script completion so triggers reset.
111
+ # It is also possible for this to happen if fast reruns is off,
112
+ # but this is very rare.
113
+ premature_stop = False
114
+
115
+ except StopException:
116
+ # This is thrown when the script executes `st.stop()`.
117
+ # We don't have to do anything here.
118
+ premature_stop = True
119
+
120
+ except Exception as ex:
121
+ run_without_errors = False
122
+ uncaught_exception = ex
123
+ handle_uncaught_app_exception(uncaught_exception)
124
+ premature_stop = True
125
+
126
+ return result, run_without_errors, rerun_exception_data, premature_stop
@@ -26,10 +26,12 @@ from typing import TYPE_CHECKING, Callable, Final
26
26
  from blinker import Signal
27
27
 
28
28
  from streamlit import config, runtime, util
29
- from streamlit.error_util import handle_uncaught_app_exception
30
29
  from streamlit.logger import get_logger
31
30
  from streamlit.proto.ClientState_pb2 import ClientState
32
31
  from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
32
+ from streamlit.runtime.scriptrunner.exceptions import RerunException, StopException
33
+ from streamlit.runtime.scriptrunner.exec_code import exec_func_with_error_handling
34
+ from streamlit.runtime.scriptrunner.script_cache import ScriptCache
33
35
  from streamlit.runtime.scriptrunner.script_requests import (
34
36
  RerunData,
35
37
  ScriptRequests,
@@ -267,7 +269,8 @@ class ScriptRunner:
267
269
  if ctx is None:
268
270
  # This should never be possible on the script_runner thread.
269
271
  raise RuntimeError(
270
- "ScriptRunner thread has a null ScriptRunContext. Something has gone very wrong!"
272
+ "ScriptRunner thread has a null ScriptRunContext. "
273
+ "Something has gone very wrong!"
271
274
  )
272
275
  return ctx
273
276
 
@@ -414,8 +417,6 @@ class ScriptRunner:
414
417
  The RerunData to use.
415
418
 
416
419
  """
417
- # Avoid circular imports
418
- from streamlit.delta_generator import dg_stack
419
420
 
420
421
  assert self._is_in_script_thread()
421
422
 
@@ -522,43 +523,25 @@ class ScriptRunner:
522
523
  if config.get_option("runner.installTracer"):
523
524
  self._install_tracer()
524
525
 
525
- # This will be set to a RerunData instance if our execution
526
- # is interrupted by a RerunException.
527
- rerun_exception_data: RerunData | None = None
528
-
529
- # Saving and restoring our original cursors/dg_stack is needed
530
- # specifically to handle the case where a RerunException is raised while
531
- # running a fragment. In this case, we need to restore both to their states
532
- # at the start of the script run to ensure that we write to the correct
533
- # places in the app during the rerun (without this, ctx.cursors and dg_stack
534
- # will still be set to the snapshots they were restored from when running
535
- # the fragment).
536
- original_cursors = ctx.cursors
537
- original_dg_stack = dg_stack.get()
538
-
539
- # If the script stops early, we don't want to remove unseen widgets,
540
- # so we track this to potentially skip session state cleanup later.
541
- premature_stop: bool = False
542
-
543
- try:
544
- # Create fake module. This gives us a name global namespace to
545
- # execute the code in.
546
- module = self._new_module("__main__")
547
-
548
- # Install the fake module as the __main__ module. This allows
549
- # the pickle module to work inside the user's code, since it now
550
- # can know the module where the pickled objects stem from.
551
- # IMPORTANT: This means we can't use "if __name__ == '__main__'" in
552
- # our code, as it will point to the wrong module!!!
553
- sys.modules["__main__"] = module
554
-
555
- # Add special variables to the module's globals dict.
556
- # Note: The following is a requirement for the CodeHasher to
557
- # work correctly. The CodeHasher is scoped to
558
- # files contained in the directory of __main__.__file__, which we
559
- # assume is the main script directory.
560
- module.__dict__["__file__"] = script_path
561
-
526
+ # Create fake module. This gives us a name global namespace to
527
+ # execute the code in.
528
+ module = self._new_module("__main__")
529
+
530
+ # Install the fake module as the __main__ module. This allows
531
+ # the pickle module to work inside the user's code, since it now
532
+ # can know the module where the pickled objects stem from.
533
+ # IMPORTANT: This means we can't use "if __name__ == '__main__'" in
534
+ # our code, as it will point to the wrong module!!!
535
+ sys.modules["__main__"] = module
536
+
537
+ # Add special variables to the module's globals dict.
538
+ # Note: The following is a requirement for the CodeHasher to
539
+ # work correctly. The CodeHasher is scoped to
540
+ # files contained in the directory of __main__.__file__, which we
541
+ # assume is the main script directory.
542
+ module.__dict__["__file__"] = script_path
543
+
544
+ def code_to_exec(code=code, module=module, ctx=ctx, rerun_data=rerun_data):
562
545
  with modified_sys_path(
563
546
  self._main_script_path
564
547
  ), self._set_execing_flag():
@@ -569,7 +552,6 @@ class ScriptRunner:
569
552
  )
570
553
 
571
554
  ctx.on_script_start()
572
- prep_time = timer() - start_time
573
555
 
574
556
  if rerun_data.fragment_id_queue:
575
557
  for fragment_id in rerun_data.fragment_id_queue:
@@ -589,67 +571,58 @@ class ScriptRunner:
589
571
  exec(code, module.__dict__)
590
572
 
591
573
  self._session_state.maybe_check_serializable()
592
- self._session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY] = True
593
- except RerunException as e:
594
- rerun_exception_data = e.rerun_data
595
- ctx.cursors = original_cursors
596
- dg_stack.set(original_dg_stack)
597
- # Interruption due to a rerun is usually from `st.rerun()`, which
598
- # we want to count as a script completion so triggers reset.
599
- # It is also possible for this to happen if fast reruns is off,
600
- # but this is very rare.
601
- premature_stop = False
602
-
603
- except StopException:
604
- # This is thrown when the script executes `st.stop()`.
605
- # We don't have to do anything here.
606
- premature_stop = True
607
-
608
- except Exception as ex:
609
- self._session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY] = False
610
- uncaught_exception = ex
611
- handle_uncaught_app_exception(uncaught_exception)
612
- premature_stop = True
613
-
614
- finally:
615
- if rerun_exception_data:
616
- # The handling for when a full script run or a fragment is stopped early
617
- # is the same, so we only have one ScriptRunnerEvent for this scenario.
618
- finished_event = ScriptRunnerEvent.SCRIPT_STOPPED_FOR_RERUN
619
- elif rerun_data.fragment_id_queue:
620
- finished_event = ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
621
- else:
622
- finished_event = ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
623
-
624
- if ctx.gather_usage_stats:
625
- try:
626
- # Prevent issues with circular import
627
- from streamlit.runtime.metrics_util import (
628
- create_page_profile_message,
629
- to_microseconds,
630
- )
631
-
632
- # Create and send page profile information
633
- ctx.enqueue(
634
- create_page_profile_message(
635
- ctx.tracked_commands,
636
- exec_time=to_microseconds(timer() - start_time),
637
- prep_time=to_microseconds(prep_time),
638
- uncaught_exception=(
639
- type(uncaught_exception).__name__
640
- if uncaught_exception
641
- else None
642
- ),
643
- )
574
+ # check for control requests, e.g. rerun requests have arrived
575
+ self._maybe_handle_execution_control_request()
576
+
577
+ prep_time = timer() - start_time
578
+ (
579
+ _,
580
+ run_without_errors,
581
+ rerun_exception_data,
582
+ premature_stop,
583
+ ) = exec_func_with_error_handling(code_to_exec, ctx)
584
+ # setting the session state here triggers a yield-callback call
585
+ # which reads self._requests and checks for rerun data
586
+ self._session_state[SCRIPT_RUN_WITHOUT_ERRORS_KEY] = run_without_errors
587
+
588
+ if rerun_exception_data:
589
+ # The handling for when a full script run or a fragment is stopped early
590
+ # is the same, so we only have one ScriptRunnerEvent for this scenario.
591
+ finished_event = ScriptRunnerEvent.SCRIPT_STOPPED_FOR_RERUN
592
+ elif rerun_data.fragment_id_queue:
593
+ finished_event = ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
594
+ else:
595
+ finished_event = ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
596
+
597
+ if ctx.gather_usage_stats:
598
+ try:
599
+ # Prevent issues with circular import
600
+ from streamlit.runtime.metrics_util import (
601
+ create_page_profile_message,
602
+ to_microseconds,
603
+ )
604
+
605
+ # Create and send page profile information
606
+ ctx.enqueue(
607
+ create_page_profile_message(
608
+ ctx.tracked_commands,
609
+ exec_time=to_microseconds(timer() - start_time),
610
+ prep_time=to_microseconds(prep_time),
611
+ uncaught_exception=(
612
+ type(uncaught_exception).__name__
613
+ if uncaught_exception
614
+ else None
615
+ ),
644
616
  )
645
- except Exception as ex:
646
- # Always capture all exceptions since we want to make sure that
647
- # the telemetry never causes any issues.
648
- _LOGGER.debug("Failed to create page profile", exc_info=ex)
649
- self._on_script_finished(ctx, finished_event, premature_stop)
650
-
651
- # Use _log_if_error() to make sure we never ever ever stop running the
652
- # script without meaning to.
617
+ )
618
+ except Exception as ex:
619
+ # Always capture all exceptions since we want to make sure that
620
+ # the telemetry never causes any issues.
621
+ _LOGGER.debug("Failed to create page profile", exc_info=ex)
622
+ self._on_script_finished(ctx, finished_event, premature_stop)
623
+
624
+ # # Use _log_if_error() to make sure we never ever ever stop running the
625
+ # # script without meaning to.
653
626
  _log_if_error(_clean_problem_modules)
654
627
 
655
628
  if rerun_exception_data is not None:
@@ -687,35 +660,6 @@ class ScriptRunner:
687
660
  return types.ModuleType(name)
688
661
 
689
662
 
690
- class ScriptControlException(BaseException):
691
- """Base exception for ScriptRunner."""
692
-
693
- pass
694
-
695
-
696
- class StopException(ScriptControlException):
697
- """Silently stop the execution of the user's script."""
698
-
699
- pass
700
-
701
-
702
- class RerunException(ScriptControlException):
703
- """Silently stop and rerun the user's script."""
704
-
705
- def __init__(self, rerun_data: RerunData):
706
- """Construct a RerunException
707
-
708
- Parameters
709
- ----------
710
- rerun_data : RerunData
711
- The RerunData that should be used to rerun the script
712
- """
713
- self.rerun_data = rerun_data
714
-
715
- def __repr__(self) -> str:
716
- return util.repr_(self)
717
-
718
-
719
663
  def _clean_problem_modules() -> None:
720
664
  """Some modules are stateful, so we have to clear their state."""
721
665
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "./static/css/main.3aaaea00.css",
4
- "main.js": "./static/js/main.7994a814.js",
4
+ "main.js": "./static/js/main.ba32d829.js",
5
5
  "static/js/9336.3e046ad7.chunk.js": "./static/js/9336.3e046ad7.chunk.js",
6
6
  "static/js/9330.2b4c99e0.chunk.js": "./static/js/9330.2b4c99e0.chunk.js",
7
7
  "static/js/2736.4336e2b9.chunk.js": "./static/js/2736.4336e2b9.chunk.js",
@@ -151,6 +151,6 @@
151
151
  },
152
152
  "entrypoints": [
153
153
  "static/css/main.3aaaea00.css",
154
- "static/js/main.7994a814.js"
154
+ "static/js/main.ba32d829.js"
155
155
  ]
156
156
  }
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><link rel="shortcut icon" href="./favicon.png"/><link rel="preload" href="./static/media/SourceSansPro-Regular.0d69e5ff5e92ac64a0c9.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-SemiBold.abed79cd0df1827e18cf.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-Bold.118dea98980e20a81ced.woff2" as="font" type="font/woff2" crossorigin><title>Streamlit</title><script>window.prerenderReady=!1</script><script defer="defer" src="./static/js/main.7994a814.js"></script><link href="./static/css/main.3aaaea00.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><link rel="shortcut icon" href="./favicon.png"/><link rel="preload" href="./static/media/SourceSansPro-Regular.0d69e5ff5e92ac64a0c9.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-SemiBold.abed79cd0df1827e18cf.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="./static/media/SourceSansPro-Bold.118dea98980e20a81ced.woff2" as="font" type="font/woff2" crossorigin><title>Streamlit</title><script>window.prerenderReady=!1</script><script defer="defer" src="./static/js/main.ba32d829.js"></script><link href="./static/css/main.3aaaea00.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>