streamlit-nightly 1.31.2.dev20240212__py2.py3-none-any.whl → 1.31.2.dev20240214__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 (184) hide show
  1. streamlit/case_converters.py +9 -4
  2. streamlit/cli_util.py +2 -0
  3. streamlit/code_util.py +5 -2
  4. streamlit/color_util.py +2 -0
  5. streamlit/column_config.py +2 -0
  6. streamlit/commands/execution_control.py +4 -2
  7. streamlit/commands/experimental_query_params.py +7 -4
  8. streamlit/commands/page_config.py +11 -9
  9. streamlit/components/v1/components.py +23 -16
  10. streamlit/config.py +3 -5
  11. streamlit/config_option.py +12 -11
  12. streamlit/connections/base_connection.py +4 -2
  13. streamlit/connections/snowflake_connection.py +4 -4
  14. streamlit/connections/snowpark_connection.py +3 -3
  15. streamlit/connections/sql_connection.py +6 -6
  16. streamlit/connections/util.py +8 -5
  17. streamlit/constants.py +2 -0
  18. streamlit/cursor.py +16 -14
  19. streamlit/delta_generator.py +10 -13
  20. streamlit/deprecation_util.py +4 -3
  21. streamlit/echo.py +5 -3
  22. streamlit/elements/alert.py +16 -14
  23. streamlit/elements/altair_utils.py +8 -6
  24. streamlit/elements/arrow.py +4 -4
  25. streamlit/elements/arrow_altair.py +24 -34
  26. streamlit/elements/arrow_vega_lite.py +9 -14
  27. streamlit/elements/balloons.py +4 -2
  28. streamlit/elements/bokeh_chart.py +7 -7
  29. streamlit/elements/code.py +6 -4
  30. streamlit/elements/deck_gl_json_chart.py +8 -8
  31. streamlit/elements/doc_string.py +5 -9
  32. streamlit/elements/empty.py +4 -2
  33. streamlit/elements/exception.py +10 -10
  34. streamlit/elements/form.py +1 -3
  35. streamlit/elements/graphviz_chart.py +5 -6
  36. streamlit/elements/heading.py +16 -14
  37. streamlit/elements/iframe.py +14 -12
  38. streamlit/elements/image.py +8 -8
  39. streamlit/elements/json.py +6 -4
  40. streamlit/elements/layouts.py +12 -10
  41. streamlit/elements/lib/column_config_utils.py +2 -2
  42. streamlit/elements/lib/column_types.py +23 -23
  43. streamlit/elements/lib/dicttools.py +10 -6
  44. streamlit/elements/lib/mutable_status_container.py +7 -7
  45. streamlit/elements/lib/pandas_styler_utils.py +6 -6
  46. streamlit/elements/lib/streamlit_plotly_theme.py +2 -0
  47. streamlit/elements/map.py +11 -22
  48. streamlit/elements/markdown.py +16 -14
  49. streamlit/elements/media.py +16 -16
  50. streamlit/elements/metric.py +9 -7
  51. streamlit/elements/plotly_chart.py +5 -5
  52. streamlit/elements/progress.py +6 -6
  53. streamlit/elements/pyplot.py +10 -13
  54. streamlit/elements/snow.py +4 -2
  55. streamlit/elements/spinner.py +2 -0
  56. streamlit/elements/text.py +7 -5
  57. streamlit/elements/toast.py +6 -4
  58. streamlit/elements/utils.py +15 -28
  59. streamlit/elements/widgets/button.py +39 -39
  60. streamlit/elements/widgets/camera_input.py +21 -17
  61. streamlit/elements/widgets/chat.py +6 -7
  62. streamlit/elements/widgets/checkbox.py +21 -19
  63. streamlit/elements/widgets/color_picker.py +18 -16
  64. streamlit/elements/widgets/data_editor.py +7 -7
  65. streamlit/elements/widgets/file_uploader.py +59 -55
  66. streamlit/elements/widgets/multiselect.py +33 -42
  67. streamlit/elements/widgets/number_input.py +10 -5
  68. streamlit/elements/widgets/radio.py +1 -1
  69. streamlit/elements/widgets/select_slider.py +25 -34
  70. streamlit/elements/widgets/selectbox.py +1 -1
  71. streamlit/elements/widgets/slider.py +28 -36
  72. streamlit/elements/widgets/text_widgets.py +6 -6
  73. streamlit/elements/widgets/time_widgets.py +13 -13
  74. streamlit/elements/write.py +21 -29
  75. streamlit/env_util.py +5 -3
  76. streamlit/error_util.py +7 -3
  77. streamlit/errors.py +3 -1
  78. streamlit/external/langchain/streamlit_callback_handler.py +26 -24
  79. streamlit/file_util.py +18 -14
  80. streamlit/folder_black_list.py +3 -1
  81. streamlit/git_util.py +5 -3
  82. streamlit/js_number.py +10 -13
  83. streamlit/logger.py +5 -5
  84. streamlit/net_util.py +14 -11
  85. streamlit/platform.py +2 -0
  86. streamlit/runtime/__init__.py +2 -0
  87. streamlit/runtime/app_session.py +42 -42
  88. streamlit/runtime/caching/__init__.py +4 -4
  89. streamlit/runtime/caching/cache_data_api.py +3 -3
  90. streamlit/runtime/caching/cache_errors.py +5 -3
  91. streamlit/runtime/caching/cache_type.py +2 -0
  92. streamlit/runtime/caching/cache_utils.py +2 -4
  93. streamlit/runtime/caching/cached_message_replay.py +12 -5
  94. streamlit/runtime/caching/hashing.py +29 -21
  95. streamlit/runtime/caching/storage/cache_storage_protocol.py +1 -2
  96. streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -5
  97. streamlit/runtime/connection_factory.py +8 -8
  98. streamlit/runtime/forward_msg_cache.py +20 -18
  99. streamlit/runtime/forward_msg_queue.py +8 -9
  100. streamlit/runtime/legacy_caching/caching.py +32 -42
  101. streamlit/runtime/legacy_caching/hashing.py +29 -25
  102. streamlit/runtime/media_file_manager.py +16 -14
  103. streamlit/runtime/media_file_storage.py +8 -8
  104. streamlit/runtime/memory_media_file_storage.py +12 -14
  105. streamlit/runtime/memory_session_storage.py +4 -3
  106. streamlit/runtime/memory_uploaded_file_manager.py +9 -10
  107. streamlit/runtime/metrics_util.py +20 -20
  108. streamlit/runtime/runtime.py +25 -27
  109. streamlit/runtime/runtime_util.py +5 -3
  110. streamlit/runtime/script_data.py +2 -0
  111. streamlit/runtime/scriptrunner/magic.py +17 -11
  112. streamlit/runtime/scriptrunner/magic_funcs.py +2 -0
  113. streamlit/runtime/scriptrunner/script_requests.py +6 -4
  114. streamlit/runtime/scriptrunner/script_run_context.py +17 -17
  115. streamlit/runtime/scriptrunner/script_runner.py +7 -5
  116. streamlit/runtime/secrets.py +4 -6
  117. streamlit/runtime/session_manager.py +14 -14
  118. streamlit/runtime/state/common.py +5 -4
  119. streamlit/runtime/state/query_params.py +8 -6
  120. streamlit/runtime/state/query_params_proxy.py +7 -5
  121. streamlit/runtime/state/safe_session_state.py +7 -5
  122. streamlit/runtime/state/session_state.py +3 -4
  123. streamlit/runtime/state/session_state_proxy.py +5 -5
  124. streamlit/runtime/state/widgets.py +20 -18
  125. streamlit/runtime/stats.py +13 -15
  126. streamlit/runtime/uploaded_file_manager.py +6 -5
  127. streamlit/runtime/websocket_session_manager.py +14 -14
  128. streamlit/source_util.py +13 -11
  129. streamlit/static/asset-manifest.json +13 -13
  130. streamlit/static/index.html +1 -1
  131. streamlit/static/static/css/2411.8b8f33d6.chunk.css +1 -0
  132. streamlit/static/static/css/43.e3b876c5.chunk.css +1 -0
  133. streamlit/static/static/css/6692.65519639.chunk.css +1 -0
  134. streamlit/static/static/js/{3075.76725a14.chunk.js → 2411.714d213e.chunk.js} +2 -2
  135. streamlit/static/static/js/4185.21ca0590.chunk.js +1 -0
  136. streamlit/static/static/js/43.36939bb1.chunk.js +1 -0
  137. streamlit/static/static/js/{5117.6a701db1.chunk.js → 5117.04bfe5d3.chunk.js} +1 -1
  138. streamlit/static/static/js/{5791.30b01ee8.chunk.js → 5791.c5138157.chunk.js} +1 -1
  139. streamlit/static/static/js/656.8c998bc8.chunk.js +2 -0
  140. streamlit/static/static/js/{6692.6ac4ea6f.chunk.js → 6692.6496cbc2.chunk.js} +1 -1
  141. streamlit/static/static/js/7142.400eefdd.chunk.js +1 -0
  142. streamlit/static/static/js/main.2737c0f9.js +2 -0
  143. streamlit/static/static/js/{main.043d802e.js.LICENSE.txt → main.2737c0f9.js.LICENSE.txt} +23 -25
  144. streamlit/string_util.py +13 -9
  145. streamlit/temporary_directory.py +3 -1
  146. streamlit/testing/v1/element_tree.py +1 -2
  147. streamlit/testing/v1/util.py +7 -3
  148. streamlit/type_util.py +30 -25
  149. streamlit/url_util.py +6 -4
  150. streamlit/user_info.py +8 -6
  151. streamlit/util.py +23 -37
  152. streamlit/version.py +16 -9
  153. streamlit/watcher/event_based_path_watcher.py +10 -10
  154. streamlit/watcher/local_sources_watcher.py +15 -13
  155. streamlit/watcher/path_watcher.py +0 -3
  156. streamlit/watcher/polling_path_watcher.py +9 -8
  157. streamlit/watcher/util.py +3 -2
  158. streamlit/web/cache_storage_manager_config.py +2 -0
  159. streamlit/web/server/app_static_file_handler.py +6 -5
  160. streamlit/web/server/browser_websocket_handler.py +10 -8
  161. streamlit/web/server/component_request_handler.py +7 -4
  162. streamlit/web/server/media_file_handler.py +5 -4
  163. streamlit/web/server/routes.py +6 -3
  164. streamlit/web/server/server.py +41 -34
  165. streamlit/web/server/server_util.py +8 -3
  166. streamlit/web/server/stats_request_handler.py +14 -5
  167. streamlit/web/server/upload_file_request_handler.py +7 -8
  168. streamlit/web/server/websocket_headers.py +2 -2
  169. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/METADATA +1 -1
  170. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/RECORD +176 -176
  171. streamlit/static/static/css/3075.81b3d18f.chunk.css +0 -1
  172. streamlit/static/static/css/43.c24b25fa.chunk.css +0 -1
  173. streamlit/static/static/css/6692.bb444a79.chunk.css +0 -1
  174. streamlit/static/static/js/1215.baf3721f.chunk.js +0 -2
  175. streamlit/static/static/js/4185.90e929dc.chunk.js +0 -1
  176. streamlit/static/static/js/43.8ca4bc8a.chunk.js +0 -1
  177. streamlit/static/static/js/7142.a359ed63.chunk.js +0 -1
  178. streamlit/static/static/js/main.043d802e.js +0 -2
  179. /streamlit/static/static/js/{3075.76725a14.chunk.js.LICENSE.txt → 2411.714d213e.chunk.js.LICENSE.txt} +0 -0
  180. /streamlit/static/static/js/{1215.baf3721f.chunk.js.LICENSE.txt → 656.8c998bc8.chunk.js.LICENSE.txt} +0 -0
  181. {streamlit_nightly-1.31.2.dev20240212.data → streamlit_nightly-1.31.2.dev20240214.data}/scripts/streamlit.cmd +0 -0
  182. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/WHEEL +0 -0
  183. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/entry_points.txt +0 -0
  184. {streamlit_nightly-1.31.2.dev20240212.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/top_level.txt +0 -0
@@ -11,11 +11,14 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
14
17
  import asyncio
15
18
  import sys
16
19
  import uuid
17
20
  from enum import Enum
18
- from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Union
21
+ from typing import TYPE_CHECKING, Callable, Final
19
22
 
20
23
  import streamlit.elements.exception as exception_utils
21
24
  from streamlit import config, runtime, source_util
@@ -44,10 +47,11 @@ from streamlit.runtime.uploaded_file_manager import UploadedFileManager
44
47
  from streamlit.version import STREAMLIT_VERSION_STRING
45
48
  from streamlit.watcher import LocalSourcesWatcher
46
49
 
47
- LOGGER = get_logger(__name__)
48
50
  if TYPE_CHECKING:
49
51
  from streamlit.runtime.state import SessionState
50
52
 
53
+ _LOGGER: Final = get_logger(__name__)
54
+
51
55
 
52
56
  class AppSessionState(Enum):
53
57
  APP_NOT_RUNNING = "APP_NOT_RUNNING"
@@ -77,10 +81,10 @@ class AppSession:
77
81
  script_data: ScriptData,
78
82
  uploaded_file_manager: UploadedFileManager,
79
83
  script_cache: ScriptCache,
80
- message_enqueued_callback: Optional[Callable[[], None]],
84
+ message_enqueued_callback: Callable[[], None] | None,
81
85
  local_sources_watcher: LocalSourcesWatcher,
82
- user_info: Dict[str, Optional[str]],
83
- session_id_override: Optional[str] = None,
86
+ user_info: dict[str, str | None],
87
+ session_id_override: str | None = None,
84
88
  ) -> None:
