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
streamlit/source_util.py CHANGED
@@ -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 re
16
18
  import threading
17
19
  from pathlib import Path
18
- from typing import Any, Callable, Dict, Optional, Tuple, cast
20
+ from typing import Any, Callable, Final, cast
19
21
 
20
22
  from blinker import Signal
21
23
 
@@ -23,10 +25,10 @@ from streamlit.logger import get_logger
23
25
  from streamlit.string_util import extract_leading_emoji
24
26
  from streamlit.util import calc_md5
25
27
 
26
- LOGGER = get_logger(__name__)
28
+ _LOGGER: Final = get_logger(__name__)
27
29
 
28
30
 
29
- def open_python_file(filename):
31
+ def open_python_file(filename: str):
30
32
  """Open a read-only Python file taking proper care of its encoding.
31
33
 
32
34
  In Python 3, we would like all files to be opened with utf-8 encoding.
@@ -41,13 +43,13 @@ def open_python_file(filename):
41
43
  # found, opens as utf-8.
42
44
  return tokenize.open(filename)
43
45
  else:
44
- return open(filename, "r", encoding="utf-8")
46
+ return open(filename, encoding="utf-8")
45
47
 
46
48
 
47
49
  PAGE_FILENAME_REGEX = re.compile(r"([0-9]*)[_ -]*(.*)\.py")
48
50
 
49
51
 
50
- def page_sort_key(script_path: Path) -> Tuple[float, str]:
52
+ def page_sort_key(script_path: Path) -> tuple[float, str]:
51
53
  matches = re.findall(PAGE_FILENAME_REGEX, script_path.name)
52
54
 
53
55
  # Failing this assert should only be possible if script_path isn't a Python
@@ -63,7 +65,7 @@ def page_sort_key(script_path: Path) -> Tuple[float, str]:
63
65
  return (float(number), label)
64
66
 
65
67
 
66
- def page_icon_and_name(script_path: Path) -> Tuple[str, str]:
68
+ def page_icon_and_name(script_path: Path) -> tuple[str, str]:
67
69
  """Compute the icon and name of a page from its script path.
68
70
 
69
71
  This is *almost* the page name displayed in the nav UI, but it has
@@ -89,21 +91,21 @@ def page_icon_and_name(script_path: Path) -> Tuple[str, str]:
89
91
 
90
92
 
91
93
  _pages_cache_lock = threading.RLock()
92
- _cached_pages: Optional[Dict[str, Dict[str, str]]] = None
94
+ _cached_pages: dict[str, dict[str, str]] | None = None
93
95
  _on_pages_changed = Signal(doc="Emitted when the pages directory is changed")
94
96
 
95
97
 
96
- def invalidate_pages_cache():
98
+ def invalidate_pages_cache() -> None:
97
99
  global _cached_pages
98
100
 
99
- LOGGER.debug("Pages directory changed")
101
+ _LOGGER.debug("Pages directory changed")
100
102
  with _pages_cache_lock:
101
103
  _cached_pages = None
102
104
 
103
105
  _on_pages_changed.send()
104
106
 
105
107
 
106
- def get_pages(main_script_path_str: str) -> Dict[str, Dict[str, str]]:
108
+ def get_pages(main_script_path_str: str) -> dict[str, dict[str, str]]:
107
109
  global _cached_pages
108
110
 
109
111
  # Avoid taking the lock if the pages cache hasn't been invalidated.
@@ -162,7 +164,7 @@ def get_pages(main_script_path_str: str) -> Dict[str, Dict[str, str]]:
162
164
 
163
165
  def register_pages_changed_callback(
164
166
  callback: Callable[[str], None],
165
- ):
167
+ ) -> Callable[[], None]:
166
168
  def disconnect():
167
169
  _on_pages_changed.disconnect(callback)
168
170
 
streamlit/string_util.py CHANGED
@@ -16,14 +16,14 @@ from __future__ import annotations
16
16
 
17
17
  import re
18
18
  import textwrap
19
- from typing import TYPE_CHECKING, Any, Tuple, cast
19
+ from typing import TYPE_CHECKING, Any, Final, cast
20
20
 
21
21
  from streamlit.errors import StreamlitAPIException
22
22
 
23
23
  if TYPE_CHECKING:
