streamlit-nightly 1.52.3.dev20260108__py3-none-any.whl → 1.52.3.dev20260110__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.
Files changed (123) hide show
  1. streamlit/components/v2/__init__.py +3 -3
  2. streamlit/components/v2/types.py +4 -4
  3. streamlit/config.py +14 -0
  4. streamlit/elements/lib/options_selector_utils.py +98 -0
  5. streamlit/elements/widgets/multiselect.py +19 -9
  6. streamlit/elements/widgets/selectbox.py +12 -24
  7. streamlit/proto/MetricsEvent_pb2.py +4 -4
  8. streamlit/proto/MetricsEvent_pb2.pyi +4 -1
  9. streamlit/proto/PageProfile_pb2.py +6 -6
  10. streamlit/proto/PageProfile_pb2.pyi +11 -1
  11. streamlit/runtime/metrics_util.py +4 -0
  12. streamlit/runtime/runtime.py +3 -0
  13. streamlit/starlette.py +34 -0
  14. streamlit/static/index.html +1 -1
  15. streamlit/static/manifest.json +300 -300
  16. streamlit/static/static/js/{ErrorOutline.esm.j3b3OjAK.js → ErrorOutline.esm.BcqUpfNe.js} +1 -1
  17. streamlit/static/static/js/{FileDownload.esm.DCizXv6Q.js → FileDownload.esm.CtJWBuub.js} +1 -1
  18. streamlit/static/static/js/{FileHelper.EpMV5UVe.js → FileHelper.D0dQPhOs.js} +1 -1
  19. streamlit/static/static/js/{FormClearHelper.lF7Ran5M.js → FormClearHelper.Cm3GDSk6.js} +1 -1
  20. streamlit/static/static/js/{InputInstructions.CMvqhPhy.js → InputInstructions.D7Hxdzwv.js} +1 -1
  21. streamlit/static/static/js/{Particles.DsGe8psi.js → Particles.vAUDtAR8.js} +1 -1
  22. streamlit/static/static/js/{ProgressBar.DzoKn4D-.js → ProgressBar.Dp2CGRba.js} +2 -2
  23. streamlit/static/static/js/{StreamlitSyntaxHighlighter.CFab0b_b.js → StreamlitSyntaxHighlighter.DC0000nJ.js} +1 -1
  24. streamlit/static/static/js/{TableChart.esm.nZsTq1Sb.js → TableChart.esm.DDVoSKOT.js} +1 -1
  25. streamlit/static/static/js/{Toolbar.CFMvwQYl.js → Toolbar.CuMH-Gqe.js} +1 -1
  26. streamlit/static/static/js/{WidgetLabelHelpIconInline.D2EEUEQX.js → WidgetLabelHelpIconInline.WAReecol.js} +1 -1
  27. streamlit/static/static/js/{base-input.DKTA2QNz.js → base-input.BX9Jll5o.js} +4 -4
  28. streamlit/static/static/js/{checkbox.D9H9J-_W.js → checkbox.BzMHUSz1.js} +1 -1
  29. streamlit/static/static/js/{createDownloadLinkElement.DCk6EhPM.js → createDownloadLinkElement.CviG5BQx.js} +1 -1
  30. streamlit/static/static/js/{data-grid-overlay-editor.DExrGdqs.js → data-grid-overlay-editor.Dzo9A4l6.js} +1 -1
  31. streamlit/static/static/js/{downloader.CLJ7BreF.js → downloader.DIDvj0d5.js} +1 -1
  32. streamlit/static/static/js/{embed.CxOHZWx2.js → embed.C8qeQ38b.js} +6 -6
  33. streamlit/static/static/js/{es6.C99ebre4.js → es6.Dpcc-U7U.js} +2 -2
  34. streamlit/static/static/js/{formatNumber.D_w4fBsk.js → formatNumber.CPvuaBa8.js} +1 -1
  35. streamlit/static/static/js/{iconPosition.Cfhw1RkE.js → iconPosition.y0q-Rqem.js} +1 -1
  36. streamlit/static/static/js/{iframeResizer.contentWindow.BcWUIYOe.js → iframeResizer.contentWindow.DblExdXF.js} +1 -1
  37. streamlit/static/static/js/{index.BWK_h3IL.js → index.B-g6lwYa.js} +1 -1
  38. streamlit/static/static/js/{index.DoLorXMA.js → index.B10MmI2m.js} +1 -1
  39. streamlit/static/static/js/{index.Dx8TcTHV.js → index.B6vCS66f.js} +6 -6
  40. streamlit/static/static/js/{index.Bp_LrAiI.js → index.B9TG5Ah8.js} +1 -1
  41. streamlit/static/static/js/{index.CrJ9KZpt.js → index.BDb0PRvK.js} +1 -1
  42. streamlit/static/static/js/{index.1AemKTSK.js → index.BGufEmCz.js} +1 -1
  43. streamlit/static/static/js/{index.7Q3Iaebc.js → index.BItU4jMo.js} +1 -1
  44. streamlit/static/static/js/{index.D18KqoUa.js → index.BPMqWDef.js} +1 -1
  45. streamlit/static/static/js/index.BTHi5W25.js +1 -0
  46. streamlit/static/static/js/{index.BHx4Qw7z.js → index.BVN9cI-k.js} +1 -1
  47. streamlit/static/static/js/index.BWOP7HFT.js +1 -0
  48. streamlit/static/static/js/{index.DDx6TP95.js → index.BXYgO5B8.js} +1 -1
  49. streamlit/static/static/js/{index.DkSjHoXw.js → index.BZBWLU1C.js} +8 -8
  50. streamlit/static/static/js/{index.f_s01aPm.js → index.BfZdZpv-.js} +2 -2
  51. streamlit/static/static/js/{index.C7_5JMRC.js → index.Bxe7fKbw.js} +1 -1
  52. streamlit/static/static/js/{index.BcbR2mbc.js → index.C1ElSZEy.js} +1 -1
  53. streamlit/static/static/js/index.C26ZOVFL.js +1 -0
  54. streamlit/static/static/js/{index.aJ3XRx8R.js → index.C7lwRBvF.js} +1 -1
  55. streamlit/static/static/js/{index.CGX2fllG.js → index.C93QGPyk.js} +1 -1
  56. streamlit/static/static/js/{index.COjurlZk.js → index.CCDejIvL.js} +2 -2
  57. streamlit/static/static/js/index.CcMmNHAq.js +1 -0
  58. streamlit/static/static/js/index.D42y-GeO.js +1 -0
  59. streamlit/static/static/js/{index.C0VFHmJN.js → index.D69ULFWq.js} +1 -1
  60. streamlit/static/static/js/{index.V4C1Oi-F.js → index.DB4MbQ40.js} +3 -3
  61. streamlit/static/static/js/{index.DJ4GBc1k.js → index.DRFJgBf-.js} +1 -1
  62. streamlit/static/static/js/{index.BXQNt1hj.js → index.DTK-btqV.js} +1 -1
  63. streamlit/static/static/js/{index.CFE-yHdT.js → index.DZEQ0G7H.js} +1 -1
  64. streamlit/static/static/js/{index.Bu3Lto_G.js → index.DaU1ayM7.js} +1 -1
  65. streamlit/static/static/js/{index.DSSQzzPk.js → index.Dla9XiNe.js} +2 -2
  66. streamlit/static/static/js/{index.DiZfOR0A.js → index.DmQqT9OM.js} +1 -1
  67. streamlit/static/static/js/{index.DuFqxjbN.js → index.DppScppA.js} +1 -1
  68. streamlit/static/static/js/{index.Dqphk1ee.js → index.Dr8b3Vn6.js} +1 -1
  69. streamlit/static/static/js/{index.CPo5dtx7.js → index.DvQ7-afx.js} +1 -1
  70. streamlit/static/static/js/{index.ATP5607r.js → index.F-NmmwfK.js} +1 -1
  71. streamlit/static/static/js/{index.gnFSTAhI.js → index.MKI8t7l2.js} +1 -1
  72. streamlit/static/static/js/{index.BXnQdCa5.js → index.RRAKXgBZ.js} +1 -1
  73. streamlit/static/static/js/{index.CyVBY8PG.js → index.VSsf6tsu.js} +2 -2
  74. streamlit/static/static/js/{index.DcudoGfL.js → index.aEeM0ekc.js} +1 -1
  75. streamlit/static/static/js/{index.Ds-w0zIo.js → index.eweE9HKU.js} +1 -1
  76. streamlit/static/static/js/{index.CBZQ_6AF.js → index.gr6yGiCL.js} +1 -1
  77. streamlit/static/static/js/{index.mZ1qbnKs.js → index.kKkHk9Mc.js} +1 -1
  78. streamlit/static/static/js/index.pMvzHC7z.js +1 -0
  79. streamlit/static/static/js/{index.DIIdzDwK.js → index.pgifwCIr.js} +1 -1
  80. streamlit/static/static/js/index.q83b8_8b.js +1 -0
  81. streamlit/static/static/js/{index.BzQChe4y.js → index.u2AvcQQT.js} +1 -1
  82. streamlit/static/static/js/{index.CrzXL2V8.js → index.w2_VrtVw.js} +1 -1
  83. streamlit/static/static/js/{index.DbmtfcDm.js → index.wv1DYVsn.js} +1 -1
  84. streamlit/static/static/js/{index.COZICZL6.js → index.x4j6nnNb.js} +1 -1
  85. streamlit/static/static/js/{input.CZPD7mCu.js → input.BoJqOS7a.js} +1 -1
  86. streamlit/static/static/js/{main.CX1jAiMw.js → main.B5XmUwmQ.js} +1 -1
  87. streamlit/static/static/js/{memory.m0jC5ULx.js → memory.9izgq54V.js} +1 -1
  88. streamlit/static/static/js/{number-overlay-editor.Dy0iTeCo.js → number-overlay-editor.PqOGQcLn.js} +1 -1
  89. streamlit/static/static/js/{pandasStylerUtils.D9jj-wHU.js → pandasStylerUtils.DUtF2t3R.js} +1 -1
  90. streamlit/static/static/js/{sandbox.CbvG1iAz.js → sandbox.DgKTHPLO.js} +1 -1
  91. streamlit/static/static/js/{styled-components.Cw3ioniY.js → styled-components.D4x8jmOI.js} +1 -1
  92. streamlit/static/static/js/{throttle.CAwhGpn0.js → throttle.DxEHIIp7.js} +1 -1
  93. streamlit/static/static/js/{timepicker.Bh3m6Pjp.js → timepicker.CDks5DWI.js} +1 -1
  94. streamlit/static/static/js/{toConsumableArray.DM0o32JS.js → toConsumableArray.CvTQRsV-.js} +1 -1
  95. streamlit/static/static/js/uniqueId.RScLN3St.js +1 -0
  96. streamlit/static/static/js/{useBasicWidgetState.C9zOVP8a.js → useBasicWidgetState.D6_b0IqA.js} +1 -1
  97. streamlit/static/static/js/{useIntlLocale.Bo42aN1U.js → useIntlLocale.Ctz17A7g.js} +1 -1
  98. streamlit/static/static/js/{useTextInputAutoExpand.DmyHLDxl.js → useTextInputAutoExpand.PAFB5o1T.js} +1 -1
  99. streamlit/static/static/js/{useUpdateUiValue.aWXWpqmw.js → useUpdateUiValue.CcTvmGlb.js} +1 -1
  100. streamlit/static/static/js/{useWaveformController.DlE14M1X.js → useWaveformController.vLi36Ir4.js} +1 -1
  101. streamlit/static/static/js/{withCalculatedWidth.B5JFJSmG.js → withCalculatedWidth.BtA2jL5-.js} +1 -1
  102. streamlit/static/static/js/{withFullScreenWrapper.dgVioHk1.js → withFullScreenWrapper.CeeoYBpc.js} +1 -1
  103. streamlit/web/bootstrap.py +105 -10
  104. streamlit/web/cli.py +21 -4
  105. streamlit/web/server/app_discovery.py +421 -0
  106. streamlit/web/server/server.py +0 -13
  107. streamlit/web/server/starlette/__init__.py +2 -1
  108. streamlit/web/server/starlette/starlette_app.py +326 -3
  109. streamlit/web/server/starlette/starlette_routes.py +27 -8
  110. {streamlit_nightly-1.52.3.dev20260108.dist-info → streamlit_nightly-1.52.3.dev20260110.dist-info}/METADATA +1 -1
  111. {streamlit_nightly-1.52.3.dev20260108.dist-info → streamlit_nightly-1.52.3.dev20260110.dist-info}/RECORD +115 -113
  112. streamlit/static/static/js/index.BOGNGR9a.js +0 -1
  113. streamlit/static/static/js/index.C-lnh8pI.js +0 -1
  114. streamlit/static/static/js/index.C98anBCM.js +0 -1
  115. streamlit/static/static/js/index.CFtGP8pH.js +0 -1
  116. streamlit/static/static/js/index.Cg59Loqx.js +0 -1
  117. streamlit/static/static/js/index.CiU2Tdcl.js +0 -1
  118. streamlit/static/static/js/index.DpnqUQVD.js +0 -1
  119. streamlit/static/static/js/uniqueId.DtV_RZzG.js +0 -1
  120. {streamlit_nightly-1.52.3.dev20260108.data → streamlit_nightly-1.52.3.dev20260110.data}/scripts/streamlit.cmd +0 -0
  121. {streamlit_nightly-1.52.3.dev20260108.dist-info → streamlit_nightly-1.52.3.dev20260110.dist-info}/WHEEL +0 -0
  122. {streamlit_nightly-1.52.3.dev20260108.dist-info → streamlit_nightly-1.52.3.dev20260110.dist-info}/entry_points.txt +0 -0
  123. {streamlit_nightly-1.52.3.dev20260108.dist-info → streamlit_nightly-1.52.3.dev20260110.dist-info}/top_level.txt +0 -0