85
89
  """Initialize the AppSession.
86
90
 
@@ -139,17 +143,15 @@ class AppSession:
139
143
  # due to the source code changing we need to pass in the previous client state.
140
144
  self._client_state = ClientState()
141
145
 
142
- self._local_sources_watcher: Optional[
143
- LocalSourcesWatcher
144
- ] = local_sources_watcher
145
- self._stop_config_listener: Optional[Callable[[], bool]] = None
146
- self._stop_pages_listener: Optional[Callable[[], bool]] = None
146
+ self._local_sources_watcher: LocalSourcesWatcher | None = local_sources_watcher
147
+ self._stop_config_listener: Callable[[], bool] | None = None
148
+ self._stop_pages_listener: Callable[[], None] | None = None
147
149
 
148
150
  self.register_file_watchers()
149
151
 
150
152
  self._run_on_save = config.get_option("server.runOnSave")
151
153
 
152
- self._scriptrunner: Optional[ScriptRunner] = None
154
+ self._scriptrunner: ScriptRunner | None = None
153
155
 
154
156
  # This needs to be lazily imported to avoid a dependency cycle.
155
157
  from streamlit.runtime.state import SessionState
@@ -157,9 +159,9 @@ class AppSession:
157
159
  self._session_state = SessionState()
158
160
  self._user_info = user_info
159
161
 
160
- self._debug_last_backmsg_id: Optional[str] = None
162
+ self._debug_last_backmsg_id: str | None = None
161
163
 
162
- LOGGER.debug("AppSession initialized (id=%s)", self.id)
164
+ _LOGGER.debug("AppSession initialized (id=%s)", self.id)
163
165
 
164
166
  def __del__(self) -> None:
165
167
  """Ensure that we call shutdown() when an AppSession is garbage collected."""
@@ -210,7 +212,7 @@ class AppSession:
210
212
  self._stop_config_listener = None
211
213
  self._stop_pages_listener = None
212
214
 
213
- def flush_browser_queue(self) -> List[ForwardMsg]:
215
+ def flush_browser_queue(self) -> list[ForwardMsg]:
214
216
  """Clear the forward message queue and return the messages it contained.
