streamlit-nightly 1.32.3.dev20240326__py2.py3-none-any.whl → 1.32.3.dev20240328__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.
Files changed (80) hide show
  1. streamlit/__init__.py +4 -2
  2. streamlit/components/v1/__init__.py +3 -17
  3. streamlit/components/v1/{custom_component.py → components.py} +159 -11
  4. streamlit/delta_generator.py +3 -0
  5. streamlit/elements/widgets/time_widgets.py +5 -21
  6. streamlit/errors.py +0 -6
  7. streamlit/proto/AutoRerun_pb2.py +25 -0
  8. streamlit/proto/AutoRerun_pb2.pyi +48 -0
  9. streamlit/proto/ClientState_pb2.py +3 -3
  10. streamlit/proto/ClientState_pb2.pyi +4 -1
  11. streamlit/proto/Delta_pb2.py +2 -2
  12. streamlit/proto/Delta_pb2.pyi +4 -1
  13. streamlit/proto/ForwardMsg_pb2.py +10 -9
  14. streamlit/proto/ForwardMsg_pb2.pyi +12 -3
  15. streamlit/proto/NewSession_pb2.py +24 -24
  16. streamlit/proto/NewSession_pb2.pyi +8 -1
  17. streamlit/proto/PageProfile_pb2.py +6 -6
  18. streamlit/proto/PageProfile_pb2.pyi +4 -1
  19. streamlit/runtime/app_session.py +67 -24
  20. streamlit/runtime/caching/cache_data_api.py +3 -3
  21. streamlit/runtime/caching/cache_resource_api.py +2 -2
  22. streamlit/runtime/fragment.py +239 -0
  23. streamlit/runtime/metrics_util.py +17 -9
  24. streamlit/runtime/runtime.py +6 -12
  25. streamlit/runtime/scriptrunner/script_requests.py +53 -37
  26. streamlit/runtime/scriptrunner/script_run_context.py +15 -2
  27. streamlit/runtime/scriptrunner/script_runner.py +63 -14
  28. streamlit/runtime/state/common.py +2 -0
  29. streamlit/runtime/state/session_state.py +51 -7
  30. streamlit/runtime/state/widgets.py +10 -2
  31. streamlit/static/asset-manifest.json +19 -19
  32. streamlit/static/index.html +1 -1
  33. streamlit/static/static/js/1074.73973756.chunk.js +1 -0
  34. streamlit/static/static/js/1451.3b0a3e31.chunk.js +1 -0
  35. streamlit/static/static/js/1792.b8efa879.chunk.js +1 -0
  36. streamlit/static/static/js/{3092.3d4df25e.chunk.js → 3092.ad569cc8.chunk.js} +1 -1
  37. streamlit/static/static/js/3513.e3e7300a.chunk.js +1 -0
  38. streamlit/static/static/js/4177.69f9f18d.chunk.js +1 -0
  39. streamlit/static/static/js/4319.a6745434.chunk.js +1 -0
  40. streamlit/static/static/js/{4477.2555c11a.chunk.js → 4477.e10e4373.chunk.js} +1 -1
  41. streamlit/static/static/js/{4666.99f3abc3.chunk.js → 4666.b694c5a9.chunk.js} +1 -1
  42. streamlit/static/static/js/5106.44f0ff51.chunk.js +1 -0
  43. streamlit/static/static/js/5379.6571574f.chunk.js +1 -0
  44. streamlit/static/static/js/6013.8e80e091.chunk.js +1 -0
  45. streamlit/static/static/js/6718.802da17e.chunk.js +1 -0
  46. streamlit/static/static/js/7175.be4076bc.chunk.js +1 -0
  47. streamlit/static/static/js/{7602.f0420392.chunk.js → 7602.6175e969.chunk.js} +1 -1
  48. streamlit/static/static/js/{8492.e6dab83f.chunk.js → 8492.f56c9d4c.chunk.js} +1 -1
  49. streamlit/static/static/js/8691.9ccf7f89.chunk.js +1 -0
  50. streamlit/static/static/js/main.722453f0.js +2 -0
  51. streamlit/testing/v1/local_script_runner.py +2 -0
  52. streamlit/time_util.py +88 -0
  53. streamlit/web/server/component_request_handler.py +2 -2
  54. streamlit/web/server/server.py +2 -1
  55. {streamlit_nightly-1.32.3.dev20240326.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/METADATA +1 -1
  56. {streamlit_nightly-1.32.3.dev20240326.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/RECORD +61 -63
  57. streamlit/components/lib/__init__.py +0 -13
  58. streamlit/components/lib/local_component_registry.py +0 -82
  59. streamlit/components/types/__init__.py +0 -13
  60. streamlit/components/types/base_component_registry.py +0 -98
  61. streamlit/components/types/base_custom_component.py +0 -137
  62. streamlit/components/v1/component_registry.py +0 -103
  63. streamlit/static/static/js/1074.71719df6.chunk.js +0 -1
  64. streamlit/static/static/js/1451.e3be1711.chunk.js +0 -1
  65. streamlit/static/static/js/1792.16c16498.chunk.js +0 -1
  66. streamlit/static/static/js/3513.57cff89c.chunk.js +0 -1
  67. streamlit/static/static/js/4177.ab9a7aa1.chunk.js +0 -1
  68. streamlit/static/static/js/4319.213fc321.chunk.js +0 -1
  69. streamlit/static/static/js/5106.22187bfc.chunk.js +0 -1
  70. streamlit/static/static/js/5379.e466522d.chunk.js +0 -1
  71. streamlit/static/static/js/6013.75c92264.chunk.js +0 -1
  72. streamlit/static/static/js/6718.97945fc6.chunk.js +0 -1
  73. streamlit/static/static/js/7175.8c1b4d38.chunk.js +0 -1
  74. streamlit/static/static/js/8691.24a5792f.chunk.js +0 -1
  75. streamlit/static/static/js/main.7fde7092.js +0 -2
  76. /streamlit/static/static/js/{main.7fde7092.js.LICENSE.txt → main.722453f0.js.LICENSE.txt} +0 -0
  77. {streamlit_nightly-1.32.3.dev20240326.data → streamlit_nightly-1.32.3.dev20240328.data}/scripts/streamlit.cmd +0 -0
  78. {streamlit_nightly-1.32.3.dev20240326.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/WHEEL +0 -0
  79. {streamlit_nightly-1.32.3.dev20240326.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/entry_points.txt +0 -0
  80. {streamlit_nightly-1.32.3.dev20240326.dist-info → streamlit_nightly-1.32.3.dev20240328.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,7 @@ from streamlit.proto import AppPage_pb2 as streamlit_dot_proto_dot_AppPage__pb2
15
15
  from streamlit.proto import SessionStatus_pb2 as streamlit_dot_proto_dot_SessionStatus__pb2
16
16
 
17
17
 
18
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n streamlit/proto/NewSession.proto\x1a\x1dstreamlit/proto/AppPage.proto\x1a#streamlit/proto/SessionStatus.proto\"\xec\x01\n\nNewSession\x12\x1f\n\ninitialize\x18\x01 \x01(\x0b\x32\x0b.Initialize\x12\x15\n\rscript_run_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x18\n\x10main_script_path\x18\x04 \x01(\t\x12\x17\n\x06\x63onfig\x18\x06 \x01(\x0b\x32\x07.Config\x12(\n\x0c\x63ustom_theme\x18\x07 \x01(\x0b\x32\x12.CustomThemeConfig\x12\x1b\n\tapp_pages\x18\x08 \x03(\x0b\x32\x08.AppPage\x12\x18\n\x10page_script_hash\x18\t \x01(\tJ\x04\x08\x05\x10\x06\"\xba\x01\n\nInitialize\x12\x1c\n\tuser_info\x18\x01 \x01(\x0b\x32\t.UserInfo\x12*\n\x10\x65nvironment_info\x18\x03 \x01(\x0b\x32\x10.EnvironmentInfo\x12&\n\x0esession_status\x18\x04 \x01(\x0b\x32\x0e.SessionStatus\x12\x14\n\x0c\x63ommand_line\x18\x05 \x01(\t\x12\x12\n\nsession_id\x18\x06 \x01(\t\x12\x10\n\x08is_hello\x18\x07 \x01(\x08\"\x97\x02\n\x06\x43onfig\x12\x1a\n\x12gather_usage_stats\x18\x02 \x01(\x08\x12\x1e\n\x16max_cached_message_age\x18\x03 \x01(\x05\x12\x14\n\x0cmapbox_token\x18\x04 \x01(\t\x12\x19\n\x11\x61llow_run_on_save\x18\x05 \x01(\x08\x12\x14\n\x0chide_top_bar\x18\x06 \x01(\x08\x12\x18\n\x10hide_sidebar_nav\x18\x07 \x01(\x08\x12)\n\x0ctoolbar_mode\x18\x08 \x01(\x0e\x32\x13.Config.ToolbarMode\"?\n\x0bToolbarMode\x12\x08\n\x04\x41UTO\x10\x00\x12\r\n\tDEVELOPER\x10\x01\x12\n\n\x06VIEWER\x10\x02\x12\x0b\n\x07MINIMAL\x10\x03J\x04\x08\x01\x10\x02\"\x8c\x04\n\x11\x43ustomThemeConfig\x12\x15\n\rprimary_color\x18\x01 \x01(\t\x12\"\n\x1asecondary_background_color\x18\x02 \x01(\t\x12\x18\n\x10\x62\x61\x63kground_color\x18\x03 \x01(\t\x12\x12\n\ntext_color\x18\x04 \x01(\t\x12+\n\x04\x66ont\x18\x05 \x01(\x0e\x32\x1d.CustomThemeConfig.FontFamily\x12*\n\x04\x62\x61se\x18\x06 \x01(\x0e\x32\x1c.CustomThemeConfig.BaseTheme\x12\x1f\n\x17widget_background_color\x18\x07 \x01(\t\x12\x1b\n\x13widget_border_color\x18\x08 \x01(\t\x12\x15\n\x05radii\x18\t \x01(\x0b\x32\x06.Radii\x12\x11\n\tbody_font\x18\r \x01(\t\x12\x11\n\tcode_font\x18\x0e \x01(\t\x12\x1d\n\nfont_faces\x18\x0f \x03(\x0b\x32\t.FontFace\x12\x1e\n\nfont_sizes\x18\x10 \x01(\x0b\x32\n.FontSizes\x12!\n\x19skeleton_background_color\x18\x11 \x01(\t\" \n\tBaseTheme\x12\t\n\x05LIGHT\x10\x00\x12\x08\n\x04\x44\x41RK\x10\x01\"6\n\nFontFamily\x12\x0e\n\nSANS_SERIF\x10\x00\x12\t\n\x05SERIF\x10\x01\x12\r\n\tMONOSPACE\x10\x02\"F\n\x08\x46ontFace\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0e\n\x06\x66\x61mily\x18\x02 \x01(\t\x12\x0e\n\x06weight\x18\x03 \x01(\x05\x12\r\n\x05style\x18\x04 \x01(\t\"<\n\x05Radii\x12\x1a\n\x12\x62\x61se_widget_radius\x18\x01 \x01(\x05\x12\x17\n\x0f\x63heckbox_radius\x18\x02 \x01(\x05\"T\n\tFontSizes\x12\x16\n\x0etiny_font_size\x18\x01 \x01(\x05\x12\x17\n\x0fsmall_font_size\x18\x02 \x01(\x05\x12\x16\n\x0e\x62\x61se_font_size\x18\x03 \x01(\x05\"E\n\x08UserInfo\x12\x17\n\x0finstallation_id\x18\x01 \x01(\t\x12\x1a\n\x12installation_id_v3\x18\x05 \x01(\tJ\x04\x08\x02\x10\x03\"D\n\x0f\x45nvironmentInfo\x12\x19\n\x11streamlit_version\x18\x01 \x01(\t\x12\x16\n\x0epython_version\x18\x02 \x01(\tB/\n\x1c\x63om.snowflake.apps.streamlitB\x0fNewSessionProtob\x06proto3')
18
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n streamlit/proto/NewSession.proto\x1a\x1dstreamlit/proto/AppPage.proto\x1a#streamlit/proto/SessionStatus.proto\"\x8b\x02\n\nNewSession\x12\x1f\n\ninitialize\x18\x01 \x01(\x0b\x32\x0b.Initialize\x12\x15\n\rscript_run_id\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x18\n\x10main_script_path\x18\x04 \x01(\t\x12\x17\n\x06\x63onfig\x18\x06 \x01(\x0b\x32\x07.Config\x12(\n\x0c\x63ustom_theme\x18\x07 \x01(\x0b\x32\x12.CustomThemeConfig\x12\x1b\n\tapp_pages\x18\x08 \x03(\x0b\x32\x08.AppPage\x12\x18\n\x10page_script_hash\x18\t \x01(\t\x12\x1d\n\x15\x66ragment_ids_this_run\x18\n \x03(\tJ\x04\x08\x05\x10\x06\"\xba\x01\n\nInitialize\x12\x1c\n\tuser_info\x18\x01 \x01(\x0b\x32\t.UserInfo\x12*\n\x10\x65nvironment_info\x18\x03 \x01(\x0b\x32\x10.EnvironmentInfo\x12&\n\x0esession_status\x18\x04 \x01(\x0b\x32\x0e.SessionStatus\x12\x14\n\x0c\x63ommand_line\x18\x05 \x01(\t\x12\x12\n\nsession_id\x18\x06 \x01(\t\x12\x10\n\x08is_hello\x18\x07 \x01(\x08\"\x97\x02\n\x06\x43onfig\x12\x1a\n\x12gather_usage_stats\x18\x02 \x01(\x08\x12\x1e\n\x16max_cached_message_age\x18\x03 \x01(\x05\x12\x14\n\x0cmapbox_token\x18\x04 \x01(\t\x12\x19\n\x11\x61llow_run_on_save\x18\x05 \x01(\x08\x12\x14\n\x0chide_top_bar\x18\x06 \x01(\x08\x12\x18\n\x10hide_sidebar_nav\x18\x07 \x01(\x08\x12)\n\x0ctoolbar_mode\x18\x08 \x01(\x0e\x32\x13.Config.ToolbarMode\"?\n\x0bToolbarMode\x12\x08\n\x04\x41UTO\x10\x00\x12\r\n\tDEVELOPER\x10\x01\x12\n\n\x06VIEWER\x10\x02\x12\x0b\n\x07MINIMAL\x10\x03J\x04\x08\x01\x10\x02\"\x8c\x04\n\x11\x43ustomThemeConfig\x12\x15\n\rprimary_color\x18\x01 \x01(\t\x12\"\n\x1asecondary_background_color\x18\x02 \x01(\t\x12\x18\n\x10\x62\x61\x63kground_color\x18\x03 \x01(\t\x12\x12\n\ntext_color\x18\x04 \x01(\t\x12+\n\x04\x66ont\x18\x05 \x01(\x0e\x32\x1d.CustomThemeConfig.FontFamily\x12*\n\x04\x62\x61se\x18\x06 \x01(\x0e\x32\x1c.CustomThemeConfig.BaseTheme\x12\x1f\n\x17widget_background_color\x18\x07 \x01(\t\x12\x1b\n\x13widget_border_color\x18\x08 \x01(\t\x12\x15\n\x05radii\x18\t \x01(\x0b\x32\x06.Radii\x12\x11\n\tbody_font\x18\r \x01(\t\x12\x11\n\tcode_font\x18\x0e \x01(\t\x12\x1d\n\nfont_faces\x18\x0f \x03(\x0b\x32\t.FontFace\x12\x1e\n\nfont_sizes\x18\x10 \x01(\x0b\x32\n.FontSizes\x12!\n\x19skeleton_background_color\x18\x11 \x01(\t\" \n\tBaseTheme\x12\t\n\x05LIGHT\x10\x00\x12\x08\n\x04\x44\x41RK\x10\x01\"6\n\nFontFamily\x12\x0e\n\nSANS_SERIF\x10\x00\x12\t\n\x05SERIF\x10\x01\x12\r\n\tMONOSPACE\x10\x02\"F\n\x08\x46ontFace\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x0e\n\x06\x66\x61mily\x18\x02 \x01(\t\x12\x0e\n\x06weight\x18\x03 \x01(\x05\x12\r\n\x05style\x18\x04 \x01(\t\"<\n\x05Radii\x12\x1a\n\x12\x62\x61se_widget_radius\x18\x01 \x01(\x05\x12\x17\n\x0f\x63heckbox_radius\x18\x02 \x01(\x05\"T\n\tFontSizes\x12\x16\n\x0etiny_font_size\x18\x01 \x01(\x05\x12\x17\n\x0fsmall_font_size\x18\x02 \x01(\x05\x12\x16\n\x0e\x62\x61se_font_size\x18\x03 \x01(\x05\"E\n\x08UserInfo\x12\x17\n\x0finstallation_id\x18\x01 \x01(\t\x12\x1a\n\x12installation_id_v3\x18\x05 \x01(\tJ\x04\x08\x02\x10\x03\"D\n\x0f\x45nvironmentInfo\x12\x19\n\x11streamlit_version\x18\x01 \x01(\t\x12\x16\n\x0epython_version\x18\x02 \x01(\tB/\n\x1c\x63om.snowflake.apps.streamlitB\x0fNewSessionProtob\x06proto3')
19
19
 
20
20
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
21
21
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.NewSession_pb2', globals())
@@ -24,27 +24,27 @@ if _descriptor._USE_C_DESCRIPTORS == False:
24
24
  DESCRIPTOR._options = None
25
25
  DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\017NewSessionProto'
26
26
  _NEWSESSION._serialized_start=105
27
- _NEWSESSION._serialized_end=341
28
- _INITIALIZE._serialized_start=344
29
- _INITIALIZE._serialized_end=530
30
- _CONFIG._serialized_start=533
31
- _CONFIG._serialized_end=812
32
- _CONFIG_TOOLBARMODE._serialized_start=743
33
- _CONFIG_TOOLBARMODE._serialized_end=806
34
- _CUSTOMTHEMECONFIG._serialized_start=815
35
- _CUSTOMTHEMECONFIG._serialized_end=1339
36
- _CUSTOMTHEMECONFIG_BASETHEME._serialized_start=1251
37
- _CUSTOMTHEMECONFIG_BASETHEME._serialized_end=1283
38
- _CUSTOMTHEMECONFIG_FONTFAMILY._serialized_start=1285
39
- _CUSTOMTHEMECONFIG_FONTFAMILY._serialized_end=1339
40
- _FONTFACE._serialized_start=1341
41
- _FONTFACE._serialized_end=1411
42
- _RADII._serialized_start=1413
43
- _RADII._serialized_end=1473
44
- _FONTSIZES._serialized_start=1475
45
- _FONTSIZES._serialized_end=1559
46
- _USERINFO._serialized_start=1561
47
- _USERINFO._serialized_end=1630
48
- _ENVIRONMENTINFO._serialized_start=1632
49
- _ENVIRONMENTINFO._serialized_end=1700
27
+ _NEWSESSION._serialized_end=372
28
+ _INITIALIZE._serialized_start=375
29
+ _INITIALIZE._serialized_end=561
30
+ _CONFIG._serialized_start=564
31
+ _CONFIG._serialized_end=843
32
+ _CONFIG_TOOLBARMODE._serialized_start=774
33
+ _CONFIG_TOOLBARMODE._serialized_end=837
34
+ _CUSTOMTHEMECONFIG._serialized_start=846
35
+ _CUSTOMTHEMECONFIG._serialized_end=1370
36
+ _CUSTOMTHEMECONFIG_BASETHEME._serialized_start=1282
37
+ _CUSTOMTHEMECONFIG_BASETHEME._serialized_end=1314
38
+ _CUSTOMTHEMECONFIG_FONTFAMILY._serialized_start=1316
39
+ _CUSTOMTHEMECONFIG_FONTFAMILY._serialized_end=1370
40
+ _FONTFACE._serialized_start=1372
41
+ _FONTFACE._serialized_end=1442
42
+ _RADII._serialized_start=1444
43
+ _RADII._serialized_end=1504
44
+ _FONTSIZES._serialized_start=1506
45
+ _FONTSIZES._serialized_end=1590
46
+ _USERINFO._serialized_start=1592
47
+ _USERINFO._serialized_end=1661
48
+ _ENVIRONMENTINFO._serialized_start=1663
49
+ _ENVIRONMENTINFO._serialized_end=1731
50
50
  # @@protoc_insertion_point(module_scope)
@@ -52,6 +52,7 @@ class NewSession(google.protobuf.message.Message):
52
52
  CUSTOM_THEME_FIELD_NUMBER: builtins.int
53
53
  APP_PAGES_FIELD_NUMBER: builtins.int
54
54
  PAGE_SCRIPT_HASH_FIELD_NUMBER: builtins.int
55
+ FRAGMENT_IDS_THIS_RUN_FIELD_NUMBER: builtins.int
55
56
  @property
56
57
  def initialize(self) -> global___Initialize:
57
58
  """Initialization data. This data does *not* change from rerun to rerun,
@@ -82,6 +83,11 @@ class NewSession(google.protobuf.message.Message):
82
83
  """A list of all of this app's pages, in order and including the main page."""
83
84
  page_script_hash: builtins.str
84
85
  """A hash of the script corresponding to the page currently being viewed."""
86
+ @property
87
+ def fragment_ids_this_run(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]:
88
+ """The fragment IDs being run in this session if it corresponds to a fragment
89
+ script run.
90
+ """
85
91
  def __init__(
86
92
  self,
87
93
  *,
@@ -93,9 +99,10 @@ class NewSession(google.protobuf.message.Message):
93
99
  custom_theme: global___CustomThemeConfig | None = ...,
94
100
  app_pages: collections.abc.Iterable[streamlit.proto.AppPage_pb2.AppPage] | None = ...,
95
101
  page_script_hash: builtins.str = ...,
102
+ fragment_ids_this_run: collections.abc.Iterable[builtins.str] | None = ...,
96
103
  ) -> None: ...
97
104
  def HasField(self, field_name: typing_extensions.Literal["config", b"config", "custom_theme", b"custom_theme", "initialize", b"initialize"]) -> builtins.bool: ...
98
- def ClearField(self, field_name: typing_extensions.Literal["app_pages", b"app_pages", "config", b"config", "custom_theme", b"custom_theme", "initialize", b"initialize", "main_script_path", b"main_script_path", "name", b"name", "page_script_hash", b"page_script_hash", "script_run_id", b"script_run_id"]) -> None: ...
105
+ def ClearField(self, field_name: typing_extensions.Literal["app_pages", b"app_pages", "config", b"config", "custom_theme", b"custom_theme", "fragment_ids_this_run", b"fragment_ids_this_run", "initialize", b"initialize", "main_script_path", b"main_script_path", "name", b"name", "page_script_hash", b"page_script_hash", "script_run_id", b"script_run_id"]) -> None: ...
99
106
 
100
107
  global___NewSession = NewSession
101
108
 
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
13
13
 
14
14
 
15
15
 
16
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!streamlit/proto/PageProfile.proto\"\xc1\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\"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')
16
+ 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
17
 
18
18
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
19
19
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'streamlit.proto.PageProfile_pb2', globals())
@@ -22,9 +22,9 @@ if _descriptor._USE_C_DESCRIPTORS == False:
22
22
  DESCRIPTOR._options = None
23
23
  DESCRIPTOR._serialized_options = b'\n\034com.snowflake.apps.streamlitB\020PageProfileProto'
24
24
  _PAGEPROFILE._serialized_start=38
25
- _PAGEPROFILE._serialized_end=231
26
- _ARGUMENT._serialized_start=233
27
- _ARGUMENT._serialized_end=287
28
- _COMMAND._serialized_start=289
29
- _COMMAND._serialized_end=351
25
+ _PAGEPROFILE._serialized_end=256
26
+ _ARGUMENT._serialized_start=258
27
+ _ARGUMENT._serialized_end=312
28
+ _COMMAND._serialized_start=314
29
+ _COMMAND._serialized_end=376
30
30
  # @@protoc_insertion_point(module_scope)
@@ -42,6 +42,7 @@ class PageProfile(google.protobuf.message.Message):
42
42
  OS_FIELD_NUMBER: builtins.int
43
43
  TIMEZONE_FIELD_NUMBER: builtins.int
44
44
  HEADLESS_FIELD_NUMBER: builtins.int
45
+ IS_FRAGMENT_RUN_FIELD_NUMBER: builtins.int
45
46
  @property
46
47
  def commands(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Command]: ...
47
48
  exec_time: builtins.int
@@ -54,6 +55,7 @@ class PageProfile(google.protobuf.message.Message):
54
55
  os: builtins.str
55
56
  timezone: builtins.str
56
57
  headless: builtins.bool
58
+ is_fragment_run: builtins.bool
57
59
  def __init__(
58
60
  self,
59
61
  *,
@@ -66,8 +68,9 @@ class PageProfile(google.protobuf.message.Message):
66
68
  os: builtins.str = ...,
67
69
  timezone: builtins.str = ...,
68
70
  headless: builtins.bool = ...,
71
+ is_fragment_run: builtins.bool = ...,
69
72
  ) -> None: ...
70
- def ClearField(self, field_name: typing_extensions.Literal["attributions", b"attributions", "commands", b"commands", "config", b"config", "exec_time", b"exec_time", "headless", b"headless", "os", b"os", "prep_time", b"prep_time", "timezone", b"timezone", "uncaught_exception", b"uncaught_exception"]) -> None: ...
73
+ def ClearField(self, field_name: typing_extensions.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"]) -> None: ...
71
74
 
72
75
  global___PageProfile = PageProfile
73
76
 
@@ -38,6 +38,7 @@ from streamlit.proto.NewSession_pb2 import (
38
38
  from streamlit.proto.PagesChanged_pb2 import PagesChanged
39
39
  from streamlit.runtime import caching, legacy_caching
40
40
  from streamlit.runtime.forward_msg_queue import ForwardMsgQueue
41
+ from streamlit.runtime.fragment import FragmentStorage, MemoryFragmentStorage
41
42
  from streamlit.runtime.metrics_util import Installation
42
43
  from streamlit.runtime.script_data import ScriptData
43
44
  from streamlit.runtime.scriptrunner import RerunData, ScriptRunner, ScriptRunnerEvent
@@ -161,6 +162,8 @@ class AppSession:
161
162
 
162
163
  self._debug_last_backmsg_id: str | None = None
163
164
 
165
+ self._fragment_storage: FragmentStorage = MemoryFragmentStorage()
166
+
164
167
  _LOGGER.debug("AppSession initialized (id=%s)", self.id)
165
168
 
166
169
  def __del__(self) -> None:
@@ -353,26 +356,33 @@ class AppSession:
353
356
  return
354
357
 
355
358
  if client_state:
359
+ fragment_id = client_state.fragment_id
360
+
356
361
  rerun_data = RerunData(
357
362
  client_state.query_string,
358
363
  client_state.widget_states,
359
364
  client_state.page_script_hash,
360
365
  client_state.page_name,
366
+ fragment_id_queue=[fragment_id] if fragment_id else [],
361
367
  )
362
368
  else:
363
369
  rerun_data = RerunData()
364
370
 
365
371
  if self._scriptrunner is not None:
366
- if bool(config.get_option("runner.fastReruns")):
367
- # If fastReruns is enabled, we don't send rerun requests to our
368
- # existing ScriptRunner. Instead, we tell it to shut down. We'll
369
- # then spin up a new ScriptRunner, below, to handle the rerun
370
- # immediately.
372
+ if (
373
+ bool(config.get_option("runner.fastReruns"))
374
+ and not rerun_data.fragment_id_queue
375
+ ):
376
+ # If fastReruns is enabled and this is *not* a rerun of a fragment,
377
+ # we don't send rerun requests to our existing ScriptRunner. Instead, we
378
+ # tell it to shut down. We'll then spin up a new ScriptRunner, below, to
379
+ # handle the rerun immediately.
371
380
  self._scriptrunner.request_stop()
372
381
  self._scriptrunner = None
373
382
  else:
374
- # fastReruns is not enabled. Send our ScriptRunner a rerun
375
- # request. If the request is accepted, we're done.
383
+ # Either fastReruns is not enabled or this RERUN request is a request to
384
+ # run a fragment. We send our current ScriptRunner a rerun request, and
385
+ # if it's accepted, we're done.
376
386
  success = self._scriptrunner.request_rerun(rerun_data)
377
387
  if success:
378
388
  return
@@ -400,6 +410,7 @@ class AppSession:
400
410
  script_cache=self._script_cache,
401
411
  initial_rerun_data=initial_rerun_data,
402
412
  user_info=self._user_info,
413
+ fragment_storage=self._fragment_storage,
403
414
  )
404
415
  self._scriptrunner.on_event.connect(self._on_scriptrunner_event)
405
416
  self._scriptrunner.start()
@@ -464,6 +475,7 @@ class AppSession:
464
475
  exception: BaseException | None = None,
465
476
  client_state: ClientState | None = None,
466
477
  page_script_hash: str | None = None,
478
+ fragment_ids_this_run: set[str] | None = None,
467
479
  ) -> None:
468
480
  """Called when our ScriptRunner emits an event.
469
481
 
@@ -473,7 +485,13 @@ class AppSession:
473
485
  """
474
486
  self._event_loop.call_soon_threadsafe(
475
487
  lambda: self._handle_scriptrunner_event_on_event_loop(
476
- sender, event, forward_msg, exception, client_state, page_script_hash
488
+ sender,
489
+ event,
490
+ forward_msg,
491
+ exception,
492
+ client_state,
493
+ page_script_hash,
494
+ fragment_ids_this_run,
477
495
  )
478
496
  )
479
497
 
@@ -485,6 +503,7 @@ class AppSession:
485
503
  exception: BaseException | None = None,
486
504
  client_state: ClientState | None = None,
487
505
  page_script_hash: str | None = None,
506
+ fragment_ids_this_run: set[str] | None = None,
488
507
  ) -> None:
489
508
  """Handle a ScriptRunner event.
490
509
 
@@ -515,6 +534,11 @@ class AppSession:
515
534
  page_script_hash : str | None
516
535
  A hash of the script path corresponding to the page currently being
517
536
  run. Set only for the SCRIPT_STARTED event.
537
+
538
+ fragment_ids_this_run : set[str] | None
539
+ The fragment IDs of the fragments being executed in this script run. Only
540
+ set for the SCRIPT_STARTED event. If this value is falsy, this script run
541
+ must be for the full script.
518
542
  """
519
543
 
520
544
  assert (
@@ -540,30 +564,43 @@ class AppSession:
540
564
  page_script_hash is not None
541
565
  ), "page_script_hash must be set for the SCRIPT_STARTED event"
542
566
 
543
- self._clear_queue()
567
+ # When running the full script, we clear the browser ForwardMsg queue since
568
+ # anything from a previous script run that has yet to be sent to the browser
569
+ # will be overwritten. For fragment runs, however, we don't want to do this
570
+ # as the ForwardMsgs in the queue may not correspond to the running
571
+ # fragment, so dropping the messages may result in the app missing
572
+ # information.
573
+ if not fragment_ids_this_run:
574
+ self._clear_queue()
575
+
544
576
  self._enqueue_forward_msg(
545
- self._create_new_session_message(page_script_hash)
577
+ self._create_new_session_message(
578
+ page_script_hash, fragment_ids_this_run
579
+ )
546
580
  )
547
581
 
548
582
  elif (
549
583
  event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
550
584
  or event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_COMPILE_ERROR
585
+ or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
551
586
  ):
552
587
  if self._state != AppSessionState.SHUTDOWN_REQUESTED:
553
588
  self._state = AppSessionState.APP_NOT_RUNNING
554
589
 
555
- script_succeeded = event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
556
-
557
- script_finished_msg = self._create_script_finished_message(
558
- ForwardMsg.FINISHED_SUCCESSFULLY
559
- if script_succeeded
560
- else ForwardMsg.FINISHED_WITH_COMPILE_ERROR
561
- )
562
- self._enqueue_forward_msg(script_finished_msg)
590
+ if event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS:
591
+ status = ForwardMsg.FINISHED_SUCCESSFULLY
592
+ elif event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS:
593
+ status = ForwardMsg.FINISHED_FRAGMENT_RUN_SUCCESSFULLY
594
+ else:
595
+ status = ForwardMsg.FINISHED_WITH_COMPILE_ERROR
563
596
 
597
+ self._enqueue_forward_msg(self._create_script_finished_message(status))
564
598
  self._debug_last_backmsg_id = None
565
599
 
566
- if script_succeeded:
600
+ if (
601
+ event == ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS
602
+ or event == ScriptRunnerEvent.FRAGMENT_STOPPED_WITH_SUCCESS
603
+ ):
567
604
  # The script completed successfully: update our
568
605
  # LocalSourcesWatcher to account for any source code changes
569
606
  # that change which modules should be watched.
@@ -582,11 +619,12 @@ class AppSession:
582
619
  self._enqueue_forward_msg(msg)
583
620
 
584
621
  elif event == ScriptRunnerEvent.SCRIPT_STOPPED_FOR_RERUN:
585
- script_finished_msg = self._create_script_finished_message(
586
- ForwardMsg.FINISHED_EARLY_FOR_RERUN
587
- )
588
622
  self._state = AppSessionState.APP_NOT_RUNNING
589
- self._enqueue_forward_msg(script_finished_msg)
623
+ self._enqueue_forward_msg(
624
+ self._create_script_finished_message(
625
+ ForwardMsg.FINISHED_EARLY_FOR_RERUN
626
+ )
627
+ )
590
628
  if self._local_sources_watcher:
591
629
  self._local_sources_watcher.update_watched_modules()
592
630
 
@@ -630,7 +668,9 @@ class AppSession:
630
668
  msg.session_event.script_changed_on_disk = True
631
669
  return msg
632
670
 
633
- def _create_new_session_message(self, page_script_hash: str) -> ForwardMsg:
671
+ def _create_new_session_message(
672
+ self, page_script_hash: str, fragment_ids_this_run: set[str] | None = None
673
+ ) -> ForwardMsg:
634
674
  """Create and return a new_session ForwardMsg."""
635
675
  msg = ForwardMsg()
636
676
 
@@ -639,6 +679,9 @@ class AppSession:
639
679
  msg.new_session.main_script_path = self._script_data.main_script_path
640
680
  msg.new_session.page_script_hash = page_script_hash
641
681
 
682
+ if fragment_ids_this_run:
683
+ msg.new_session.fragment_ids_this_run.extend(fragment_ids_this_run)
684
+
642
685
  _populate_app_pages(msg.new_session, self._script_data.main_script_path)
643
686
  _populate_config_msg(msg.new_session.config)
644
687
  _populate_theme_msg(msg.new_session.custom_theme)
@@ -58,9 +58,9 @@ from streamlit.runtime.caching.storage.dummy_cache_storage import (
58
58
  MemoryCacheStorageManager,
59
59
  )
60
60
  from streamlit.runtime.metrics_util import gather_metrics
61
- from streamlit.runtime.runtime_util import duration_to_seconds
62
61
  from streamlit.runtime.scriptrunner.script_run_context import get_script_run_ctx
63
62
  from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
63
+ from streamlit.time_util import time_to_seconds
64
64
 
65
65
  _LOGGER: Final = get_logger(__name__)
66
66
 
@@ -154,7 +154,7 @@ class DataCaches(CacheStatsProvider):
154
154
  If it doesn't exist, create a new one with the given params.
155
155
  """
156
156
 
157
- ttl_seconds = duration_to_seconds(ttl, coerce_none_to_inf=False)
157
+ ttl_seconds = time_to_seconds(ttl, coerce_none_to_inf=False)
158
158
 
159
159
  # Get the existing cache, if it exists, and validate that its params
160
160
  # haven't changed.
@@ -254,7 +254,7 @@ class DataCaches(CacheStatsProvider):
254
254
  CacheStorageContext.
255
255
  """
256
256
 
257
- ttl_seconds = duration_to_seconds(ttl, coerce_none_to_inf=False)
257
+ ttl_seconds = time_to_seconds(ttl, coerce_none_to_inf=False)
258
258
 
259
259
  cache_context = self.create_cache_storage_context(
260
260
  function_key="DUMMY_KEY",
@@ -45,9 +45,9 @@ from streamlit.runtime.caching.cached_message_replay import (
45
45
  )
46
46
  from streamlit.runtime.caching.hashing import HashFuncsDict
47
47
  from streamlit.runtime.metrics_util import gather_metrics
48
- from streamlit.runtime.runtime_util import duration_to_seconds
49
48
  from streamlit.runtime.scriptrunner.script_run_context import get_script_run_ctx
50
49
  from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
50
+ from streamlit.time_util import time_to_seconds
51
51
 
52
52
  _LOGGER: Final = get_logger(__name__)
53
53
 
@@ -89,7 +89,7 @@ class ResourceCaches(CacheStatsProvider):
89
89
  if max_entries is None:
90
90
  max_entries = math.inf
91
91
 
92
- ttl_seconds = duration_to_seconds(ttl)
92
+ ttl_seconds = time_to_seconds(ttl)
93
93
 
94
94
  # Get the existing cache, if it exists, and validate that its params
95
95
  # haven't changed.
@@ -0,0 +1,239 @@
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
+ import contextlib
18
+ import hashlib
19
+ import inspect
20
+ from abc import abstractmethod
21
+ from copy import deepcopy
22
+ from datetime import timedelta
23
+ from functools import wraps
24
+ from typing import Any, Callable, Protocol, TypeVar, overload
25
+
26
+ from streamlit.proto.ForwardMsg_pb2 import ForwardMsg
27
+ from streamlit.runtime.metrics_util import gather_metrics
28
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
29
+ from streamlit.time_util import time_to_seconds
30
+
31
+ F = TypeVar("F", bound=Callable[..., Any])
32
+ Fragment = Callable[[], Any]
33
+
34
+
35
+ class FragmentStorage(Protocol):
36
+ """A key-value store for Fragments. Used to implement the @st.experimental_fragment
37
+ decorator.
38
+
39
+ We intentionally define this as its own protocol despite how generic it appears to
40
+ be at first glance. The reason why is that, in any case where fragments aren't just
41
+ stored as Python closures in memory, storing and retrieving Fragments will generally
42
+ involve serializing and deserializing function bytecode, which is a tricky aspect
43
+ to implementing FragmentStorages that won't generally appear with our other *Storage
44
+ protocols.
45
+ """
46
+
47
+ @abstractmethod
48
+ def get(self, key: str) -> Fragment:
49
+ """Returns the stored fragment for the given key."""
50
+ raise NotImplementedError
51
+
52
+ @abstractmethod
53
+ def set(self, key: str, value: Fragment) -> None:
54
+ """Saves a fragment under the given key."""
55
+ raise NotImplementedError
56
+
57
+ @abstractmethod
58
+ def delete(self, key: str) -> None:
59
+ """Delete the fragment corresponding to the given key."""
60
+ raise NotImplementedError
61
+
62
+ @abstractmethod
63
+ def clear(self) -> None:
64
+ """Remove all fragments saved in this FragmentStorage."""
65
+ raise NotImplementedError
66
+
67
+
68
+ # NOTE: Ideally, we'd like to add a MemoryFragmentStorageStatProvider implementation to
69
+ # keep track of memory usage due to fragments, but doing something like this ends up
70
+ # being difficult in practice as the memory usage of a closure is hard to measure (the
71
+ # vendored implementation of pympler.asizeof that we use elsewhere is unable to measure
72
+ # the size of a function).
73
+ class MemoryFragmentStorage(FragmentStorage):
74
+ """A simple, memory-backed implementation of FragmentStorage.
75
+
76
+ MemoryFragmentStorage is just a wrapper around a plain Python dict that complies with
77
+ the FragmentStorage protocol.
78
+ """
79
+
80
+ def __init__(self):
81
+ self._fragments: dict[str, Fragment] = {}
82
+
83
+ def get(self, key: str) -> Fragment:
84
+ return self._fragments[key]
85
+
86
+ def set(self, key: str, value: Fragment) -> None:
87
+ self._fragments[key] = value
88
+
89
+ def delete(self, key: str) -> None:
90
+ del self._fragments[key]
91
+
92
+ def clear(self) -> None:
93
+ self._fragments.clear()
94
+
95
+
96
+ @overload
97
+ def fragment(
98
+ func: F,
99
+ *,
100
+ run_every: int | float | timedelta | str | None = None,
101
+ ) -> F:
102
+ ...
103
+
104
+
105
+ # Support being able to pass parameters to this decorator (that is, being able to write
106
+ # `@fragment(run_every=5.0)`).
107
+ @overload
108
+ def fragment(
109
+ func: None = None,
110
+ *,
111
+ run_every: int | float | timedelta | str | None = None,
112
+ ) -> Callable[[F], F]:
113
+ ...
114
+
115
+
116
+ @gather_metrics("experimental_fragment")
117
+ def fragment(
118
+ func: F | None = None,
119
+ *,
120
+ run_every: int | float | timedelta | str | None = None,
121
+ ) -> Callable[[F], F] | F:
122
+ """Allow a function to be run independently of the full script.
123
+
124
+ Functions decorated with ``@st.experimental_fragment`` are handled specially within
125
+ an app: when a widget created within an invocation of the function (a fragment) is
126
+ interacted with, then only that fragment is rerun rather than the full streamlit app.
127
+
128
+ Parameters
129
+ ----------
130
+ run_every: int, float, timedelta, str, or None
131
+ If set, fragments created from this function rerun periodically at the specified
132
+ time interval.
133
+
134
+ Example
135
+ -------
136
+ The following example demonstrates basic usage of ``@st.experimental_fragment``. In
137
+ this app, clicking on the "rerun full script" button will increment both counters,
138
+ but the "rerun fragment" button will only increment the counter within the fragment.
139
+
140
+ ```python3
141
+ import streamlit as st
142
+
143
+ if "script_runs" not in st.session_state:
144
+ st.session_state.script_runs = 0
145
+ st.session_state.fragment_runs = 0
146
+
147
+ @st.experimental_fragment
148
+ def fragment():
149
+ st.button("rerun fragment")
150
+ st.write(f"fragment runs: {st.session_state.fragment_runs}")
151
+ st.session_state.fragment_runs += 1
152
+
153
+ fragment()
154
+
155
+ st.button("rerun full script")
156
+ st.write(f"full script runs: {st.session_state.script_runs}")
157
+ st.session_state.script_runs += 1
158
+ ```
159
+ """
160
+
161
+ if func is None:
162
+ # Support passing the params via function decorator
163
+ def wrapper(f: F) -> F:
164
+ return fragment(
165
+ func=f,
166
+ run_every=run_every,
167
+ )
168
+
169
+ return wrapper
170
+ else:
171
+ non_optional_func = func
172
+
173
+ @wraps(non_optional_func)
174
+ def wrap(*args, **kwargs):
175
+ from streamlit.delta_generator import dg_stack
176
+
177
+ ctx = get_script_run_ctx()
178
+ if ctx is None:
179
+ return
180
+
181
+ cursors_snapshot = deepcopy(ctx.cursors)
182
+ dg_stack_snapshot = deepcopy(dg_stack.get())
183
+ active_dg = dg_stack_snapshot[-1]
184
+ h = hashlib.new("md5")
185
+ h.update(
186
+ f"{non_optional_func.__module__}.{non_optional_func.__qualname__}{active_dg._get_delta_path_str()}".encode(
187
+ "utf-8"
188
+ )
189
+ )
190
+ fragment_id = h.hexdigest()
191
+
192
+ def wrapped_fragment():
193
+ import streamlit as st
194
+
195
+ # NOTE: We need to call get_script_run_ctx here again and can't just use the
196
+ # value of ctx from above captured by the closure because subsequent
197
+ # fragment runs will generally run in a new script run, thus we'll have a
198
+ # new ctx.
199
+ ctx = get_script_run_ctx(suppress_warning=True)
200
+ assert ctx is not None
201
+
202
+ if ctx.fragment_ids_this_run:
203
+ # This script run is a run of one or more fragments. We restore the
204
+ # state of ctx.cursors and dg_stack to the snapshots we took when this
205
+ # fragment was declared.
206
+ ctx.cursors = deepcopy(cursors_snapshot)
207
+ dg_stack.set(deepcopy(dg_stack_snapshot))
208
+ else:
209
+ # Otherwise, we must be in a full script run. We need to temporarily set
210
+ # ctx.current_fragment_id so that elements corresponding to this
211
+ # fragment get tagged with the appropriate ID. ctx.current_fragment_id
212
+ # gets reset after the fragment function finishes running.
213
+ ctx.current_fragment_id = fragment_id
214
+
215
+ try:
216
+ with st.container():
217
+ result = non_optional_func(*args, **kwargs)
218
+ finally:
219
+ ctx.current_fragment_id = None
220
+
221
+ return result
222
+
223
+ ctx.fragment_storage.set(fragment_id, wrapped_fragment)
224
+
225
+ if run_every:
226
+ msg = ForwardMsg()
227
+ msg.auto_rerun.interval = time_to_seconds(run_every)
228
+ msg.auto_rerun.fragment_id = fragment_id
229
+ ctx.enqueue(msg)
230
+
231
+ return wrapped_fragment()
232
+
233
+ with contextlib.suppress(AttributeError):
234
+ # Make this a well-behaved decorator by preserving important function
235
+ # attributes.
236
+ wrap.__dict__.update(non_optional_func.__dict__)
237
+ wrap.__signature__ = inspect.signature(non_optional_func) # type: ignore
238
+
239
+ return wrap