@@ -28,7 +28,7 @@ from streamlit.components.v2.get_bidi_component_manager import (
28
28
  from streamlit.errors import StreamlitAPIException
29
29
 
30
30
  if TYPE_CHECKING:
31
- from streamlit.components.v2.types import BidiComponentCallable
31
+ from streamlit.components.v2.types import ComponentRenderer
32
32
 
33
33
  if TYPE_CHECKING:
34
34
  from collections.abc import Callable
@@ -211,7 +211,7 @@ def component(
211
211
  css: str | None = None,
212
212
  js: str | None = None,
213
213
  isolate_styles: bool = True,
214
- ) -> BidiComponentCallable:
214
+ ) -> ComponentRenderer:
215
215
  '''Register an ``st.components.v2`` component and return a callable to mount it.
216
216
 
217
217
  Components may provide any combination of HTML, CSS, and JavaScript. If none
@@ -287,7 +287,7 @@ def component(
287
287
 
288
288
  Returns
289
289
  -------
290
- BidiComponentCallable
290
+ ComponentRenderer
291
291
  The component's mounting command.
292
292
 
293
293
  This callable accepts the component parameters like ``key`` and
@@ -45,7 +45,7 @@ BidiComponentDefaults = dict[str, Any] | None
45
45
  ComponentIsolateStyles = bool
46
46
 
47
47
 
48
- class BidiComponentCallable(Protocol):
48
+ class ComponentRenderer(Protocol):
49
49
  '''Signature of the mounting command returned by ``st.components.v2.component``.