24
24
  from streamlit.type_util import SupportsStr
25
25
 
26
- _ALPHANUMERIC_CHAR_REGEX = re.compile(r"^[a-zA-Z0-9_&\-\. ]+$")
26
+ _ALPHANUMERIC_CHAR_REGEX: Final = re.compile(r"^[a-zA-Z0-9_&\-\. ]+$")
27
27
 
28
28
 
29
29
  def decode_ascii(string: bytes) -> str:
@@ -31,7 +31,7 @@ def decode_ascii(string: bytes) -> str:
31
31
  return string.decode("ascii")
32
32
 
33
33
 
34
- def clean_text(text: "SupportsStr") -> str:
34
+ def clean_text(text: SupportsStr) -> str:
35
35
  """Convert an object to text, dedent it, and strip whitespace."""
36
36
  return textwrap.dedent(str(text)).strip()
37
37
 
@@ -67,7 +67,7 @@ def validate_emoji(maybe_emoji: str | None) -> str:
67
67
  )
68
68
 
69
69
 
70
- def extract_leading_emoji(text: str) -> Tuple[str, str]:
70
+ def extract_leading_emoji(text: str) -> tuple[str, str]:
71
71
  """Return a tuple containing the first emoji found in the given string and
72
72
  the rest of the string (minus an optional separator between the two).
73
73
  """
@@ -115,7 +115,9 @@ def escape_markdown(raw_string: str) -> str:
115
115
  return result
116
116
 
117
117
 
118
- TEXTCHARS = bytearray({7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7F})
118
+ TEXTCHARS: Final = bytearray(
119
+ {7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7F}
120
+ )
119
121
 
120
122
 
121
123
  def is_binary_string(inp: bytes) -> bool:
@@ -126,18 +128,20 @@ def is_binary_string(inp: bytes) -> bool:
126
128
 
127
129
  def simplify_number(num: int) -> str:
128
130
  """Simplifies number into Human readable format, returns str"""
129
- num_converted = float("{:.2g}".format(num))
131
+ num_converted = float(f"{num:.2g}")
130
132
  magnitude = 0
131
133
  while abs(num_converted) >= 1000:
132
134
  magnitude += 1
133
135
  num_converted /= 1000.0
134
136
  return "{}{}".format(
135
- "{:f}".format(num_converted).rstrip("0").rstrip("."),
137
+ f"{num_converted:f}".rstrip("0").rstrip("."),
136
138
  ["", "k", "m", "b", "t"][magnitude],
137
139
  )
138
140
 
139
141
 
140
- _OBJ_MEM_ADDRESS = re.compile(r"^\<[a-zA-Z_]+[a-zA-Z0-9<>._ ]* at 0x[0-9a-f]+\>$")
142
+ _OBJ_MEM_ADDRESS: Final = re.compile(
143
+ r"^\<[a-zA-Z_]+[a-zA-Z0-9<>._ ]* at 0x[0-9a-f]+\>$"
144
+ )
141
145
 
142
146
 
143
147
  def is_mem_address_str(string):
@@ -148,7 +152,7 @@ def is_mem_address_str(string):
148
152
  return False
149
153
 
150
154
 
151
- _RE_CONTAINS_HTML = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")
155
+ _RE_CONTAINS_HTML: Final = re.compile(r"(?:</[^<]+>)|(?:<[^<]+/>)")
152
156
 
153
157
 
154
158
  def probably_contains_html_tags(s: str) -> bool:
@@ -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 shutil
16
18
  import tempfile
17
19
 
@@ -21,7 +23,7 @@ from streamlit import util
21
23
  # tempfile.mkdtemp
22
24
 
23
25
 
