streamlit-nightly 1.31.2.dev20240213__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 (153) 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 +8 -19
  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/storage/cache_storage_protocol.py +1 -2
  95. streamlit/runtime/caching/storage/local_disk_cache_storage.py +6 -5
  96. streamlit/runtime/connection_factory.py +8 -8
  97. streamlit/runtime/forward_msg_cache.py +20 -18
  98. streamlit/runtime/forward_msg_queue.py +8 -9
  99. streamlit/runtime/legacy_caching/caching.py +32 -42
  100. streamlit/runtime/media_file_manager.py +16 -14
  101. streamlit/runtime/media_file_storage.py +8 -8
  102. streamlit/runtime/memory_media_file_storage.py +12 -14
  103. streamlit/runtime/memory_session_storage.py +4 -3
  104. streamlit/runtime/memory_uploaded_file_manager.py +9 -10
  105. streamlit/runtime/metrics_util.py +20 -20
  106. streamlit/runtime/runtime.py +25 -27
  107. streamlit/runtime/runtime_util.py +5 -3
  108. streamlit/runtime/script_data.py +2 -0
  109. streamlit/runtime/scriptrunner/magic.py +17 -11
  110. streamlit/runtime/scriptrunner/magic_funcs.py +2 -0
  111. streamlit/runtime/scriptrunner/script_requests.py +6 -4
  112. streamlit/runtime/scriptrunner/script_run_context.py +17 -17
  113. streamlit/runtime/scriptrunner/script_runner.py +7 -5
  114. streamlit/runtime/secrets.py +4 -6
  115. streamlit/runtime/session_manager.py +14 -14
  116. streamlit/runtime/state/common.py +5 -4
  117. streamlit/runtime/state/query_params.py +8 -6
  118. streamlit/runtime/state/query_params_proxy.py +7 -5
  119. streamlit/runtime/state/safe_session_state.py +7 -5
  120. streamlit/runtime/state/session_state.py +3 -4
  121. streamlit/runtime/state/session_state_proxy.py +5 -5
  122. streamlit/runtime/state/widgets.py +20 -18
  123. streamlit/runtime/uploaded_file_manager.py +6 -5
  124. streamlit/runtime/websocket_session_manager.py +14 -14
  125. streamlit/source_util.py +13 -11
  126. streamlit/string_util.py +13 -9
  127. streamlit/temporary_directory.py +3 -1
  128. streamlit/testing/v1/element_tree.py +1 -2
  129. streamlit/type_util.py +21 -25
  130. streamlit/url_util.py +6 -4
  131. streamlit/user_info.py +8 -6
  132. streamlit/util.py +23 -37
  133. streamlit/watcher/event_based_path_watcher.py +10 -10
  134. streamlit/watcher/local_sources_watcher.py +15 -13
  135. streamlit/watcher/path_watcher.py +0 -3
  136. streamlit/watcher/polling_path_watcher.py +9 -8
  137. streamlit/watcher/util.py +3 -2
  138. streamlit/web/cache_storage_manager_config.py +2 -0
  139. streamlit/web/server/app_static_file_handler.py +6 -5
  140. streamlit/web/server/browser_websocket_handler.py +10 -8
  141. streamlit/web/server/component_request_handler.py +7 -4
  142. streamlit/web/server/media_file_handler.py +5 -4
  143. streamlit/web/server/routes.py +6 -3
  144. streamlit/web/server/server.py +31 -31
  145. streamlit/web/server/server_util.py +4 -2
  146. streamlit/web/server/upload_file_request_handler.py +7 -8
  147. streamlit/web/server/websocket_headers.py +2 -2
  148. {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/METADATA +1 -1
  149. {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/RECORD +153 -153
  150. {streamlit_nightly-1.31.2.dev20240213.data → streamlit_nightly-1.31.2.dev20240214.data}/scripts/streamlit.cmd +0 -0
  151. {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/WHEEL +0 -0
  152. {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/entry_points.txt +0 -0
  153. {streamlit_nightly-1.31.2.dev20240213.dist-info → streamlit_nightly-1.31.2.dev20240214.dist-info}/top_level.txt +0 -0
@@ -12,11 +12,13 @@
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 collections
16
18
  import os
17
19
  import sys
18
20
  import types
19
- from typing import Callable, Dict, List, Optional, Set
21
+ from typing import Callable, Final
20
22
 
21
23
  from streamlit import config, file_util
22
24
  from streamlit.folder_black_list import FolderBlackList
@@ -27,7 +29,7 @@ from streamlit.watcher.path_watcher import (
27
29
  get_default_path_watcher_class,
28
30
  )
29
31
 
30
- LOGGER = get_logger(__name__)
32
+ _LOGGER: Final = get_logger(__name__)
31
33
 
32
34
  WatchedModule = collections.namedtuple("WatchedModule", ["watcher", "module_name"])
33
35
 
@@ -40,23 +42,23 @@ class LocalSourcesWatcher:
40
42
  def __init__(self, main_script_path: str):
41
43
  self._main_script_path = os.path.abspath(main_script_path)
42
44
  self._script_folder = os.path.dirname(self._main_script_path)
43
- self._on_file_changed: List[Callable[[str], None]] = []
45
+ self._on_file_changed: list[Callable[[str], None]] = []
44
46
  self._is_closed = False
45
- self._cached_sys_modules: Set[str] = set()
47
+ self._cached_sys_modules: set[str] = set()
46
48
 
47
49
  # Blacklist for folders that should not be watched
48
50
  self._folder_black_list = FolderBlackList(
49
51
  config.get_option("server.folderWatchBlacklist")
50
52
  )
51
53
 
52
- self._watched_modules: Dict[str, WatchedModule] = {}
53
- self._watched_pages: Set[str] = set()
54
+ self._watched_modules: dict[str, WatchedModule] = {}
55
+ self._watched_pages: set[str] = set()
54
56
 
55
57
  self.update_watched_pages()
56
58
 
57
59
  def update_watched_pages(self) -> None:
58
60
  old_watched_pages = self._watched_pages
59
- new_pages_paths: Set[str] = set()
61
+ new_pages_paths: set[str] = set()
60
62
 
61
63
  for page_info in get_pages(self._main_script_path).values():
62
64
  new_pages_paths.add(page_info["script_path"])
@@ -77,7 +79,7 @@ class LocalSourcesWatcher:
77
79
 
78
80
  def on_file_changed(self, filepath):
79
81
  if filepath not in self._watched_modules:
80
- LOGGER.error("Received event for non-watched file: %s", filepath)
82
+ _LOGGER.error("Received event for non-watched file: %s", filepath)
81
83
  return
82
84
 
83
85
  # Workaround:
@@ -159,17 +161,17 @@ class LocalSourcesWatcher:
159
161
  self._cached_sys_modules = set(sys.modules)
160
162
  self._register_necessary_watchers(modules_paths)
161
163
 
162
- def _register_necessary_watchers(self, module_paths: Dict[str, Set[str]]) -> None:
164
+ def _register_necessary_watchers(self, module_paths: dict[str, set[str]]) -> None:
163
165
  for name, paths in module_paths.items():
164
166
  for path in paths:
165
167
  if self._file_should_be_watched(path):
166
168
  self._register_watcher(path, name)
167
169
 
168
- def _exclude_blacklisted_paths(self, paths: Set[str]) -> Set[str]:
170
+ def _exclude_blacklisted_paths(self, paths: set[str]) -> set[str]:
169
171
  return {p for p in paths if not self._folder_black_list.is_blacklisted(p)}
170
172
 
171
173
 
172
- def get_module_paths(module: types.ModuleType) -> Set[str]:
174
+ def get_module_paths(module: types.ModuleType) -> set[str]:
173
175
  paths_extractors = [
174
176
  # https://docs.python.org/3/reference/datamodel.html
175
177
  # __file__ is the pathname of the file from which the module was loaded
@@ -202,7 +204,7 @@ def get_module_paths(module: types.ModuleType) -> Set[str]:
202
204
  # Some modules might not have __file__ or __spec__ attributes.
203
205
  pass
204
206
  except Exception as e:
205
- LOGGER.warning(f"Examining the path of {module.__name__} raised: {e}")
207
+ _LOGGER.warning(f"Examining the path of {module.__name__} raised: {e}")
206
208
 
207
209
  all_paths.update(
208
210
  [os.path.abspath(str(p)) for p in potential_paths if _is_valid_path(p)]
@@ -210,5 +212,5 @@ def get_module_paths(module: types.ModuleType) -> Set[str]:
210
212
  return all_paths
211
213
 
212
214
 
213
- def _is_valid_path(path: Optional[str]) -> bool:
215
+ def _is_valid_path(path: str | None) -> bool:
214
216
  return isinstance(path, str) and (os.path.isfile(path) or os.path.isdir(path))
@@ -18,11 +18,8 @@ from typing import Callable, Type, Union
18
18
 
19
19
  import streamlit.watcher
20
20
  from streamlit import cli_util, config, env_util
21
- from streamlit.logger import get_logger
22
21
  from streamlit.watcher.polling_path_watcher import PollingPathWatcher
23
22
 
24
- LOGGER = get_logger(__name__)
25
-
26
23
 
27
24
  # local_sources_watcher.py caches the return value of
28
25
  # get_default_path_watcher_class(), so it needs to differentiate between the
@@ -14,19 +14,20 @@
14
14
 
15
15
  """A class that watches a given path via polling."""
16
16
 
17
+ from __future__ import annotations
18
+
17
19
  import time
18
20
  from concurrent.futures import ThreadPoolExecutor
19
- from typing import Callable, Optional
21
+ from typing import Callable, Final
20
22
 
21
23
  from streamlit.logger import get_logger
22
24
  from streamlit.util import repr_
23
25
  from streamlit.watcher import util
24
26
 
25
- LOGGER = get_logger(__name__)
26
-
27
+ _LOGGER: Final = get_logger(__name__)
27
28
 
28
- _MAX_WORKERS = 4
29
- _POLLING_PERIOD_SECS = 0.2
29
+ _MAX_WORKERS: Final = 4
30
+ _POLLING_PERIOD_SECS: Final = 0.2
30
31
 
31
32
 
32
33
  class PollingPathWatcher:
@@ -41,14 +42,14 @@ class PollingPathWatcher:
41
42
  This is a no-op, and exists for interface parity with
42
43
  EventBasedPathWatcher.
43
44
  """
44
- LOGGER.debug("Watcher closed")
45
+ _LOGGER.debug("Watcher closed")
45
46
 
46
47
  def __init__(
47
48
  self,
48
49
  path: str,
49
50
  on_changed: Callable[[str], None],
50
51
  *, # keyword-only arguments:
51
- glob_pattern: Optional[str] = None,
52
+ glob_pattern: str | None = None,
52
53
  allow_nonexistent: bool = False,
53
54
  ) -> None:
54
55
  """Constructor.
@@ -113,7 +114,7 @@ class PollingPathWatcher:
113
114
 
114
115
  self._md5 = md5
115
116
 
116
- LOGGER.debug("Change detected: %s", self._path)
117
+ _LOGGER.debug("Change detected: %s", self._path)
117
118
  self._on_changed(self._path)
118
119
 
119
120
  self._schedule()
streamlit/watcher/util.py CHANGED
@@ -18,11 +18,12 @@ These are functions that only make sense within the watcher. In particular,
18
18
  functions that use streamlit.config can go here to avoid a dependency cycle.
19
19
  """
20
20
 
21
+ from __future__ import annotations
22
+
21
23
  import hashlib
22
24
  import os
23
25
  import time
24
26
  from pathlib import Path
25
- from typing import Optional
26
27
 
27
28
  from streamlit.util import HASHLIB_KWARGS
28
29
 
@@ -36,7 +37,7 @@ _RETRY_WAIT_SECS = 0.1
36
37
  def calc_md5_with_blocking_retries(
37
38
  path: str,
38
39
  *, # keyword-only arguments:
39
- glob_pattern: Optional[str] = None,
40
+ glob_pattern: str | None = None,
40
41
  allow_nonexistent: bool = False,
41
42
  ) -> str:
42
43
  """Calculate the MD5 checksum of a given path.
@@ -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
  from streamlit.runtime.caching.storage import CacheStorageManager
16
18
  from streamlit.runtime.caching.storage.local_disk_cache_storage import (
17
19
  LocalDiskCacheStorageManager,
@@ -12,17 +12,18 @@
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 mimetypes
16
18
  import os
17
19
  from pathlib import Path
18
- from typing import Optional
20
+ from typing import Final
19
21
 
20
22
  import tornado.web
21
23
 
22
24
  from streamlit.logger import get_logger
23
25
 
24
- _LOGGER = get_logger(__name__)
25
-
26
+ _LOGGER: Final = get_logger(__name__)
26
27
 
27
28
  # We agreed on these limitations for the initial release of static file sharing,
28
29
  # based on security concerns from the SiS and Community Cloud teams
@@ -34,11 +35,11 @@ SAFE_APP_STATIC_FILE_EXTENSIONS = (".jpg", ".jpeg", ".png", ".gif", ".webp")
34
35
 
35
36
 
36
37
  class AppStaticFileHandler(tornado.web.StaticFileHandler):
37
- def initialize(self, path: str, default_filename: Optional[str] = None) -> None:
38
+ def initialize(self, path: str, default_filename: str | None = None) -> None:
38
39
  super().initialize(path, default_filename)
39
40
  mimetypes.add_type("image/webp", ".webp")
40
41
 
41
- def validate_absolute_path(self, root: str, absolute_path: str) -> Optional[str]:
42
+ def validate_absolute_path(self, root: str, absolute_path: str) -> str | None:
42
43
  full_path = os.path.realpath(absolute_path)
43
44
 
44
45
  if os.path.isdir(full_path):
@@ -12,10 +12,12 @@
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 base64
16
18
  import binascii
17
19
  import json
18
- from typing import Any, Awaitable, Dict, List, Optional, Union
20
+ from typing import Any, Awaitable, Final
19
21
 
20
22
  import tornado.concurrent
21
23
  import tornado.locks
@@ -23,7 +25,6 @@ import tornado.netutil
23
25
  import tornado.web
24
26
  import tornado.websocket
25
27
  from tornado.websocket import WebSocketHandler
26
- from typing_extensions import Final
27
28
 
28
29
  from streamlit import config
29
30
  from streamlit.logger import get_logger
@@ -41,7 +42,7 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
41
42
 
42
43
  def initialize(self, runtime: Runtime) -> None:
43
44
  self._runtime = runtime
44
- self._session_id: Optional[str] = None
45
+ self._session_id: str | None = None
45
46
  # The XSRF cookie is normally set when xsrf_form_html is used, but in a
46
47
  # pure-Javascript application that does not use any regular forms we just
47
48
  # need to read the self.xsrf_token manually to set the cookie as a side
@@ -61,7 +62,7 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
61
62
  except tornado.websocket.WebSocketClosedError as e:
62
63
  raise SessionClientDisconnectedError from e
63
64
 
64
- def select_subprotocol(self, subprotocols: List[str]) -> Optional[str]:
65
+ def select_subprotocol(self, subprotocols: list[str]) -> str | None:
65
66
  """Return the first subprotocol in the given list.
66
67
 
67
68
  This method is used by Tornado to select a protocol when the
@@ -87,7 +88,7 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
87
88
 
88
89
  return None
89
90
 
90
- def open(self, *args, **kwargs) -> Optional[Awaitable[None]]:
91
+ def open(self, *args, **kwargs) -> Awaitable[None] | None:
91
92
  # Extract user info from the X-Streamlit-User header
92
93
  is_public_cloud_app = False
93
94
 
@@ -100,7 +101,8 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
100
101
  except (KeyError, binascii.Error, json.decoder.JSONDecodeError):
101
102
  email = "test@example.com"
102
103
 
103
- user_info: Dict[str, Optional[str]] = dict()
104
+ user_info: dict[str, str | None] = dict()
105
+
104
106
  if is_public_cloud_app:
105
107
  user_info["email"] = None
106
108
  else:
@@ -135,7 +137,7 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
135
137
  self._runtime.disconnect_session(self._session_id)
136
138
  self._session_id = None
137
139
 
138
- def get_compression_options(self) -> Optional[Dict[Any, Any]]:
140
+ def get_compression_options(self) -> dict[Any, Any] | None:
139
141
  """Enable WebSocket compression.
140
142
 
141
143
  Returning an empty dict enables websocket compression. Returning
@@ -147,7 +149,7 @@ class BrowserWebSocketHandler(WebSocketHandler, SessionClient):
147
149
  return {}
148
150
  return None
149
151
 
150
- def on_message(self, payload: Union[str, bytes]) -> None:
152
+ def on_message(self, payload: str | bytes) -> None:
151
153
  if not self._session_id:
152
154
  return
153
155
 
@@ -12,8 +12,11 @@
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 mimetypes
16
18
  import os
19
+ from typing import Final
17
20
 
18
21
  import tornado.web
19
22
 
@@ -21,7 +24,7 @@ import streamlit.web.server.routes
21
24
  from streamlit.components.v1.components import ComponentRegistry
22
25
  from streamlit.logger import get_logger
23
26
 
24
- _LOGGER = get_logger(__name__)
27
+ _LOGGER: Final = get_logger(__name__)
25
28
 
26
29
 
27
30
  class ComponentRequestHandler(tornado.web.RequestHandler):
@@ -63,7 +66,7 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
63
66
 
64
67
  self.set_extra_headers(path)
65
68
 
66
- def set_extra_headers(self, path) -> None:
69
+ def set_extra_headers(self, path: str) -> None:
67
70
  """Disable cache for HTML files.
68
71
 
69
72
  Other assets like JS and CSS are suffixed with their hash, so they can
@@ -86,7 +89,7 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
86
89
  self.finish()
87
90
 
88
91
  @staticmethod
89
- def get_content_type(abspath) -> str:
92
+ def get_content_type(abspath: str) -> str:
90
93
  """Returns the ``Content-Type`` header to be used for this request.
91
94
  From tornado.web.StaticFileHandler.
92
95
  """
@@ -108,4 +111,4 @@ class ComponentRequestHandler(tornado.web.RequestHandler):
108
111
  @staticmethod
109
112
  def get_url(file_id: str) -> str:
110
113
  """Return the URL for a component file with the given ID."""
111
- return "components/{}".format(file_id)
114
+ return f"components/{file_id}"
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Optional
15
+ from __future__ import annotations
16
+
16
17
  from urllib.parse import quote
17
18
 
18
19
  import tornado.web
@@ -67,11 +68,11 @@ class MediaFileHandler(tornado.web.StaticFileHandler):
67
68
  # Check that the value can be encoded in latin1. Latin1 is
68
69
  # the default encoding for headers.
69
70
  filename.encode("latin1")
70
- file_expr = 'filename="{}"'.format(filename)
71
+ file_expr = f'filename="{filename}"'
71
72
  except UnicodeEncodeError:
72
73
  # RFC5987 syntax.
73
74
  # See: https://datatracker.ietf.org/doc/html/rfc5987
74
- file_expr = "filename*=utf-8''{}".format(quote(filename))
75
+ file_expr = f"filename*=utf-8''{quote(filename)}"
75
76
 
76
77
  self.set_header("Content-Disposition", f"attachment; {file_expr}")
77
78
 
@@ -112,7 +113,7 @@ class MediaFileHandler(tornado.web.StaticFileHandler):
112
113
 
113
114
  @classmethod
114
115
  def get_content(
115
- cls, abspath: str, start: Optional[int] = None, end: Optional[int] = None
116
+ cls, abspath: str, start: int | None = None, end: int | None = None
116
117
  ):
117
118
  _LOGGER.debug("MediaFileHandler: GET %s", abspath)
118
119
 
@@ -12,7 +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 os
18
+ from typing import Final
16
19
 
17
20
  import tornado.web
18
21
 
@@ -21,10 +24,10 @@ from streamlit.logger import get_logger
21
24
  from streamlit.runtime.runtime_util import serialize_forward_msg
22
25
  from streamlit.web.server.server_util import emit_endpoint_deprecation_notice
23
26
 
24
- _LOGGER = get_logger(__name__)
27
+ _LOGGER: Final = get_logger(__name__)
25
28
 
26
29
 
27
- def allow_cross_origin_requests():
30
+ def allow_cross_origin_requests() -> bool:
28
31
  """True if cross-origin requests are allowed.
29
32
 
30
33
  We only allow cross-origin requests when CORS protection has been disabled
@@ -43,7 +46,7 @@ class StaticFileHandler(tornado.web.StaticFileHandler):
43
46
 
44
47
  super().initialize(path=path, default_filename=default_filename)
45
48
 
46
- def set_extra_headers(self, path):
49
+ def set_extra_headers(self, path: str) -> None:
47
50
  """Disable cache for HTML files.
48
51
 
49
52
  Other assets like JS and CSS are suffixed with their hash, so they can
@@ -12,14 +12,14 @@
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 errno
16
18
  import logging
17
19
  import os
18
- import socket
19
- import ssl
20
20
  import sys
21
21
  from pathlib import Path
22
- from typing import Any, Awaitable, List, Optional, Union
22
+ from typing import TYPE_CHECKING, Any, Awaitable, Final
23
23
 
24
24
  import tornado.concurrent
25
25
  import tornado.locks
@@ -27,7 +27,6 @@ import tornado.netutil
27
27
  import tornado.web
28
28
  import tornado.websocket
29
29
  from tornado.httpserver import HTTPServer
30
- from typing_extensions import Final
31
30
 
32
31
  from streamlit import cli_util, config, file_util, source_util, util
33
32
  from streamlit.components.v1.components import ComponentRegistry
@@ -55,7 +54,10 @@ from streamlit.web.server.server_util import DEVELOPMENT_PORT, make_url_path_reg
55
54
  from streamlit.web.server.stats_request_handler import StatsRequestHandler
56
55
  from streamlit.web.server.upload_file_request_handler import UploadFileRequestHandler
57
56
 
58
- LOGGER = get_logger(__name__)
57
+ if TYPE_CHECKING:
58
+ from ssl import SSLContext
59
+
60
+ _LOGGER: Final = get_logger(__name__)
59
61
 
60
62
  TORNADO_SETTINGS = {
61
63
  # Gzip HTTP responses.
@@ -74,11 +76,11 @@ TORNADO_SETTINGS = {
74
76
 
75
77
  # When server.port is not available it will look for the next available port
76
78
  # up to MAX_PORT_SEARCH_RETRIES.
77
- MAX_PORT_SEARCH_RETRIES = 100
79
+ MAX_PORT_SEARCH_RETRIES: Final = 100
78
80
 
79
81
  # When server.address starts with this prefix, the server will bind
80
82
  # to an unix socket.
81
- UNIX_SOCKET_PREFIX = "unix://"
83
+ UNIX_SOCKET_PREFIX: Final = "unix://"
82
84
 
83
85
  MEDIA_ENDPOINT: Final = "/media"
84
86
  UPLOAD_FILE_ENDPOINT: Final = "/_stcore/upload_file"
@@ -128,11 +130,9 @@ def start_listening(app: tornado.web.Application) -> None:
128
130
  start_listening_tcp_socket(http_server)
129
131
 
130
132
 
131
- def _get_ssl_options(
132
- cert_file: Optional[str], key_file: Optional[str]
133
- ) -> Union[ssl.SSLContext, None]:
133
+ def _get_ssl_options(cert_file: str | None, key_file: str | None) -> SSLContext | None:
134
134
  if bool(cert_file) != bool(key_file):
135
- LOGGER.error(
135
+ _LOGGER.error(
136
136
  "Options 'server.sslCertFile' and 'server.sslKeyFile' must "
137
137
  "be set together. Set missing options or delete existing options."
138
138
  )
@@ -142,12 +142,14 @@ def _get_ssl_options(
142
142
  # sufficiently user-friendly
143
143
  # FileNotFoundError: [Errno 2] No such file or directory
144
144
  if not Path(cert_file).exists():
145
- LOGGER.error("Cert file '%s' does not exist.", cert_file)
145
+ _LOGGER.error("Cert file '%s' does not exist.", cert_file)
146
146
  sys.exit(1)
147
147
  if not Path(key_file).exists():
148
- LOGGER.error("Key file '%s' does not exist.", key_file)
148
+ _LOGGER.error("Key file '%s' does not exist.", key_file)
149
149
  sys.exit(1)
150
150
 
151
+ import ssl
152
+
151
153
  ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
152
154
  # When the SSL certificate fails to load, an exception is raised as below,
153
155
  # but it is not sufficiently user-friendly.
@@ -155,7 +157,7 @@ def _get_ssl_options(
155
157
  try:
156
158
  ssl_ctx.load_cert_chain(cert_file, key_file)
157
159
  except ssl.SSLError:
158
- LOGGER.error(
160
+ _LOGGER.error(
159
161
  "Failed to load SSL certificate. Make sure "
160
162
  "cert file '%s' and key file '%s' are correct.",
161
163
  cert_file,
@@ -184,7 +186,7 @@ def start_listening_tcp_socket(http_server: HTTPServer) -> None:
184
186
  port = config.get_option("server.port")
185
187
 
186
188
  if int(port) == DEVELOPMENT_PORT:
187
- LOGGER.warning(
189
+ _LOGGER.warning(
188
190
  "Port %s is reserved for internal development. "
189
191
  "It is strongly recommended to select an alternative port "
190
192
  "for `server.port`.",
@@ -195,13 +197,13 @@ def start_listening_tcp_socket(http_server: HTTPServer) -> None:
195
197
  http_server.listen(port, address)
196
198
  break # It worked! So let's break out of the loop.
197
199
 
198
- except (OSError, socket.error) as e:
200
+ except OSError as e:
199
201
  if e.errno == errno.EADDRINUSE:
200
202
  if server_port_is_manually_set():
201
- LOGGER.error("Port %s is already in use", port)
203
+ _LOGGER.error("Port %s is already in use", port)
202
204
  sys.exit(1)
203
205
  else:
204
- LOGGER.debug(
206
+ _LOGGER.debug(
205
207
  "Port %s already in use, trying to use the next one.", port
206
208
  )
207
209
  port += 1
@@ -262,13 +264,13 @@ class Server:
262
264
  When this returns, Streamlit is ready to accept new sessions.
263
265
  """
264
266
 
265
- LOGGER.debug("Starting server...")
267
+ _LOGGER.debug("Starting server...")
266
268
 
267
269
  app = self._create_app()
268
270
  start_listening(app)
269
271
 
270
272
  port = config.get_option("server.port")
271
- LOGGER.debug("Server started on port %s", port)
273
+ _LOGGER.debug("Server started on port %s", port)
272
274
 
273
275
  await self._runtime.start()
274
276
 
@@ -281,7 +283,7 @@ class Server:
281
283
  """Create our tornado web app."""
282
284
  base = config.get_option("server.baseUrlPath")
283
285
 
284
- routes: List[Any] = [
286
+ routes: list[Any] = [
285
287
  (
286
288
  make_url_path_regex(base, STREAM_ENDPOINT),
287
289
  BrowserWebSocketHandler,
@@ -354,10 +356,10 @@ class Server:
354
356
  )
355
357
 
356
358
  if config.get_option("global.developmentMode"):
357
- LOGGER.debug("Serving static content from the Node dev server")
359
+ _LOGGER.debug("Serving static content from the Node dev server")
358
360
  else:
359
361
  static_path = file_util.get_static_dir()
360
- LOGGER.debug("Serving static content from %s", static_path)
362
+ _LOGGER.debug("Serving static content from %s", static_path)
361
363
 
362
364
  routes.extend(
363
365
  [
@@ -367,14 +369,12 @@ class Server:
367
369
  {
368
370
  "path": "%s/" % static_path,
369
371
  "default_filename": "index.html",
370
- "get_pages": lambda: set(
371
- [
372
- page_info["page_name"]
373
- for page_info in source_util.get_pages(
374
- self.main_script_path
375
- ).values()
376
- ]
377
- ),
372
+ "get_pages": lambda: {
373
+ page_info["page_name"]
374
+ for page_info in source_util.get_pages(
375
+ self.main_script_path
376
+ ).values()
377
+ },
378
378
  },
379
379
  ),
380
380
  (make_url_path_regex(base, trailing_slash=False), AddSlashHandler),
@@ -14,7 +14,9 @@
14
14
 
15
15
  """Server related utility functions"""
16
16
 
17
- from typing import Final, Optional
17
+ from __future__ import annotations
18
+
19
+ from typing import Final
18
20
  from urllib.parse import urljoin
19
21
 
20
22
  import tornado.web
@@ -67,7 +69,7 @@ def is_url_from_allowed_origins(url: str) -> bool:
67
69
  return False
68
70
 
69
71
 
70
- def _get_server_address_if_manually_set() -> Optional[str]:
72
+ def _get_server_address_if_manually_set() -> str | None:
71
73
  if config.is_manually_set("browser.serverAddress"):
72
74
  return url_util.get_hostname(config.get_option("browser.serverAddress"))
73
75
  return None
@@ -12,19 +12,18 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Callable, Dict, List
15
+ from __future__ import annotations
16
+
17
+ from typing import Any, Callable
16
18
 
17
19
  import tornado.httputil
18
20
  import tornado.web
19
21
 
20
22
  from streamlit import config
21
- from streamlit.logger import get_logger
22
23
  from streamlit.runtime.memory_uploaded_file_manager import MemoryUploadedFileManager
23
- from streamlit.runtime.uploaded_file_manager import UploadedFileManager, UploadedFileRec
24
+ from streamlit.runtime.uploaded_file_manager import UploadedFileRec
24
25
  from streamlit.web.server import routes, server_util
25
26
 
26
- LOGGER = get_logger(__name__)
27
-
28
27
 
29
28
  class UploadFileRequestHandler(tornado.web.RequestHandler):
30
29
  """Implements the POST /upload_file endpoint."""
@@ -84,8 +83,8 @@ class UploadFileRequestHandler(tornado.web.RequestHandler):
84
83
  def put(self, **kwargs):
85
84
  """Receive an uploaded file and add it to our UploadedFileManager."""
86
85
 
87
- args: Dict[str, List[bytes]] = {}
88
- files: Dict[str, List[Any]] = {}
86
+ args: dict[str, list[bytes]] = {}
87
+ files: dict[str, list[Any]] = {}
89
88
 
90
89
  session_id = self.path_kwargs["session_id"]
91
90
  file_id = self.path_kwargs["file_id"]
@@ -104,7 +103,7 @@ class UploadFileRequestHandler(tornado.web.RequestHandler):
104
103
  self.send_error(400, reason=str(e))
105
104
  return
106
105
 
107
- uploaded_files: List[UploadedFileRec] = []
106
+ uploaded_files: list[UploadedFileRec] = []
108
107
 
109
108
  for _, flist in files.items():
110
109
  for file in flist:
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Dict, Optional
15
+ from __future__ import annotations
16
16
 
17
17
  from streamlit import runtime
18
18
  from streamlit.runtime.metrics_util import gather_metrics
@@ -21,7 +21,7 @@ from streamlit.web.server.browser_websocket_handler import BrowserWebSocketHandl
21
21
 
22
22
 
23
23
  @gather_metrics("_get_websocket_headers")
24
- def _get_websocket_headers() -> Optional[Dict[str, str]]:
24
+ def _get_websocket_headers() -> dict[str, str] | None:
25
25
  """Return a copy of the HTTP request headers for the current session's
26
26
  WebSocket connection. If there's no active session, return None instead.
27
27