50
50
 
51
51
  This callable mounts a bidirectional component in a Streamlit app and
@@ -69,8 +69,8 @@ class BidiComponentCallable(Protocol):
69
69
  .. note::
70
70
  If you want to access this key in your component's frontend, you
71
71
  must pass it explicitly within the ``data`` parameter. The ``key``
72
- parameter in ``BidiComponentCallable`` is not the same as the
73
- ``key`` property in ``ComponentArgs`` in the component's frontend
72
+ parameter in ``ComponentRenderer`` is not the same as the
73
+ ``key`` property in ``FrontendRendererArgs`` in the component's frontend
74
74
  code.
75
75
 
76
76
  The frontend key is automatically generated to be unique among all
@@ -309,9 +309,9 @@ class BidiComponentCallable(Protocol):
309
309
 
310
310
 
311
311
  __all__ = [
312
- "BidiComponentCallable",
313
312
  "BidiComponentData",
314
313
  "BidiComponentDefaults",
315
314
  "BidiComponentKey",
316
315
  "ComponentIsolateStyles",
316
+ "ComponentRenderer",
317
317
  ]
streamlit/config.py CHANGED
@@ -62,6 +62,20 @@ _config_options: dict[str, ConfigOption] | None = None
62
62
  # resolve config and secret files relative to the main script:
63
63
  _main_script_path: str | None = None
64
64
 
65
+ # Stores the server mode for metrics tracking.
66
+ # Possible values:
67
+ # - "tornado": Traditional Tornado server
68
+ # - "starlette-managed": Starlette server via server.useStarlette config
69
+ # - "starlette-app": st.App started via streamlit run
70
+ # - "asgi-server": st.App with external ASGI server (uvicorn, gunicorn, etc.)
71
+ # - "asgi-mounted": st.App mounted on another ASGI framework (FastAPI, Starlette)
72
+ _server_mode: (
73
+ Literal[
74
+ "tornado", "starlette-managed", "starlette-app", "asgi-server", "asgi-mounted"
75
+ ]
76
+ | None
77
+ ) = None
78
+
65
79
  # Indicates that a config option was defined by the user.
66
80
  _USER_DEFINED: Final = "<user defined>"
67
81
 
@@ -20,6 +20,7 @@ from typing import TYPE_CHECKING, Any, Final, TypeVar, overload
20
20
  from streamlit import config, logger
21
21
  from streamlit.dataframe_util import OptionSequence, convert_anything_to_list