24
- class TemporaryDirectory(object):
26
+ class TemporaryDirectory:
25
27
  """Temporary directory context manager.
26
28
 
27
29
  Creates a temporary directory that exists within the context manager scope.
@@ -1383,8 +1383,7 @@ class Block:
1383
1383
  def __iter__(self):
1384
1384
  yield self
1385
1385
  for child_idx in self.children:
1386
- for c in self.children[child_idx]:
1387
- yield c
1386
+ yield from self.children[child_idx]
1388
1387
 
1389
1388
  def __getitem__(self, k: int) -> Node:
1390
1389
  return self.children[k]
streamlit/type_util.py CHANGED
@@ -25,17 +25,13 @@ from enum import Enum, EnumMeta, auto
25
25
  from typing import (
26
26
  TYPE_CHECKING,
27
27
  Any,
28
- Dict,
29
28
  Final,
30
29
  Iterable,
31
- List,
32
30
  Literal,
33
31
  NamedTuple,
34
32
  Protocol,
35
33
  Sequence,
36
- Set,
37
34
  Tuple,
38
- Type,
39
35
  TypeVar,
40
36
  Union,
41
37
  cast,
@@ -172,11 +168,11 @@ def is_type(
172
168
 
173
169
 
174
170
  @overload
175
- def is_type(obj: object, fqn_type_pattern: Union[str, re.Pattern[str]]) -> bool:
171
+ def is_type(obj: object, fqn_type_pattern: str | re.Pattern[str]) -> bool:
176
172
  ...
177
173
 
178
174
 
179
- def is_type(obj: object, fqn_type_pattern: Union[str, re.Pattern[str]]) -> bool:
175
+ def is_type(obj: object, fqn_type_pattern: str | re.Pattern[str]) -> bool:
180
176
  """Check type without importing expensive modules.
181
177
 
182
178
  Parameters
@@ -406,7 +402,7 @@ def is_list_of_scalars(data: Iterable[Any]) -> bool:
406
402
  return infer_dtype(data, skipna=True) not in ["mixed", "unknown-array"]
407
403
 
408
404
 
409
- def is_plotly_chart(obj: object) -> TypeGuard[Union[Figure, list[Any], dict[str, Any]]]:
405
+ def is_plotly_chart(obj: object) -> TypeGuard[Figure | list[Any] | dict[str, Any]]:
410
406
  """True if input looks like a Plotly chart."""
