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
@@ -39,7 +39,7 @@ from __future__ import annotations
39
39
 
40
40
  import os
41
41
  import threading
42
- from typing import Callable, Dict, Final, cast
42
+ from typing import Callable, Final, cast
43
43
 
44
44
  from blinker import ANY, Signal
45
45
  from watchdog import events
@@ -109,13 +109,13 @@ class EventBasedPathWatcher:
109
109
  path_watcher.stop_watching_path(self._path, self._on_changed)
110
110
 
111
111
 
112
- class _MultiPathWatcher(object):
112
+ class _MultiPathWatcher:
113
113
  """Watches multiple paths."""
114
114
 
115
- _singleton: "_MultiPathWatcher" | None = None
115
+ _singleton: _MultiPathWatcher | None = None
116
116
 
117
117
  @classmethod
118
- def get_singleton(cls) -> "_MultiPathWatcher":
118
+ def get_singleton(cls) -> _MultiPathWatcher:
119
119
  """Return the singleton _MultiPathWatcher object.
120
120
 
121
121
  Instantiates one if necessary.
@@ -127,18 +127,18 @@ class _MultiPathWatcher(object):
127
127
  return cast("_MultiPathWatcher", _MultiPathWatcher._singleton)
128
128
 
129
129
  # Don't allow constructor to be called more than once.
130
- def __new__(cls) -> "_MultiPathWatcher":
130
+ def __new__(cls) -> _MultiPathWatcher:
131
131
  """Constructor."""
132
132
  if _MultiPathWatcher._singleton is not None:
133
133
  raise RuntimeError("Use .get_singleton() instead")
134
- return super(_MultiPathWatcher, cls).__new__(cls)
134
+ return super().__new__(cls)
135
135
 
136
136
  def __init__(self) -> None:
137
137
  """Constructor."""
138
138
  _MultiPathWatcher._singleton = self
139
139
 
140
140
  # Map of folder_to_watch -> _FolderEventHandler.
141
- self._folder_handlers: Dict[str, _FolderEventHandler] = {}
141
+ self._folder_handlers: dict[str, _FolderEventHandler] = {}
142
142
 
143
143
  # Used for mutation of _folder_handlers dict
144
144
  self._lock = threading.Lock()
@@ -218,7 +218,7 @@ class _MultiPathWatcher(object):
218
218
  self._observer.join(timeout=5)
219
219
 
220
220
 
221
- class WatchedPath(object):
221
+ class WatchedPath:
222
222
  """Emits notifications when a single path is modified."""
223
223
 
224
224
  def __init__(
@@ -254,8 +254,8 @@ class _FolderEventHandler(events.FileSystemEventHandler):
254
254
  """
255
255
 
256
256
  def __init__(self) -> None:
257
- super(_FolderEventHandler, self).__init__()
258
- self._watched_paths: Dict[str, WatchedPath] = {}
257
+ super().__init__()
258
+ self._watched_paths: dict[str, WatchedPath] = {}
259
259
  self._lock = threading.Lock() # for watched_paths mutations
260
260
  self.watch: ObservedWatch | None = None
261
261
 
@@ -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
@@ -51,11 +50,14 @@ from streamlit.web.server.routes import (
51
50
  MessageCacheHandler,
52
51
  StaticFileHandler,
53
52
  )
54
- from streamlit.web.server.server_util import make_url_path_regex
53
+ from streamlit.web.server.server_util import DEVELOPMENT_PORT, make_url_path_regex
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,
@@ -183,23 +185,30 @@ def start_listening_tcp_socket(http_server: HTTPServer) -> None:
183
185
  address = config.get_option("server.address")
184
186
  port = config.get_option("server.port")
185
187
 
188
+ if int(port) == DEVELOPMENT_PORT:
189
+ _LOGGER.warning(
190
+ "Port %s is reserved for internal development. "
191
+ "It is strongly recommended to select an alternative port "
192
+ "for `server.port`.",
193
+ DEVELOPMENT_PORT,
194
+ )
195
+
186
196
  try:
187
197
  http_server.listen(port, address)
188
198
  break # It worked! So let's break out of the loop.
189
199
 
190
- except (OSError, socket.error) as e:
200
+ except OSError as e:
191
201
  if e.errno == errno.EADDRINUSE:
192
202
  if server_port_is_manually_set():
193
- LOGGER.error("Port %s is already in use", port)
203
+ _LOGGER.error("Port %s is already in use", port)
194
204
  sys.exit(1)
195
205
  else:
196
- LOGGER.debug(
206
+ _LOGGER.debug(
197
207
  "Port %s already in use, trying to use the next one.", port
198
208
  )
199
209
  port += 1
200
- # Save port 3000 because it is used for the development
201
- # server in the front end.
202
- if port == 3000:
210
+ # Don't use the development port here:
211
+ if port == DEVELOPMENT_PORT:
203
212
  port += 1
204
213
 
205
214
  config.set_option(
@@ -255,13 +264,13 @@ class Server:
255
264
  When this returns, Streamlit is ready to accept new sessions.
256
265
  """
257
266
 
258
- LOGGER.debug("Starting server...")
267
+ _LOGGER.debug("Starting server...")
259
268
 
260
269
  app = self._create_app()
261
270
  start_listening(app)
262
271
 
263
272
  port = config.get_option("server.port")
264
- LOGGER.debug("Server started on port %s", port)
273
+ _LOGGER.debug("Server started on port %s", port)
265
274
 
266
275
  await self._runtime.start()
267
276
 
@@ -274,7 +283,7 @@ class Server:
274
283
  """Create our tornado web app."""
275
284
  base = config.get_option("server.baseUrlPath")
276
285
 
277
- routes: List[Any] = [
286
+ routes: list[Any] = [
278
287
  (
279
288
  make_url_path_regex(base, STREAM_ENDPOINT),
280
289
  BrowserWebSocketHandler,
@@ -347,10 +356,10 @@ class Server:
347
356
  )
348
357
 
349
358
  if config.get_option("global.developmentMode"):
350
- LOGGER.debug("Serving static content from the Node dev server")
359
+ _LOGGER.debug("Serving static content from the Node dev server")
351
360
  else:
352
361
  static_path = file_util.get_static_dir()
353
- LOGGER.debug("Serving static content from %s", static_path)
362
+ _LOGGER.debug("Serving static content from %s", static_path)
354
363
 
355
364
  routes.extend(
356
365
  [
@@ -360,14 +369,12 @@ class Server:
360
369
  {
361
370
  "path": "%s/" % static_path,
362
371
  "default_filename": "index.html",
363
- "get_pages": lambda: set(
364
- [
365
- page_info["page_name"]
366
- for page_info in source_util.get_pages(
367
- self.main_script_path
368
- ).values()
369
- ]
370
- ),
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
+ },
371
378
  },
372
379
  ),
373
380
  (make_url_path_regex(base, trailing_slash=False), AddSlashHandler),