215
217
 
216
218
  The Server calls this periodically to deliver new messages
@@ -232,7 +234,7 @@ class AppSession:
232
234
 
233
235
  """
234
236
  if self._state != AppSessionState.SHUTDOWN_REQUESTED:
235
- LOGGER.debug("Shutting down (id=%s)", self.id)
237
+ _LOGGER.debug("Shutting down (id=%s)", self.id)
236
238
  # Clear any unused session files in upload file manager and media
237
239
  # file manager
238
240
  self._uploaded_file_mgr.remove_session_files(self.id)
@@ -298,10 +300,10 @@ class AppSession:
298
300
  elif msg_type == "file_urls_request":
299
301
  self._handle_file_urls_request(msg.file_urls_request)
300
302
  else:
301
- LOGGER.warning('No handler for "%s"', msg_type)
303
+ _LOGGER.warning('No handler for "%s"', msg_type)
302
304
 
303
305
  except Exception as ex:
304
- LOGGER.error(ex)
306
+ _LOGGER.error(ex)
305
307
  self.handle_backmsg_exception(ex)
306
308
 
307
309
  def handle_backmsg_exception(self, e: BaseException) -> None:
@@ -333,7 +335,7 @@ class AppSession:
333
335
  lambda: self._enqueue_forward_msg(self._create_exception_message(e))
334
336
  )
335
337
 
336
- def request_rerun(self, client_state: Optional[ClientState]) -> None:
338
+ def request_rerun(self, client_state: ClientState | None) -> None:
337
339
  """Signal that we're interested in running the script.