411
407
  return (
412
408
  is_type(obj, "plotly.graph_objs._figure.Figure")
@@ -417,7 +413,7 @@ def is_plotly_chart(obj: object) -> TypeGuard[Union[Figure, list[Any], dict[str,
417
413
 
418
414
  def is_graphviz_chart(
419
415
  obj: object,
420
- ) -> TypeGuard[Union[graphviz.Graph, graphviz.Digraph]]:
416
+ ) -> TypeGuard[graphviz.Graph | graphviz.Digraph]:
421
417
  """True if input looks like a GraphViz chart."""
422
418
  return (
423
419
  # GraphViz < 0.18
@@ -478,7 +474,7 @@ def is_namedtuple(x: object) -> TypeGuard[NamedTuple]:
478
474
  return all(type(n).__name__ == "str" for n in f)
479
475
 
480
476
 
481
- def is_pandas_styler(obj: object) -> TypeGuard["Styler"]:
477
+ def is_pandas_styler(obj: object) -> TypeGuard[Styler]:
482
478
  return is_type(obj, _PANDAS_STYLER_TYPE_STR)
483
479
 
484
480
 
@@ -523,7 +519,7 @@ def convert_anything_to_df(
523
519
  max_unevaluated_rows: int = MAX_UNEVALUATED_DF_ROWS,
524
520
  ensure_copy: bool = False,
525
521
  allow_styler: bool = False,
526
- ) -> Union[DataFrame, "Styler"]:
522
+ ) -> DataFrame | Styler:
527
523
  ...
528
524
 
529
525
 
@@ -532,7 +528,7 @@ def convert_anything_to_df(
532
528
  max_unevaluated_rows: int = MAX_UNEVALUATED_DF_ROWS,
533
529
  ensure_copy: bool = False,
534
530
  allow_styler: bool = False,
535
- ) -> Union[DataFrame, "Styler"]:
531
+ ) -> DataFrame | Styler:
536
532
  """Try to convert different formats to a Pandas Dataframe.
537
533
 
538
534
  Parameters
@@ -634,7 +630,7 @@ def ensure_iterable(obj: OptionSequence[V_co]) -> Iterable[Any]:
634
630
  ...
635
631
 
636
632
 
637
- def ensure_iterable(obj: Union[OptionSequence[V_co], Iterable[V_co]]) -> Iterable[Any]:
633
+ def ensure_iterable(obj: OptionSequence[V_co] | Iterable[V_co]) -> Iterable[Any]:
638
634
  """Try to convert different formats to something iterable. Most inputs
639
635
  are assumed to be iterable, but if we have a DataFrame, we can just
640
636
  select the first column to iterate over. If the input is not iterable,
@@ -850,7 +846,7 @@ def pyarrow_table_to_bytes(table: pa.Table) -> bytes:
850
846
  return cast(bytes, sink.getvalue().to_pybytes())
851
847
 
852
848
 
853
- def is_colum_type_arrow_incompatible(column: Union[Series[Any], Index]) -> bool:
849
+ def is_colum_type_arrow_incompatible(column: Series[Any] | Index) -> bool:
854
850
  """Return True if the column type is known to cause issues during Arrow conversion."""
855
851
  from pandas.api.types import infer_dtype, is_dict_like, is_list_like
856
852
 
@@ -896,7 +892,7 @@ def is_colum_type_arrow_incompatible(column: Union[Series[Any], Index]) -> bool:
896
892
 
897
893
 
898
894
  def fix_arrow_incompatible_column_types(
899
- df: DataFrame, selected_columns: List[str] | None = None
895
+ df: DataFrame, selected_columns: list[str] | None = None
900
896
  ) -> DataFrame:
901
897
  """Fix column types that are not supported by Arrow table.
902
898
 
@@ -1075,16 +1071,16 @@ def _unify_missing_values(df: DataFrame) -> DataFrame:
1075
1071
 
1076
1072
  def convert_df_to_data_format(
1077
1073
  df: DataFrame, data_format: DataFormat
1078
- ) -> Union[
1079
- DataFrame,
1080
- Series[Any],
1081
- pa.Table,
1082
- np.ndarray[Any, np.dtype[Any]],
1083
- Tuple[Any],
1084
- List[Any],
1085
- Set[Any],
1086
- Dict[str, Any],
1087
- ]:
1074
+ ) -> (
1075
+ DataFrame
1076
+ | Series[Any]
1077
+ | pa.Table
1078
+ | np.ndarray[Any, np.dtype[Any]]
1079
+ | tuple[Any]
1080
+ | list[Any]
1081
+ | set[Any]
1082
+ | dict[str, Any]
1083
+ ):
1088
1084
  """Convert a dataframe to the specified data format.
1089
1085
 
1090
1086
  Parameters
@@ -1279,7 +1275,7 @@ E2 = TypeVar("E2", bound=Enum)
1279
1275
  ALLOWED_ENUM_COERCION_CONFIG_SETTINGS = ("off", "nameOnly", "nameAndValue")
1280
1276
 
1281
1277
 
1282
- def coerce_enum(from_enum_value: E1, to_enum_class: Type[E2]) -> E1 | E2:
1278
+ def coerce_enum(from_enum_value: E1, to_enum_class: type[E2]) -> E1 | E2:
1283
1279
  """Attempt to coerce an Enum value to another EnumMeta.
1284
1280
 
1285
1281
  An Enum value of EnumMeta E1 is considered coercable to EnumType E2
streamlit/url_util.py CHANGED
@@ -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 re
16
- from typing import Literal, Optional, Tuple
18
+ from typing import Final, Literal
17
19
  from urllib.parse import urlparse
18
20
 
19
21
  from typing_extensions import TypeAlias
@@ -22,7 +24,7 @@ UrlSchema: TypeAlias = Literal["http", "https", "mailto", "data"]
22
24
 
23
25
 
24
26
  # Regular expression for process_gitblob_url
25
- _GITBLOB_RE = re.compile(
27
+ _GITBLOB_RE: Final = re.compile(
26
28
  r"(?P<base>https:\/\/?(gist\.)?github.com\/)"
27
29
  r"(?P<account>([\w\.]+\/){1,2})"
28
30
  r"(?P<blob_or_raw>(blob|raw))?"
@@ -55,7 +57,7 @@ def process_gitblob_url(url: str) -> str:
55
57
  return url
56
58
 
57
59
 
58
- def get_hostname(url: str) -> Optional[str]:
60
+ def get_hostname(url: str) -> str | None:
59
61
  """Return the hostname of a URL (with or without protocol)."""
60
62
  # Just so urllib can parse the URL, make sure there's a protocol.
61
63
  # (The actual protocol doesn't matter to us)
@@ -68,7 +70,7 @@ def get_hostname(url: str) -> Optional[str]:
68
70
 
69
71
  def is_url(
70
72
  url: str,
71
- allowed_schemas: Tuple[UrlSchema, ...] = ("http", "https"),
73
+ allowed_schemas: tuple[UrlSchema, ...] = ("http", "https"),
72
74
  ) -> bool:
73
75
  """Check if a string looks like an URL.
74
76
 
streamlit/user_info.py CHANGED
@@ -12,7 +12,9 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Iterator, Mapping, NoReturn, Optional
15
+ from __future__ import annotations
16
+
17
+ from typing import Iterator, Mapping, NoReturn, Union
16
18
 
17
19
  from streamlit.errors import StreamlitAPIException
18
20
  from streamlit.runtime.scriptrunner import get_script_run_ctx as _get_script_run_ctx
@@ -29,7 +31,7 @@ def _get_user_info() -> UserInfo:
29
31
 
30
32
  # Class attributes are listed as "Parameters" in the docstring as a workaround
31
33
  # for the docstring parser for docs.strreamlit.io
32
- class UserInfoProxy(Mapping[str, Optional[str]]):
34
+ class UserInfoProxy(Mapping[str, Union[str, None]]):
33
35
  """
34
36
  A read-only, dict-like object for accessing information about current user.
35
37
 
@@ -58,19 +60,19 @@ class UserInfoProxy(Mapping[str, Optional[str]]):
58
60
 
59
61
  """
60
62
 
61
- def __getitem__(self, key: str) -> Optional[str]:
63
+ def __getitem__(self, key: str) -> str | None:
62
64
  return _get_user_info()[key]
63
65
 
64
- def __getattr__(self, key: str) -> Optional[str]:
66
+ def __getattr__(self, key: str) -> str | None:
65
67
  try:
66
68
  return _get_user_info()[key]
67
69
  except KeyError:
68
70
  raise AttributeError
69
71
 
70
- def __setattr__(self, name: str, value: Optional[str]) -> NoReturn:
72
+ def __setattr__(self, name: str, value: str | None) -> NoReturn:
71
73
  raise StreamlitAPIException("st.experimental_user cannot be modified")
72
74
 
73
- def __setitem__(self, name: str, value: Optional[str]) -> NoReturn:
75
+ def __setitem__(self, name: str, value: str | None) -> NoReturn:
74
76
  raise StreamlitAPIException("st.experimental_user cannot be modified")
75
77
 
76
78
  def __iter__(self) -> Iterator[str]:
streamlit/util.py CHANGED
@@ -23,21 +23,9 @@ import hashlib
23
23
  import os
24
24
  import subprocess
25
25
  import sys
26
- from typing import (
27
- Any,
28
- Dict,
29
- Generic,
30
- Iterable,
31
- List,
32
- Mapping,
33
- Optional,
34
- Set,
35
- TypeVar,
36
- Union,
37
- )
26
+ from typing import Any, Callable, Final, Generic, Iterable, Mapping, TypeVar
38
27
 
39
28
  from cachetools import TTLCache
40
- from typing_extensions import Final
41
29
 
42
30
  from streamlit import env_util
43
31
 
@@ -47,14 +35,14 @@ FLOAT_EQUALITY_EPSILON: Final[float] = 0.000000000005
47
35
 
48
36
  # Due to security issue in md5 and sha1, usedforsecurity
49
37
  # argument is added to hashlib for python versions higher than 3.8
50
- HASHLIB_KWARGS: Dict[str, Any] = (
38
+ HASHLIB_KWARGS: dict[str, Any] = (
51
39
  {"usedforsecurity": False} if sys.version_info >= (3, 9) else {}
52
40
  )
53
41
 
54
42
 
55
- def memoize(func):
43
+ def memoize(func: Callable[..., Any]) -> Callable[..., Any]:
56
44
  """Decorator to memoize the result of a no-args func."""
57
- result: List[Any] = []
45
+ result: list[Any] = []
58
46
 
59
47
  @functools.wraps(func)
60
48
  def wrapped_func():
@@ -65,7 +53,7 @@ def memoize(func):
65
53
  return wrapped_func
66
54
 
67
55
 
68
- def open_browser(url):
56
+ def open_browser(url: str) -> None:
69
57
  """Open a web browser pointing to a given URL.
70
58
 
71
59
  We use this function instead of Python's `webbrowser` module because this
@@ -107,13 +95,13 @@ def open_browser(url):
107
95
  raise Error('Cannot open browser in platform "%s"' % platform.system())
108
96
 
109
97
 
110
- def _open_browser_with_webbrowser(url):
98
+ def _open_browser_with_webbrowser(url: str) -> None:
111
99
  import webbrowser
112
100
 
113
101
  webbrowser.open(url)
114
102
 
115
103
 
116
- def _open_browser_with_command(command, url):
104
+ def _open_browser_with_command(command: str, url: str) -> None:
117
105
  cmd_line = [command, url]
118
106
  with open(os.devnull, "w") as devnull:
119
107
  subprocess.Popen(cmd_line, stdout=devnull, stderr=subprocess.STDOUT)
@@ -167,13 +155,13 @@ def index_(iterable: Iterable[_Value], x: _Value) -> int:
167
155
  elif isinstance(value, float) and isinstance(x, float):
168
156
  if abs(x - value) < FLOAT_EQUALITY_EPSILON:
169
157
  return i
170
- raise ValueError("{} is not in iterable".format(str(x)))
158
+ raise ValueError(f"{str(x)} is not in iterable")
171
159
 
172
160
 
173
161
  _Key = TypeVar("_Key", bound=str)
174
162
 
175
163
 
176
- def lower_clean_dict_keys(dict: Mapping[_Key, _Value]) -> Dict[str, _Value]:
164
+ def lower_clean_dict_keys(dict: Mapping[_Key, _Value]) -> dict[str, _Value]:
177
165
  return {k.lower().strip(): v for k, v in dict.items()}
178
166
 
179
167
 
@@ -182,7 +170,7 @@ class Error(Exception):
182
170
  pass
183
171
 
184
172
 
185
- def calc_md5(s: Union[bytes, str]) -> str:
173
+ def calc_md5(s: bytes | str) -> str:
186
174
  """Return the md5 hash of the given string."""
187
175
  h = hashlib.new("md5", **HASHLIB_KWARGS)
188
176
 
@@ -193,8 +181,8 @@ def calc_md5(s: Union[bytes, str]) -> str:
193
181
 
194
182
 
195
183
  def exclude_keys_in_dict(
196
- d: Dict[str, Any], keys_to_exclude: List[str]
197
- ) -> Dict[str, Any]:
184
+ d: dict[str, Any], keys_to_exclude: list[str]
185
+ ) -> dict[str, Any]:
198
186
  """Returns new object but without keys defined in keys_to_exclude"""
199
187
  return {
200
188
  key: value for key, value in d.items() if key.lower() not in keys_to_exclude
@@ -202,20 +190,18 @@ def exclude_keys_in_dict(
202
190
 
203
191
 
204
192
  def extract_key_query_params(
205
- query_params: Dict[str, List[str]], param_key: str
206
- ) -> Set[str]:
193
+ query_params: dict[str, list[str]], param_key: str
194
+ ) -> set[str]:
207
195
  """Extracts key (case-insensitive) query params from Dict, and returns them as Set of str."""
208
- return set(
209
- [
210
- item.lower()
211
- for sublist in [
212
- [value.lower() for value in query_params[key]]
213
- for key in query_params.keys()
214
- if key.lower() == param_key and query_params.get(key)
215
- ]
216
- for item in sublist
196
+ return {
197
+ item.lower()
198
+ for sublist in [
199
+ [value.lower() for value in query_params[key]]
200
+ for key in query_params.keys()
201
+ if key.lower() == param_key and query_params.get(key)
217
202
  ]
218
- )
203
+ for item in sublist
204
+ }
219
205
 
220
206
 
221
207
  K = TypeVar("K")
@@ -227,7 +213,7 @@ class TimedCleanupCache(TTLCache, Generic[K, V]):
227
213
 
228
214
  def __init__(self, *args, **kwargs):
229
215
  super().__init__(*args, **kwargs)
230
- self._task: Optional[asyncio.Task[Any]] = None
216
+ self._task: asyncio.Task[Any] | None = None
231
217
 
232
218
  def __setitem__(self, key: K, value: V) -> None:
233
219
  # Set an expiration task to run periodically
@@ -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