22
22
  from streamlit.errors import StreamlitAPIException
23
+ from streamlit.runtime.state import get_session_state
23
24
  from streamlit.runtime.state.common import RegisterWidgetResult
24
25
  from streamlit.type_util import (
25
26
  check_python_comparable,
@@ -282,3 +283,100 @@ def create_mappings(
282
283
  formatted_options,
283
284
  formatted_option_to_option_mapping,
284
285
  )
286
+
287
+
288
+ def validate_and_sync_value_with_options(
289
+ current_value: T | None,
290
+ opt: Sequence[T],
291
+ default_index: int | None,
292
+ key: str | int | None,
293
+ ) -> tuple[T | None, bool]:
294
+ """Validate current value against options, resetting session state if invalid.
295
+
296
+ This function has a side-effect: if the value is not found in the options
297
+ and a key is provided, it will update session state with the new value.
298
+
299
+ Parameters
300
+ ----------
301
+ current_value
302
+ The current widget value to validate.
303
+ opt
304
+ The sequence of valid options.
305
+ default_index
306
+ The default index to reset to if value is invalid.
307
+ key
308
+ The widget key for session state updates.
309
+
310
+ Returns
311
+ -------
312
+ tuple[T | None, bool]
313
+ A tuple of (validated_value, value_was_reset).
314
+ """
315
+ if current_value is None:
316
+ return current_value, False
317
+
318
+ # Check if current value is still in the new options
319
+ try:
320
+ index_(opt, current_value)
321
+ return current_value, False
322
+ except ValueError:
323
+ # Value not in options - reset to default
324
+ if default_index is not None and len(opt) > 0:
325
+ new_value: T | None = opt[default_index]
326
+ else:
327
+ new_value = None
328
+
329
+ if key is not None:
330
+ # Update session_state so subsequent accesses in this run
331
+ # return the corrected value. Use reset_state_value to avoid
332
+ # the "cannot be modified after widget instantiated" error.
333
+ get_session_state().reset_state_value(str(key), new_value)
334
+
335
+ return new_value, True
336
+
337
+
338
+ def validate_and_sync_multiselect_value_with_options(
339
+ current_values: list[T] | list[T | str],
340
+ opt: Sequence[T],
341
+ key: str | int | None,
342
+ ) -> tuple[list[T] | list[T | str], bool]:
343
+ """Validate multiselect values against options, syncing session state if needed.
344
+
345
+ This function has a side-effect: if any values are filtered out and a key
346
+ is provided, it will update session state with the filtered list.
347
+
348
+ Unlike selectbox which resets to a default when the value is invalid,
349
+ multiselect filters out invalid values and keeps the valid ones.
350
+
351
+ Parameters
352
+ ----------
353
+ current_values
354
+ The current list of selected values to validate.
355
+ opt
356
+ The sequence of valid options.
357
+ key
358
+ The widget key for session state updates.
359
+
360
+ Returns
361
+ -------
362
+ tuple[list[T] | list[T | str], bool]
363
+ A tuple of (validated_values, values_were_filtered).
364
+ """
365
+ if not current_values:
366
+ return current_values, False
367
+
368
+ valid_values: list[T | str] = []
369
+ for value in current_values:
370
+ try:
371
+ index_(opt, value)
372
+ valid_values.append(value)
373
+ except ValueError: # noqa: PERF203
374
+ pass
375
+
376
+ if len(valid_values) == len(current_values):
377
+ return current_values, False
378
+
379
+ if key is not None:
380
+ get_session_state().reset_state_value(str(key), valid_values)
381
+
382
+ return valid_values, True
@@ -38,6 +38,7 @@ from streamlit.elements.lib.options_selector_utils import (
38
38
  create_mappings,
39
39
  get_default_indices,
40
40
  maybe_coerce_enum_sequence,
41
+ validate_and_sync_multiselect_value_with_options,
41
42
  )
42
43
  from streamlit.elements.lib.policies import (
43
44
  check_widget_policies,
@@ -493,15 +494,9 @@ class MultiSelectMixin:
493
494
  element_id = compute_and_register_element_id(
494
495
  widget_name,
495
496
  user_key=key,
496
- # Treat the provided key as the main identity. Only include
497
- # changes to the options, accept_new_options, and max_selections
498
- # in the identity computation as those can invalidate the
499
- # current selection.
500
497
  key_as_main_identity={
501
- "options",
502
498
  "max_selections",
503
499
  "accept_new_options",
504
- "format_func",
505
500
  },
506
501
  dg=self.dg,
507
502
  label=label,
@@ -554,8 +549,23 @@ class MultiSelectMixin:
554
549
  widget_state, options, indexable_options
555
550
  )
556
551
 
557
- if widget_state.value_changed:
558
- proto.raw_values[:] = serde.serialize(widget_state.value)
552
+ if accept_new_options:
553
+ # accept_new_options is True, so we keep the user-entered values.
554
+ current_values = widget_state.value
555
+ value_needs_reset = False
556
+ else:
557
+ # Validate the current values against the new options.
558
+ # If values are no longer valid (not in options), filter them out.
559
+ # This handles the case where options change dynamically and the
560
+ # previously selected values are no longer available.
561
+ current_values, value_needs_reset = (
562
+ validate_and_sync_multiselect_value_with_options(
563
+ widget_state.value, indexable_options, key
564
+ )
565
+ )
566
+
567
+ if value_needs_reset or widget_state.value_changed:
568
+ proto.raw_values[:] = serde.serialize(current_values)
559
569
  proto.set_value = True
560
570
 
561
571
  validate_width(width)
@@ -566,7 +576,7 @@ class MultiSelectMixin:
566
576
 
567
577
  self.dg._enqueue(widget_name, proto, layout_config=layout_config)
568
578
 
569
- return widget_state.value
579
+ return current_values
570
580
 
571
581
  @property
572
582
  def dg(self) -> DeltaGenerator:
@@ -37,6 +37,7 @@ from streamlit.elements.lib.options_selector_utils import (
37
37
  create_mappings,
38
38
  index_,
39
39
  maybe_coerce_enum,
40
+ validate_and_sync_value_with_options,
40
41
  )
41
42
  from streamlit.elements.lib.policies import (
42
43
  check_widget_policies,
@@ -595,30 +596,17 @@ class SelectboxMixin:
595
596
  )
596
597
  widget_state = maybe_coerce_enum(widget_state, options, opt)
597
598
 
598
- # Validate the current value against the new options.
599
- # If the value is no longer valid (not in options), reset to default.
600
- # This handles the case where options change dynamically and the
601
- # previously selected value is no longer available.
602
- current_value = widget_state.value
603
- value_needs_reset = False
604
-
605
- if current_value is not None and not accept_new_options:
606
- # Check if current value is still in the new options
607
- try:
608
- index_(opt, current_value)
609
- except ValueError:
610
- # Value not in options - reset to default.
611
- value_needs_reset = True
612
- if index is not None and len(opt) > 0:
613
- current_value = opt[index]
614
- else:
615
- current_value = None
616
-
617
- # Update session_state so subsequent accesses in this run
618
- # return the corrected value. Use reset_state_value to avoid
619
- # the "cannot be modified after widget instantiated" error.
620
- if key is not None:
621
- get_session_state().reset_state_value(key, current_value)
599
+ if accept_new_options:
600
+ current_value = widget_state.value
601
+ value_needs_reset = False
602
+ else:
603
+ # Validate the current value against the new options.
604
+ # If the value is no longer valid (not in options), reset to default.
605
+ # This handles the case where options change dynamically and the
606
+ # previously selected value is no longer available.
607
+ current_value, value_needs_reset = validate_and_sync_value_with_options(
608
+ widget_state.value, opt, index, key
609
+ )
622
610
 
623
611
  if value_needs_reset or widget_state.value_changed:
624
612
  serialized_value = serde.serialize(current_value)
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
15
15
  from streamlit.proto import PageProfile_pb2 as streamlit_dot_proto_dot_PageProfile__pb2
16
16
 
17
17
 
18
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"streamlit/proto/MetricsEvent.proto\x1a!streamlit/proto/PageProfile.proto\"\xbf\x07\n\x0cMetricsEvent\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x14\n\x0c\x61nonymous_id\x18\x02 \x01(\t\x12\x15\n\rmachine_id_v3\x18\x03 \x01(\t\x12\x15\n\rmachine_id_v4\x18) \x01(\t\x12\x13\n\x0breport_hash\x18\x04 \x01(\t\x12\x0b\n\x03\x64\x65v\x18\x05 \x01(\x08\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x19\n\x11streamlit_version\x18\x07 \x01(\t\x12\x10\n\x08is_hello\x18\x08 \x01(\x08\x12\x0e\n\x06\x61pp_id\x18! \x01(\t\x12\x12\n\nsession_id\x18# \x01(\t\x12\x16\n\x0epython_version\x18$ \x01(\t\x12\x11\n\tserver_os\x18* \x01(\t\x12\x13\n\x0bhas_display\x18+ \x01(\x08\x12\x14\n\x0cis_webdriver\x18, \x01(\x08\x12\x11\n\thosted_at\x18\t \x01(\t\x12\r\n\x05owner\x18\n \x01(\t\x12\x0c\n\x04repo\x18\x0b \x01(\t\x12\x0e\n\x06\x62ranch\x18\x0c \x01(\t\x12\x13\n\x0bmain_module\x18\r \x01(\t\x12\x12\n\ncreator_id\x18\x0e \x01(\t\x12\x18\n\x10\x63ontext_page_url\x18\x0f \x01(\t\x12\x1a\n\x12\x63ontext_page_title\x18\x10 \x01(\t\x12\x19\n\x11\x63ontext_page_path\x18\x11 \x01(\t\x12\x1d\n\x15\x63ontext_page_referrer\x18\x12 \x01(\t\x12\x1b\n\x13\x63ontext_page_search\x18\x13 \x01(\t\x12\x16\n\x0e\x63ontext_locale\x18\x14 \x01(\t\x12\x1a\n\x12\x63ontext_user_agent\x18\x15 \x01(\t\x12\r\n\x05label\x18\x16 \x01(\t\x12\x1a\n\x08\x63ommands\x18\x17 \x03(\x0b\x32\x08.Command\x12\x11\n\texec_time\x18\x18 \x01(\x03\x12\x11\n\tprep_time\x18\x19 \x01(\x03\x12\x0e\n\x06\x63onfig\x18\x1a \x03(\t\x12\x1a\n\x12uncaught_exception\x18\x1b \x01(\t\x12\x14\n\x0c\x61ttributions\x18\x1c \x03(\t\x12\n\n\x02os\x18\x1d \x01(\t\x12\x10\n\x08timezone\x18\x1e \x01(\t\x12\x10\n\x08headless\x18\x1f \x01(\x08\x12\x17\n\x0fis_fragment_run\x18 \x01(\x08\x12\x10\n\x08numPages\x18\" \x01(\x03\x12\x18\n\x10page_script_hash\x18% \x01(\t\x12\x14\n\x0c\x61\x63tive_theme\x18& \x01(\t\x12\x17\n\x0ftotal_load_time\x18\' \x01(\x03\x12\"\n\x0c\x62rowser_info\x18( \x01(\x0b\x32\x0c.BrowserInfo\"]\n\x0b\x42rowserInfo\x12\x14\n\x0c\x62rowser_name\x18\x01 \x01(\t\x12\x17\n\x0f\x62rowser_version\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65vice_type\x18\x03 \x01(\t\x12\n\n\x02os\x18\x04 \x01(\tB1\n\x1c\x63om.snowflake.apps.streamlitB\x11MetricsEventProtob\x06proto3')
18
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\"streamlit/proto/MetricsEvent.proto\x1a!streamlit/proto/PageProfile.proto\"\xd4\x07\n\x0cMetricsEvent\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x14\n\x0c\x61nonymous_id\x18\x02 \x01(\t\x12\x15\n\rmachine_id_v3\x18\x03 \x01(\t\x12\x15\n\rmachine_id_v4\x18) \x01(\t\x12\x13\n\x0breport_hash\x18\x04 \x01(\t\x12\x0b\n\x03\x64\x65v\x18\x05 \x01(\x08\x12\x0e\n\x06source\x18\x06 \x01(\t\x12\x19\n\x11streamlit_version\x18\x07 \x01(\t\x12\x10\n\x08is_hello\x18\x08 \x01(\x08\x12\x0e\n\x06\x61pp_id\x18! \x01(\t\x12\x12\n\nsession_id\x18# \x01(\t\x12\x16\n\x0epython_version\x18$ \x01(\t\x12\x11\n\tserver_os\x18* \x01(\t\x12\x13\n\x0bhas_display\x18+ \x01(\x08\x12\x14\n\x0cis_webdriver\x18, \x01(\x08\x12\x11\n\thosted_at\x18\t \x01(\t\x12\r\n\x05owner\x18\n \x01(\t\x12\x0c\n\x04repo\x18\x0b \x01(\t\x12\x0e\n\x06\x62ranch\x18\x0c \x01(\t\x12\x13\n\x0bmain_module\x18\r \x01(\t\x12\x12\n\ncreator_id\x18\x0e \x01(\t\x12\x18\n\x10\x63ontext_page_url\x18\x0f \x01(\t\x12\x1a\n\x12\x63ontext_page_title\x18\x10 \x01(\t\x12\x19\n\x11\x63ontext_page_path\x18\x11 \x01(\t\x12\x1d\n\x15\x63ontext_page_referrer\x18\x12 \x01(\t\x12\x1b\n\x13\x63ontext_page_search\x18\x13 \x01(\t\x12\x16\n\x0e\x63ontext_locale\x18\x14 \x01(\t\x12\x1a\n\x12\x63ontext_user_agent\x18\x15 \x01(\t\x12\r\n\x05label\x18\x16 \x01(\t\x12\x1a\n\x08\x63ommands\x18\x17 \x03(\x0b\x32\x08.Command\x12\x11\n\texec_time\x18\x18 \x01(\x03\x12\x11\n\tprep_time\x18\x19 \x01(\x03\x12\x0e\n\x06\x63onfig\x18\x1a \x03(\t\x12\x1a\n\x12uncaught_exception\x18\x1b \x01(\t\x12\x14\n\x0c\x61ttributions\x18\x1c \x03(\t\x12\n\n\x02os\x18\x1d \x01(\t\x12\x10\n\x08timezone\x18\x1e \x01(\t\x12\x10\n\x08headless\x18\x1f \x01(\x08\x12\x17\n\x0fis_fragment_run\x18 \x01(\x08\x12\x10\n\x08numPages\x18\" \x01(\x03\x12\x18\n\x10page_script_hash\x18% \x01(\t\x12\x14\n\x0c\x61\x63tive_theme\x18& \x01(\t\x12\x17\n\x0ftotal_load_time\x18\' \x01(\x03\x12\"\n\x0c\x62rowser_info\x18( \x01(\x0b\x32\x0c.BrowserInfo\x12\x13\n\x0bserver_mode\x18- \x01(\t\"]\n\x0b\x42rowserInfo\x12\x14\n\x0c\x62rowser_name\x18\x01 \x01(\t\x12\x17\n\x0f\x62rowser_version\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65vice_type\x18\x03 \x01(\t\x12\n\n\x02os\x18\x04 \x01(\tB1\n\x1c\x63om.snowflake.apps.streamlitB\x11MetricsEventProtob\x06proto3')
19
19
 
20
20
  _globals = globals()
21
21
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -24,7 +24,7 @@ if not _descriptor._USE_C_DESCRIPTORS:
24
24
  _globals['DESCRIPTOR']._loaded_options = None
25
25
  _globals['DESCRIPTOR']._serialized_options = b'\n\034com.snowflake.apps.streamlitB\021MetricsEventProto'
26
26
  _globals['_METRICSEVENT']._serialized_start=74
27
- _globals['_METRICSEVENT']._serialized_end=1033
28
- _globals['_BROWSERINFO']._serialized_start=1035
29
- _globals['_BROWSERINFO']._serialized_end=1128
27
+ _globals['_METRICSEVENT']._serialized_end=1054
28
+ _globals['_BROWSERINFO']._serialized_start=1056
29
+ _globals['_BROWSERINFO']._serialized_end=1149
30
30
  # @@protoc_insertion_point(module_scope)
@@ -83,6 +83,7 @@ class MetricsEvent(google.protobuf.message.Message):
83
83
  ACTIVE_THEME_FIELD_NUMBER: builtins.int
84
84
  TOTAL_LOAD_TIME_FIELD_NUMBER: builtins.int
85
85
  BROWSER_INFO_FIELD_NUMBER: builtins.int
86
+ SERVER_MODE_FIELD_NUMBER: builtins.int
86
87
  event: builtins.str
87
88
  """Common Event Fields:"""
88
89
  anonymous_id: builtins.str
@@ -128,6 +129,7 @@ class MetricsEvent(google.protobuf.message.Message):
128
129
  page_script_hash: builtins.str
129
130
  active_theme: builtins.str
130
131
  total_load_time: builtins.int
132
+ server_mode: builtins.str
131
133
  @property
132
134
  def commands(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[streamlit.proto.PageProfile_pb2.Command]:
133
135
  """Page Profile Event fields:
@@ -187,10 +189,11 @@ class MetricsEvent(google.protobuf.message.Message):
187
189
  active_theme: builtins.str = ...,
188
190
  total_load_time: builtins.int = ...,
189
191
  browser_info: Global___BrowserInfo | None = ...,
192
+ server_mode: builtins.str = ...,
190
193
  ) -> None: ...
191
194
  _HasFieldArgType: typing_extensions.TypeAlias = typing.Literal["browser_info", b"browser_info"]
192
195
  def HasField(self, field_name: _HasFieldArgType) -> builtins.bool: ...
193
- _ClearFieldArgType: typing_extensions.TypeAlias = typing.Literal["active_theme", b"active_theme", "anonymous_id", b"anonymous_id", "app_id", b"app_id", "attributions", b"attributions", "branch", b"branch", "browser_info", b"browser_info", "commands", b"commands", "config", b"config", "context_locale", b"context_locale", "context_page_path", b"context_page_path", "context_page_referrer", b"context_page_referrer", "context_page_search", b"context_page_search", "context_page_title", b"context_page_title", "context_page_url", b"context_page_url", "context_user_agent", b"context_user_agent", "creator_id", b"creator_id", "dev", b"dev", "event", b"event", "exec_time", b"exec_time", "has_display", b"has_display", "headless", b"headless", "hosted_at", b"hosted_at", "is_fragment_run", b"is_fragment_run", "is_hello", b"is_hello", "is_webdriver", b"is_webdriver", "label", b"label", "machine_id_v3", b"machine_id_v3", "machine_id_v4", b"machine_id_v4", "main_module", b"main_module", "numPages", b"numPages", "os", b"os", "owner", b"owner", "page_script_hash", b"page_script_hash", "prep_time", b"prep_time", "python_version", b"python_version", "repo", b"repo", "report_hash", b"report_hash", "server_os", b"server_os", "session_id", b"session_id", "source", b"source", "streamlit_version", b"streamlit_version", "timezone", b"timezone", "total_load_time", b"total_load_time", "uncaught_exception", b"uncaught_exception"]
196
+ _ClearFieldArgType: typing_extensions.TypeAlias = typing.Literal["active_theme", b"active_theme", "anonymous_id", b"anonymous_id", "app_id", b"app_id", "attributions", b"attributions", "branch", b"branch", "browser_info", b"browser_info", "commands", b"commands", "config", b"config", "context_locale", b"context_locale", "context_page_path", b"context_page_path", "context_page_referrer", b"context_page_referrer", "context_page_search", b"context_page_search", "context_page_title", b"context_page_title", "context_page_url", b"context_page_url", "context_user_agent", b"context_user_agent", "creator_id", b"creator_id", "dev", b"dev", "event", b"event", "exec_time", b"exec_time", "has_display", b"has_display", "headless", b"headless", "hosted_at", b"hosted_at", "is_fragment_run", b"is_fragment_run", "is_hello", b"is_hello", "is_webdriver", b"is_webdriver", "label", b"label", "machine_id_v3", b"machine_id_v3", "machine_id_v4", b"machine_id_v4", "main_module", b"main_module", "numPages", b"numPages", "os", b"os", "owner", b"owner", "page_script_hash", b"page_script_hash", "prep_time", b"prep_time", "python_version", b"python_version", "repo", b"repo", "report_hash", b"report_hash", "server_mode", b"server_mode", "server_os", b"server_os", "session_id", b"session_id", "source", b"source", "streamlit_version", b"streamlit_version", "timezone", b"timezone", "total_load_time", b"total_load_time", "uncaught_exception", b"uncaught_exception"]
194
197
  def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
195
198
 
196
199
  Global___MetricsEvent: typing_extensions.TypeAlias = MetricsEvent
@@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
14
14
 
15
15
 
16
16
 
17
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/PageProfile.proto\"\xda\x01\n\x0bPageProfile\x12\x1a\n\x08\x63ommands\x18\x01 \x03(\x0b\x32\x08.Command\x12\x11\n\texec_time\x18\x02 \x01(\x03\x12\x11\n\tprep_time\x18\x03 \x01(\x03\x12\x0e\n\x06\x63onfig\x18\x05 \x03(\t\x12\x1a\n\x12uncaught_exception\x18\x06 \x01(\t\x12\x14\n\x0c\x61ttributions\x18\x07 \x03(\t\x12\n\n\x02os\x18\x08 \x01(\t\x12\x10\n\x08timezone\x18\t \x01(\t\x12\x10\n\x08headless\x18\n \x01(\x08\x12\x17\n\x0fis_fragment_run\x18\x0b \x01(\x08\"6\n\x08\x41rgument\x12\t\n\x01k\x18\x01 \x01(\t\x12\t\n\x01t\x18\x02 \x01(\t\x12\t\n\x01m\x18\x03 \x01(\t\x12\t\n\x01p\x18\x05 \x01(\x05\">\n\x07\x43ommand\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\t.Argument\x12\x0c\n\x04time\x18\x04 \x01(\x03\x42\x30\n\x1c\x63om.snowflake.apps.streamlitB\x10PageProfileProtob\x06proto3')
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/PageProfile.proto\"\xef\x01\n\x0bPageProfile\x12\x1a\n\x08\x63ommands\x18\x01 \x03(\x0b\x32\x08.Command\x12\x11\n\texec_time\x18\x02 \x01(\x03\x12\x11\n\tprep_time\x18\x03 \x01(\x03\x12\x0e\n\x06\x63onfig\x18\x05 \x03(\t\x12\x1a\n\x12uncaught_exception\x18\x06 \x01(\t\x12\x14\n\x0c\x61ttributions\x18\x07 \x03(\t\x12\n\n\x02os\x18\x08 \x01(\t\x12\x10\n\x08timezone\x18\t \x01(\t\x12\x10\n\x08headless\x18\n \x01(\x08\x12\x17\n\x0fis_fragment_run\x18\x0b \x01(\x08\x12\x13\n\x0bserver_mode\x18\x0c \x01(\t\"6\n\x08\x41rgument\x12\t\n\x01k\x18\x01 \x01(\t\x12\t\n\x01t\x18\x02 \x01(\t\x12\t\n\x01m\x18\x03 \x01(\t\x12\t\n\x01p\x18\x05 \x01(\x05\">\n\x07\x43ommand\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\t.Argument\x12\x0c\n\x04time\x18\x04 \x01(\x03\x42\x30\n\x1c\x63om.snowflake.apps.streamlitB\x10PageProfileProtob\x06proto3')
18
18
 
19
19
  _globals = globals()
20
20
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -23,9 +23,9 @@ if not _descriptor._USE_C_DESCRIPTORS:
23
23
  _globals['DESCRIPTOR']._loaded_options = None
24
24
  _globals['DESCRIPTOR']._serialized_options = b'\n\034com.snowflake.apps.streamlitB\020PageProfileProto'
25
25
  _globals['_PAGEPROFILE']._serialized_start=38
26
- _globals['_PAGEPROFILE']._serialized_end=256
27
- _globals['_ARGUMENT']._serialized_start=258
28
- _globals['_ARGUMENT']._serialized_end=312
29
- _globals['_COMMAND']._serialized_start=314
30
- _globals['_COMMAND']._serialized_end=376
26
+ _globals['_PAGEPROFILE']._serialized_end=277
27
+ _globals['_ARGUMENT']._serialized_start=279
28
+ _globals['_ARGUMENT']._serialized_end=333
29
+ _globals['_COMMAND']._serialized_start=335
30
+ _globals['_COMMAND']._serialized_end=397
31
31
  # @@protoc_insertion_point(module_scope)
@@ -46,6 +46,7 @@ class PageProfile(google.protobuf.message.Message):
46
46
  TIMEZONE_FIELD_NUMBER: builtins.int
47
47
  HEADLESS_FIELD_NUMBER: builtins.int
48
48
  IS_FRAGMENT_RUN_FIELD_NUMBER: builtins.int
49
+ SERVER_MODE_FIELD_NUMBER: builtins.int
49
50
  exec_time: builtins.int
50
51
  prep_time: builtins.int
51
52
  uncaught_exception: builtins.str
@@ -53,6 +54,14 @@ class PageProfile(google.protobuf.message.Message):
53
54
  timezone: builtins.str
54
55
  headless: builtins.bool
55
56
  is_fragment_run: builtins.bool
57
+ server_mode: builtins.str
58
+ """The server mode used to run the Streamlit app:
59
+ - "tornado": Traditional Tornado server (streamlit run)
60
+ - "starlette-managed": Starlette server via server.useStarlette config
61
+ - "starlette-app": st.App started via streamlit run
62
+ - "asgi-server": st.App run directly with external ASGI server (uvicorn, gunicorn)
63
+ - "asgi-mounted": st.App mounted on another ASGI framework (FastAPI, Starlette)
64
+ """
56
65
  @property
57
66
  def commands(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[Global___Command]: ...
58
67
  @property
@@ -72,8 +81,9 @@ class PageProfile(google.protobuf.message.Message):
72
81
  timezone: builtins.str = ...,
73
82
  headless: builtins.bool = ...,
74
83
  is_fragment_run: builtins.bool = ...,
84
+ server_mode: builtins.str = ...,
75
85
  ) -> None: ...
76
- _ClearFieldArgType: typing_extensions.TypeAlias = typing.Literal["attributions", b"attributions", "commands", b"commands", "config", b"config", "exec_time", b"exec_time", "headless", b"headless", "is_fragment_run", b"is_fragment_run", "os", b"os", "prep_time", b"prep_time", "timezone", b"timezone", "uncaught_exception", b"uncaught_exception"]
86
+ _ClearFieldArgType: typing_extensions.TypeAlias = typing.Literal["attributions", b"attributions", "commands", b"commands", "config", b"config", "exec_time", b"exec_time", "headless", b"headless", "is_fragment_run", b"is_fragment_run", "os", b"os", "prep_time", b"prep_time", "server_mode", b"server_mode", "timezone", b"timezone", "uncaught_exception", b"uncaught_exception"]
77
87
  def ClearField(self, field_name: _ClearFieldArgType) -> None: ...
78
88
 
79
89
  Global___PageProfile: typing_extensions.TypeAlias = PageProfile
@@ -573,6 +573,10 @@ def create_page_profile_message(
573
573
 
574
574
  page_profile.headless = config.get_option("server.headless")
575
575
 
576
+ # Include the server mode for metrics tracking
577
+ if config._server_mode:
578
+ page_profile.server_mode = config._server_mode
579
+
576
580
  # Collect all config options that have been manually set
577
581
  config_options: set[str] = set()
578
582
  if config._config_options:
@@ -232,6 +232,9 @@ class Runtime:
232
232
  self._stats_mgr.register_provider(get_resource_cache_stats_provider())
233
233
  if self._uploaded_file_mgr is not None:
234
234
  self._stats_mgr.register_provider(self._uploaded_file_mgr)
235
+ # Register media file storage for stats if it implements StatsProvider
236
+ if isinstance(config.media_file_storage, StatsProvider):
237
+ self._stats_mgr.register_provider(config.media_file_storage)
235
238
  self._stats_mgr.register_provider(SessionStateStatProvider(self._session_mgr))
236
239
 
237
240
  # Register session manager for session event metrics if it implements StatsProvider
streamlit/starlette.py ADDED
@@ -0,0 +1,34 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
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
+ """Starlette integration for Streamlit.
16
+
17
+ This module provides the ASGI-compatible App class for running Streamlit
18
+ applications with any ASGI server (uvicorn, hypercorn, etc.).
19
+
20
+ Example
21
+ -------
22
+ >>> from streamlit.starlette import App
23
+ >>> app = App("main.py")
24
+
25
+ Run with uvicorn:
26
+
27
+ .. code-block:: bash
28
+
29
+ uvicorn myapp:app --host 0.0.0.0 --port 8501
30
+ """
31
+
32
+ from streamlit.web.server.starlette.starlette_app import App
33
+
34
+ __all__ = ["App"]
@@ -37,7 +37,7 @@
37
37
  <script>
38
38
  window.prerenderReady = false
39
39
  </script>
40
- <script type="module" crossorigin src="./static/js/index.DkSjHoXw.js"></script>
40
+ <script type="module" crossorigin src="./static/js/index.BZBWLU1C.js"></script>
41
41
  <link rel="stylesheet" crossorigin href="./static/css/index.BUP6fTcR.css">
42
42
  </head>
43
43
  <body>