338
340
 
339
341
  If the script is not already running, it will be started immediately.
@@ -347,7 +349,7 @@ class AppSession:
347
349
 
348
350
  """
349
351
  if self._state == AppSessionState.SHUTDOWN_REQUESTED:
350
- LOGGER.warning("Discarding rerun request after shutdown")
352
+ _LOGGER.warning("Discarding rerun request after shutdown")
351
353
  return
352
354
 
353
355
  if client_state:
@@ -403,7 +405,7 @@ class AppSession:
403
405
  self._scriptrunner.start()
404
406
 
405
407
  @property
406
- def session_state(self) -> "SessionState":
408
+ def session_state(self) -> SessionState:
407
409
  return self._session_state
408
410
 
409
411
  def _should_rerun_on_file_change(self, filepath: str) -> bool:
@@ -421,7 +423,7 @@ class AppSession:
421
423
 
422
424
  return True
423
425
 
424
- def _on_source_file_changed(self, filepath: Optional[str] = None) -> None:
426
+ def _on_source_file_changed(self, filepath: str | None = None) -> None:
425
427
  """One of our source files changed. Clear the cache and schedule a rerun if appropriate."""
426
428
  self._script_cache.clear()
427
429
 
@@ -456,12 +458,12 @@ class AppSession:
456
458
 
457
459
  def _on_scriptrunner_event(
458
460
  self,
459
- sender: Optional[ScriptRunner],
461
+ sender: ScriptRunner | None,
460
462
  event: ScriptRunnerEvent,
461
- forward_msg: Optional[ForwardMsg] = None,
462
- exception: Optional[BaseException] = None,
463
- client_state: Optional[ClientState] = None,
464
- page_script_hash: Optional[str] = None,
463
+ forward_msg: ForwardMsg | None = None,
464
+ exception: BaseException | None = None,
465
+ client_state: ClientState | None = None,
466
+ page_script_hash: str | None = None,
465
467
  ) -> None:
466
468
  """Called when our ScriptRunner emits an event.
467
469
 
@@ -477,12 +479,12 @@ class AppSession:
477
479
 
478
480
  def _handle_scriptrunner_event_on_event_loop(
479
481
  self,
480
- sender: Optional[ScriptRunner],
482
+ sender: ScriptRunner | None,
481
483
  event: ScriptRunnerEvent,
482
- forward_msg: Optional[ForwardMsg] = None,
483
- exception: Optional[BaseException] = None,
484
- client_state: Optional[ClientState] = None,
485
- page_script_hash: Optional[str] = None,
484
+ forward_msg: ForwardMsg | None = None,
485
+ exception: BaseException | None = None,
486
+ client_state: ClientState | None = None,
487
+ page_script_hash: str | None = None,
486
488
  ) -> None:
487
489
  """Handle a ScriptRunner event.
488
490
 
@@ -525,7 +527,7 @@ class AppSession:
525
527
  # rerun request, for example) while another ScriptRunner is still
526
528
  # shutting down. The shutting-down ScriptRunner may still
527
529
  # emit events.
528
- LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event)
530
+ _LOGGER.debug("Ignoring event from non-current ScriptRunner: %s", event)
529
531
  return
530
532
 
531
533
  prev_state = self._state
@@ -663,7 +665,7 @@ class AppSession:
663
665
  return msg
664
666
 
665
667
  def _create_script_finished_message(
666
- self, status: "ForwardMsg.ScriptFinishedStatus.ValueType"
668
+ self, status: ForwardMsg.ScriptFinishedStatus.ValueType
667
669
  ) -> ForwardMsg:
668
670
  """Create and return a script_finished ForwardMsg."""
669
671
  msg = ForwardMsg()
@@ -712,10 +714,10 @@ class AppSession:
712
714
  except Exception as ex:
713
715
  # Users may never even install Git in the first place, so this
714
716
  # error requires no action. It can be useful for debugging.
715
- LOGGER.debug("Obtaining Git information produced an error", exc_info=ex)
717
+ _LOGGER.debug("Obtaining Git information produced an error", exc_info=ex)
716
718
 
717
719
  def _handle_rerun_script_request(
718
- self, client_state: Optional[ClientState] = None
720
+ self, client_state: ClientState | None = None
719
721
  ) -> None:
720
722
  """Tell the ScriptRunner to re-run its script.
721
723
 
@@ -794,10 +796,10 @@ class AppSession:
794
796
  # This field will be available at runtime as of protobuf 3.20.1, but
795
797
  # we are using an older version.
796
798
  # For details, see: https://github.com/protocolbuffers/protobuf/issues/8175
797
- def _get_toolbar_mode() -> "Config.ToolbarMode.ValueType":
799
+ def _get_toolbar_mode() -> Config.ToolbarMode.ValueType:
798
800
  config_key = "client.toolbarMode"
799
801
  config_value = config.get_option(config_key)
800
- enum_value: Optional["Config.ToolbarMode.ValueType"] = getattr(
802
+ enum_value: Config.ToolbarMode.ValueType | None = getattr(
801
803
  Config.ToolbarMode, config_value.upper()
802
804
  )
803
805
  if enum_value is None:
@@ -844,7 +846,7 @@ def _populate_theme_msg(msg: CustomThemeConfig) -> None:
844
846
  base = theme_opts["base"]
845
847
  if base is not None:
846
848
  if base not in base_map:
847
- LOGGER.warning(
849
+ _LOGGER.warning(
848
850
  f'"{base}" is an invalid value for theme.base.'
849
851
  f" Allowed values include {list(base_map.keys())}."
850
852
  ' Setting theme.base to "light".'
@@ -860,7 +862,7 @@ def _populate_theme_msg(msg: CustomThemeConfig) -> None:
860
862
  font = theme_opts["font"]
861
863
  if font is not None:
862
864
  if font not in font_map:
863
- LOGGER.warning(
865
+ _LOGGER.warning(
864
866
  f'"{font}" is an invalid value for theme.font.'
865
867
  f" Allowed values include {list(font_map.keys())}."
866
868
  ' Setting theme.font to "sans serif".'
@@ -874,9 +876,7 @@ def _populate_user_info_msg(msg: UserInfo) -> None:
874
876
  msg.installation_id_v3 = Installation.instance().installation_id_v3
875
877
 
876
878
 
877
- def _populate_app_pages(
878
- msg: Union[NewSession, PagesChanged], main_script_path: str
879
- ) -> None:
879
+ def _populate_app_pages(msg: NewSession | PagesChanged, main_script_path: str) -> None:
880
880
  for page_script_hash, page_info in source_util.get_pages(main_script_path).items():
881
881
  page_proto = msg.app_pages.add()
882
882
 
@@ -12,8 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import contextlib
16
- from typing import Any, Iterator, Union
18
+ from typing import Any, Iterator
17
19
 
18
20
  from google.protobuf.message import Message
19
21
 
@@ -77,9 +79,7 @@ def save_widget_metadata(metadata: WidgetMetadata[Any]) -> None:
77
79
  CACHE_RESOURCE_MESSAGE_REPLAY_CTX.save_widget_metadata(metadata)
78
80
 
79
81
 
80
- def save_media_data(
81
- image_data: Union[bytes, str], mimetype: str, image_id: str
82
- ) -> None:
82
+ def save_media_data(image_data: bytes | str, mimetype: str, image_id: str) -> None:
83
83
  CACHE_DATA_MESSAGE_REPLAY_CTX.save_image_data(image_data, mimetype, image_id)
84
84
  CACHE_RESOURCE_MESSAGE_REPLAY_CTX.save_image_data(image_data, mimetype, image_id)
85
85
 
@@ -20,9 +20,9 @@ import pickle
20
20
  import threading
21
21
  import types
22
22
  from datetime import timedelta
23
- from typing import Any, Callable, TypeVar, Union, cast, overload
23
+ from typing import Any, Callable, Final, Literal, TypeVar, Union, cast, overload
24
24
 
25
- from typing_extensions import Literal, TypeAlias
25
+ from typing_extensions import TypeAlias
26
26
 
27
27
  import streamlit as st
28
28
  from streamlit import runtime
@@ -62,7 +62,7 @@ from streamlit.runtime.metrics_util import gather_metrics
62
62
  from streamlit.runtime.scriptrunner.script_run_context import get_script_run_ctx
63
63
  from streamlit.runtime.stats import CacheStat, CacheStatsProvider, group_stats
64
64
 
65
- _LOGGER = get_logger(__name__)
65
+ _LOGGER: Final = get_logger(__name__)
66
66
 
67
67
  CACHE_DATA_MESSAGE_REPLAY_CTX = CachedMessageReplayContext(CacheType.DATA)
68
68
 
@@ -12,8 +12,10 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import types
16
- from typing import Any, Optional
18
+ from typing import Any
17
19
 
18
20
  from streamlit import type_util
19
21
  from streamlit.errors import (
@@ -50,7 +52,7 @@ class UnhashableParamError(StreamlitAPIException):
50
52
  self,
51
53
  cache_type: CacheType,
52
54
  func: types.FunctionType,
53
- arg_name: Optional[str],
55
+ arg_name: str | None,
54
56
  arg_value: Any,
55
57
  orig_exc: BaseException,
56
58
  ):
@@ -62,7 +64,7 @@ class UnhashableParamError(StreamlitAPIException):
62
64
  def _create_message(
63
65
  cache_type: CacheType,
64
66
  func: types.FunctionType,
65
- arg_name: Optional[str],
67
+ arg_name: str | None,
66
68
  arg_value: Any,
67
69
  ) -> str:
68
70
  arg_name_str = arg_name if arg_name is not None else "(unnamed)"
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from __future__ import annotations
16
+
15
17
  import enum
16
18
 
17
19
 
@@ -26,9 +26,7 @@ import types
26
26
  from abc import abstractmethod
27
27
  from collections import defaultdict
28
28
  from datetime import timedelta
29
- from typing import Any, Callable, overload
30
-
31
- from typing_extensions import Literal
29
+ from typing import Any, Callable, Final, Literal, overload
32
30
 
33
31
  from streamlit import type_util
34
32
  from streamlit.elements.spinner import spinner
@@ -53,7 +51,7 @@ from streamlit.runtime.caching.cached_message_replay import (
53
51
  from streamlit.runtime.caching.hashing import HashFuncsDict, update_hash
54
52
  from streamlit.util import HASHLIB_KWARGS
55
53
 
56
- _LOGGER = get_logger(__name__)
54
+ _LOGGER: Final = get_logger(__name__)
57
55
 
58
56
  # The timer function we use with TTLCache. This is the default timer func, but
59
57
  # is exposed here as a constant so that it can be patched in unit tests.
@@ -19,10 +19,17 @@ import hashlib
19
19
  import threading
20
20
  import types
21
21
  from dataclasses import dataclass
22
- from typing import TYPE_CHECKING, Any, Iterator, Union
22
+ from typing import (
23
+ TYPE_CHECKING,
24
+ Any,
25
+ Final,
26
+ Iterator,
27
+ Protocol,
28
+ Union,
29
+ runtime_checkable,
30
+ )
23
31
 
24
32
  from google.protobuf.message import Message
25
- from typing_extensions import Protocol, runtime_checkable
26
33
 
27
34
  import streamlit as st
28
35
  from streamlit import runtime, util
@@ -45,7 +52,7 @@ from streamlit.util import HASHLIB_KWARGS
45
52
  if TYPE_CHECKING:
46
53
  from streamlit.delta_generator import DeltaGenerator
47
54
 
48
- _LOGGER = get_logger(__name__)
55
+ _LOGGER: Final = get_logger(__name__)
49
56
 
50
57
 
51
58
  @runtime_checkable
@@ -369,7 +376,7 @@ class CachedMessageReplayContext(threading.local):
369
376
 
370
377
  def maybe_show_cached_st_function_warning(
371
378
  self,
372
- dg: "DeltaGenerator",
379
+ dg: DeltaGenerator,
373
380
  st_func_name: str,
374
381
  ) -> None:
375
382
  """If appropriate, warn about calling st.foo inside @memo.
@@ -401,7 +408,7 @@ class CachedMessageReplayContext(threading.local):
401
408
 
402
409
  def _show_cached_st_function_warning(
403
410
  self,
404
- dg: "DeltaGenerator",
411
+ dg: DeltaGenerator,
405
412
  st_func_name: str,
406
413
  cached_func: types.FunctionType,
407
414
  ) -> None:
@@ -13,6 +13,9 @@
13
13
  # limitations under the License.
14
14
 
15
15
  """Hashing for st.cache_data and st.cache_resource."""
16
+
17
+ from __future__ import annotations
18
+
16
19
  import collections
17
20
  import dataclasses
18
21
  import datetime
@@ -22,15 +25,15 @@ import inspect
22
25
  import io
23
26
  import os
24
27
  import pickle
25
- import struct
26
28
  import sys
27
29
  import tempfile
28
30
  import threading
29
- import unittest.mock
30
31
  import uuid
31
32
  import weakref
32
33
  from enum import Enum
33
- from typing import Any, Callable, Dict, List, Optional, Pattern, Type, Union
34
+ from typing import Any, Callable, Dict, Final, Pattern, Type, Union
35
+
36
+ from typing_extensions import TypeAlias
34
37
 
35
38
  from streamlit import type_util, util
36
39
  from streamlit.errors import StreamlitAPIException
@@ -40,18 +43,20 @@ from streamlit.runtime.uploaded_file_manager import UploadedFile
40
43
  from streamlit.util import HASHLIB_KWARGS
41
44
 
42
45
  # If a dataframe has more than this many rows, we consider it large and hash a sample.
43
- _PANDAS_ROWS_LARGE = 100000
44
- _PANDAS_SAMPLE_SIZE = 10000
46
+ _PANDAS_ROWS_LARGE: Final = 100000
47
+ _PANDAS_SAMPLE_SIZE: Final = 10000
45
48
 
46
49
  # Similar to dataframes, we also sample large numpy arrays.
47
- _NP_SIZE_LARGE = 1000000
48
- _NP_SAMPLE_SIZE = 100000
50
+ _NP_SIZE_LARGE: Final = 1000000
51
+ _NP_SAMPLE_SIZE: Final = 100000
49
52
 
50
- HashFuncsDict = Dict[Union[str, Type[Any]], Callable[[Any], Any]]
53
+ HashFuncsDict: TypeAlias = Dict[Union[str, Type[Any]], Callable[[Any], Any]]
51
54
 
52
55
  # Arbitrary item to denote where we found a cycle in a hashed object.
53
56
  # This allows us to hash self-referencing lists, dictionaries, etc.
54
- _CYCLE_PLACEHOLDER = b"streamlit-57R34ML17-hesamagicalponyflyingthroughthesky-CYCLE"
57
+ _CYCLE_PLACEHOLDER: Final = (
58
+ b"streamlit-57R34ML17-hesamagicalponyflyingthroughthesky-CYCLE"
59
+ )
55
60
 
56
61
 
57
62
  class UserHashError(StreamlitAPIException):
@@ -60,7 +65,7 @@ class UserHashError(StreamlitAPIException):
60
65
  orig_exc,
61
66
  object_to_hash,
62
67
  hash_func,
63
- cache_type: Optional[CacheType] = None,
68
+ cache_type: CacheType | None = None,
64
69
  ):
65
70
  self.alternate_name = type(orig_exc).__name__
66
71
  self.hash_func = hash_func
@@ -101,7 +106,7 @@ If you think this is actually a Streamlit bug, please
101
106
  self,
102
107
  orig_exc: BaseException,
103
108
  failed_obj: Any,
104
- ) -> Dict[str, Any]:
109
+ ) -> dict[str, Any]:
105
110
  hash_source = hash_stacks.current.hash_source
106
111
 
107
112
  failed_obj_type_str = type_util.get_fqn_type(failed_obj)
@@ -139,8 +144,8 @@ def update_hash(
139
144
  val: Any,
140
145
  hasher,
141
146
  cache_type: CacheType,
142
- hash_source: Optional[Callable[..., Any]] = None,
143
- hash_funcs: Optional[HashFuncsDict] = None,
147
+ hash_source: Callable[..., Any] | None = None,
148
+ hash_funcs: HashFuncsDict | None = None,
144
149
  ) -> None:
145
150
  """Updates a hashlib hasher with the hash of val.
146
151
 
@@ -166,10 +171,10 @@ class _HashStack:
166
171
  """
167
172
 
168
173
  def __init__(self):
169
- self._stack: collections.OrderedDict[int, List[Any]] = collections.OrderedDict()
174
+ self._stack: collections.OrderedDict[int, list[Any]] = collections.OrderedDict()
170
175
  # A function that we decorate with streamlit cache
171
176
  # primitive (st.cache_data or st.cache_resource).
172
- self.hash_source: Optional[Callable[..., Any]] = None
177
+ self.hash_source: Callable[..., Any] | None = None
173
178
 
174
179
  def __repr__(self) -> str:
175
180
  return util.repr_(self)
@@ -226,11 +231,14 @@ def _int_to_bytes(i: int) -> bytes:
226
231
 
227
232
 
228
233
  def _float_to_bytes(f: float) -> bytes:
234
+ # Lazy-load for performance reasons.
235
+ import struct
236
+
229
237
  # Floats are 64bit in Python, so we need to use the "d" format.
230
238
  return struct.pack("<d", f)
231
239
 
232
240
 
233
- def _key(obj: Optional[Any]) -> Any:
241
+ def _key(obj: Any | None) -> Any:
234
242
  """Return key for memoization."""
235
243
 
236
244
  if obj is None:
@@ -268,9 +276,7 @@ def _key(obj: Optional[Any]) -> Any:
268
276
  class _CacheFuncHasher:
269
277
  """A hasher that can hash objects with cycles."""
270
278
 
271
- def __init__(
272
- self, cache_type: CacheType, hash_funcs: Optional[HashFuncsDict] = None
273
- ):
279
+ def __init__(self, cache_type: CacheType, hash_funcs: HashFuncsDict | None = None):
274
280
  # Can't use types as the keys in the internal _hash_funcs because
275
281
  # we always remove user-written modules from memory when rerunning a
276
282
  # script in order to reload it and grab the latest code changes.
@@ -286,7 +292,7 @@ class _CacheFuncHasher:
286
292
  }
287
293
  else:
288
294
  self._hash_funcs = {}
289
- self._hashes: Dict[Any, bytes] = {}
295
+ self._hashes: dict[Any, bytes] = {}
290
296
 
291
297
  # The number of the bytes in the hash.
292
298
  self.size = 0
@@ -344,7 +350,9 @@ class _CacheFuncHasher:
344
350
 
345
351
  h = hashlib.new("md5", **HASHLIB_KWARGS)
346
352
 
347
- if isinstance(obj, unittest.mock.Mock):
353
+ if type_util.is_type(obj, "unittest.mock.Mock") or type_util.is_type(
354
+ obj, "unittest.mock.MagicMock"
355
+ ):
348
356
  # Mock objects can appear to be infinitely
349
357
  # deep, so we don't try to hash them at all.
350
358
  return self.to_bytes(id(obj))
@@ -57,8 +57,7 @@ from __future__ import annotations
57
57
 
58
58
  from abc import abstractmethod
59
59
  from dataclasses import dataclass
60
-
61
- from typing_extensions import Literal, Protocol
60
+ from typing import Literal, Protocol
62
61
 
63
62
 
64
63
  class CacheStorageError(Exception):
@@ -61,6 +61,7 @@ from __future__ import annotations
61
61
  import math
62
62
  import os
63
63
  import shutil
64
+ from typing import Final
64
65
 
65
66
  from streamlit import util
66
67
  from streamlit.file_util import get_streamlit_file_path, streamlit_read, streamlit_write
@@ -76,16 +77,16 @@ from streamlit.runtime.caching.storage.in_memory_cache_storage_wrapper import (
76
77
  InMemoryCacheStorageWrapper,
77
78
  )
78
79
 
80
+ _LOGGER: Final = get_logger(__name__)
81
+
79
82
  # Streamlit directory where persisted @st.cache_data objects live.
80
83
  # (This is the same directory that @st.cache persisted objects live.
81
84
  # But @st.cache_data uses a different extension, so they don't overlap.)
82
- _CACHE_DIR_NAME = "cache"
85
+ _CACHE_DIR_NAME: Final = "cache"
83
86
 
84
87
  # The extension for our persisted @st.cache_data objects.
85
88
  # (`@st.cache_data` was originally called `@st.memo`)
86
- _CACHED_FILE_EXTENSION = "memo"
87
-
88
- _LOGGER = get_logger(__name__)
89
+ _CACHED_FILE_EXTENSION: Final = "memo"
89
90
 
90
91
 
91
92
  class LocalDiskCacheStorageManager(CacheStorageManager):
@@ -168,7 +169,7 @@ class LocalDiskCacheStorage(CacheStorage):
168
169
  # Clean up file so we don't leave zero byte files.
169
170
  try:
170
171
  os.remove(path)
171
- except (FileNotFoundError, IOError, OSError):
172
+ except (FileNotFoundError, OSError):
172
173
  # If we can't remove the file, it's not a big deal.
173
174
  pass
174
175
  raise CacheStorageError("Unable to write to cache") from e
@@ -14,13 +14,10 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- import importlib
18
17
  import os
19
18
  import re
20
19
  from datetime import timedelta
21
- from typing import Any, Dict, Type, TypeVar, overload
22
-
23
- from typing_extensions import Final, Literal
20
+ from typing import Any, Final, Literal, TypeVar, overload
24
21
 
25
22
  from streamlit.connections import (
26
23
  BaseConnection,
@@ -45,7 +42,7 @@ FIRST_PARTY_CONNECTIONS = {
45
42
  "sql": SQLConnection,
46
43
  }
47
44
  MODULE_EXTRACTION_REGEX = re.compile(r"No module named \'(.+)\'")
48
- MODULES_TO_PYPI_PACKAGES: Final[Dict[str, str]] = {
45
+ MODULES_TO_PYPI_PACKAGES: Final[dict[str, str]] = {
49
46
  "MySQLdb": "mysqlclient",
50
47
  "psycopg2": "psycopg2-binary",
51
48
  "sqlalchemy": "sqlalchemy",
@@ -63,7 +60,7 @@ ConnectionClass = TypeVar("ConnectionClass", bound=BaseConnection[Any])
63
60
  @gather_metrics("connection")
64
61
  def _create_connection(
65
62
  name: str,
66
- connection_class: Type[ConnectionClass],
63
+ connection_class: type[ConnectionClass],
67
64
  max_entries: int | None = None,
68
65
  ttl: float | timedelta | None = None,
69
66
  **kwargs,
@@ -77,7 +74,7 @@ def _create_connection(
77
74
  """
78
75
 
79
76
  def __create_connection(
80
- name: str, connection_class: Type[ConnectionClass], **kwargs
77
+ name: str, connection_class: type[ConnectionClass], **kwargs
81
78
  ) -> ConnectionClass:
82
79
  return connection_class(connection_name=name, **kwargs)
83
80
 
@@ -184,7 +181,7 @@ def connection_factory(
184
181
  @overload
185
182
  def connection_factory(
186
183
  name: str,
187
- type: Type[ConnectionClass],
184
+ type: type[ConnectionClass],
188
185
  max_entries: int | None = None,
189
186
  ttl: float | timedelta | None = None,
190
187
  **kwargs,
@@ -312,6 +309,9 @@ def connection_factory(
312
309
  if "." in connection_class:
313
310
  parts = connection_class.split(".")
314
311
  classname = parts.pop()
312
+
313
+ import importlib
314
+
315
315
  connection_module = importlib.import_module(".".join(parts))
316
316
  connection_class = getattr(connection_module, classname)
317
317
  else: