streamlit-data-editor-plus 0.1.0__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 (456) hide show
  1. streamlit/__init__.py +292 -0
  2. streamlit/__main__.py +20 -0
  3. streamlit/auth_util.py +583 -0
  4. streamlit/cli_util.py +107 -0
  5. streamlit/column_config.py +60 -0
  6. streamlit/commands/__init__.py +13 -0
  7. streamlit/commands/echo.py +128 -0
  8. streamlit/commands/execution_control.py +318 -0
  9. streamlit/commands/logo.py +250 -0
  10. streamlit/commands/navigation.py +430 -0
  11. streamlit/commands/page_config.py +335 -0
  12. streamlit/components/__init__.py +13 -0
  13. streamlit/components/lib/__init__.py +13 -0
  14. streamlit/components/lib/local_component_registry.py +84 -0
  15. streamlit/components/types/__init__.py +13 -0
  16. streamlit/components/types/base_component_registry.py +99 -0
  17. streamlit/components/types/base_custom_component.py +158 -0
  18. streamlit/components/v1/__init__.py +29 -0
  19. streamlit/components/v1/component_arrow.py +141 -0
  20. streamlit/components/v1/component_registry.py +147 -0
  21. streamlit/components/v1/components.py +38 -0
  22. streamlit/components/v1/custom_component.py +239 -0
  23. streamlit/components/v2/__init__.py +531 -0
  24. streamlit/components/v2/bidi_component/__init__.py +20 -0
  25. streamlit/components/v2/bidi_component/constants.py +29 -0
  26. streamlit/components/v2/bidi_component/main.py +537 -0
  27. streamlit/components/v2/bidi_component/serialization.py +272 -0
  28. streamlit/components/v2/bidi_component/state.py +92 -0
  29. streamlit/components/v2/component_definition_resolver.py +143 -0
  30. streamlit/components/v2/component_file_watcher.py +403 -0
  31. streamlit/components/v2/component_manager.py +439 -0
  32. streamlit/components/v2/component_manifest_handler.py +122 -0
  33. streamlit/components/v2/component_path_utils.py +233 -0
  34. streamlit/components/v2/component_registry.py +426 -0
  35. streamlit/components/v2/get_bidi_component_manager.py +51 -0
  36. streamlit/components/v2/manifest_scanner.py +620 -0
  37. streamlit/components/v2/presentation.py +198 -0
  38. streamlit/components/v2/types.py +317 -0
  39. streamlit/config.py +2977 -0
  40. streamlit/config_option.py +319 -0
  41. streamlit/config_util.py +887 -0
  42. streamlit/connections/__init__.py +32 -0
  43. streamlit/connections/base_connection.py +215 -0
  44. streamlit/connections/snowflake_connection.py +765 -0
  45. streamlit/connections/snowpark_connection.py +213 -0
  46. streamlit/connections/sql_connection.py +427 -0
  47. streamlit/connections/util.py +97 -0
  48. streamlit/cursor.py +314 -0
  49. streamlit/dataframe_util.py +1434 -0
  50. streamlit/delta_generator.py +696 -0
  51. streamlit/delta_generator_singletons.py +243 -0
  52. streamlit/deprecation_util.py +253 -0
  53. streamlit/development.py +21 -0
  54. streamlit/elements/__init__.py +13 -0
  55. streamlit/elements/alert.py +341 -0
  56. streamlit/elements/arrow.py +1065 -0
  57. streamlit/elements/balloons.py +47 -0
  58. streamlit/elements/bokeh_chart.py +75 -0
  59. streamlit/elements/code.py +154 -0
  60. streamlit/elements/deck_gl_json_chart.py +627 -0
  61. streamlit/elements/dialog_decorator.py +320 -0
  62. streamlit/elements/empty.py +130 -0
  63. streamlit/elements/exception.py +370 -0
  64. streamlit/elements/form.py +481 -0
  65. streamlit/elements/graphviz_chart.py +226 -0
  66. streamlit/elements/heading.py +407 -0
  67. streamlit/elements/help.py +565 -0
  68. streamlit/elements/html.py +189 -0
  69. streamlit/elements/iframe.py +245 -0
  70. streamlit/elements/image.py +221 -0
  71. streamlit/elements/json.py +171 -0
  72. streamlit/elements/layouts.py +1534 -0
  73. streamlit/elements/lib/__init__.py +13 -0
  74. streamlit/elements/lib/built_in_chart_utils.py +1316 -0
  75. streamlit/elements/lib/color_util.py +272 -0
  76. streamlit/elements/lib/column_config_utils.py +555 -0
  77. streamlit/elements/lib/column_types.py +2699 -0
  78. streamlit/elements/lib/dialog.py +212 -0
  79. streamlit/elements/lib/dicttools.py +152 -0
  80. streamlit/elements/lib/file_uploader_utils.py +91 -0
  81. streamlit/elements/lib/form_utils.py +77 -0
  82. streamlit/elements/lib/image_utils.py +447 -0
  83. streamlit/elements/lib/js_number.py +105 -0
  84. streamlit/elements/lib/layout_utils.py +325 -0
  85. streamlit/elements/lib/mutable_expander_container.py +73 -0
  86. streamlit/elements/lib/mutable_popover_container.py +73 -0
  87. streamlit/elements/lib/mutable_status_container.py +194 -0
  88. streamlit/elements/lib/mutable_tab_container.py +73 -0
  89. streamlit/elements/lib/options_selector_utils.py +480 -0
  90. streamlit/elements/lib/pandas_styler_utils.py +302 -0
  91. streamlit/elements/lib/policies.py +198 -0
  92. streamlit/elements/lib/shortcut_utils.py +152 -0
  93. streamlit/elements/lib/streamlit_plotly_theme.py +206 -0
  94. streamlit/elements/lib/subtitle_utils.py +175 -0
  95. streamlit/elements/lib/utils.py +274 -0
  96. streamlit/elements/map.py +526 -0
  97. streamlit/elements/markdown.py +529 -0
  98. streamlit/elements/media.py +843 -0
  99. streamlit/elements/metric.py +529 -0
  100. streamlit/elements/pdf.py +190 -0
  101. streamlit/elements/plotly_chart.py +779 -0
  102. streamlit/elements/progress.py +172 -0
  103. streamlit/elements/pyplot.py +240 -0
  104. streamlit/elements/snow.py +47 -0
  105. streamlit/elements/space.py +121 -0
  106. streamlit/elements/spinner.py +152 -0
  107. streamlit/elements/table.py +240 -0
  108. streamlit/elements/text.py +113 -0
  109. streamlit/elements/toast.py +175 -0
  110. streamlit/elements/vega_charts.py +2510 -0
  111. streamlit/elements/widgets/__init__.py +13 -0
  112. streamlit/elements/widgets/audio_input.py +334 -0
  113. streamlit/elements/widgets/button.py +1559 -0
  114. streamlit/elements/widgets/button_group.py +1061 -0
  115. streamlit/elements/widgets/camera_input.py +281 -0
  116. streamlit/elements/widgets/chat.py +1028 -0
  117. streamlit/elements/widgets/checkbox.py +420 -0
  118. streamlit/elements/widgets/color_picker.py +329 -0
  119. streamlit/elements/widgets/data_editor.py +1168 -0
  120. streamlit/elements/widgets/data_editor_plus.py +902 -0
  121. streamlit/elements/widgets/feedback.py +322 -0
  122. streamlit/elements/widgets/file_uploader.py +592 -0
  123. streamlit/elements/widgets/multiselect.py +654 -0
  124. streamlit/elements/widgets/number_input.py +719 -0
  125. streamlit/elements/widgets/radio.py +551 -0
  126. streamlit/elements/widgets/select_slider.py +568 -0
  127. streamlit/elements/widgets/selectbox.py +680 -0
  128. streamlit/elements/widgets/slider.py +1093 -0
  129. streamlit/elements/widgets/text_widgets.py +779 -0
  130. streamlit/elements/widgets/time_widgets.py +1712 -0
  131. streamlit/elements/write.py +585 -0
  132. streamlit/emojis.py +34 -0
  133. streamlit/env_util.py +56 -0
  134. streamlit/error_util.py +112 -0
  135. streamlit/errors.py +680 -0
  136. streamlit/external/__init__.py +13 -0
  137. streamlit/external/langchain/__init__.py +23 -0
  138. streamlit/external/langchain/streamlit_callback_handler.py +410 -0
  139. streamlit/file_util.py +266 -0
  140. streamlit/git_util.py +200 -0
  141. streamlit/hello/__init__.py +13 -0
  142. streamlit/hello/__pycache__/__init__.cpython-312.pyc +0 -0
  143. streamlit/hello/__pycache__/streamlit_app.cpython-312.pyc +0 -0
  144. streamlit/hello/animation_demo.py +82 -0
  145. streamlit/hello/dataframe_demo.py +71 -0
  146. streamlit/hello/hello.py +46 -0
  147. streamlit/hello/mapping_demo.py +113 -0
  148. streamlit/hello/plotting_demo.py +62 -0
  149. streamlit/hello/streamlit_app.py +57 -0
  150. streamlit/hello/utils.py +30 -0
  151. streamlit/logger.py +129 -0
  152. streamlit/material_icon_names.py +25 -0
  153. streamlit/navigation/__init__.py +13 -0
  154. streamlit/navigation/page.py +365 -0
  155. streamlit/net_util.py +125 -0
  156. streamlit/path_security.py +98 -0
  157. streamlit/platform.py +31 -0
  158. streamlit/proto/Alert_pb2.py +28 -0
  159. streamlit/proto/Alert_pb2.pyi +100 -0
  160. streamlit/proto/AppPage_pb2.py +25 -0
  161. streamlit/proto/AppPage_pb2.pyi +75 -0
  162. streamlit/proto/ArrowData_pb2.py +27 -0
  163. streamlit/proto/ArrowData_pb2.pyi +103 -0
  164. streamlit/proto/ArrowNamedDataSet_pb2.py +26 -0
  165. streamlit/proto/ArrowNamedDataSet_pb2.pyi +65 -0
  166. streamlit/proto/AudioInput_pb2.py +26 -0
  167. streamlit/proto/AudioInput_pb2.pyi +72 -0
  168. streamlit/proto/Audio_pb2.py +26 -0
  169. streamlit/proto/Audio_pb2.pyi +75 -0
  170. streamlit/proto/AuthRedirect_pb2.py +25 -0
  171. streamlit/proto/AuthRedirect_pb2.pyi +48 -0
  172. streamlit/proto/AutoRerun_pb2.py +25 -0
  173. streamlit/proto/AutoRerun_pb2.pyi +52 -0
  174. streamlit/proto/BackMsg_pb2.py +29 -0
  175. streamlit/proto/BackMsg_pb2.pyi +132 -0
  176. streamlit/proto/Balloons_pb2.py +25 -0
  177. streamlit/proto/Balloons_pb2.pyi +48 -0
  178. streamlit/proto/BidiComponent_pb2.py +32 -0
  179. streamlit/proto/BidiComponent_pb2.pyi +176 -0
  180. streamlit/proto/Block_pb2.py +62 -0
  181. streamlit/proto/Block_pb2.pyi +518 -0
  182. streamlit/proto/ButtonGroup_pb2.py +32 -0
  183. streamlit/proto/ButtonGroup_pb2.pyi +157 -0
  184. streamlit/proto/ButtonLikeIconPosition_pb2.py +25 -0
  185. streamlit/proto/ButtonLikeIconPosition_pb2.pyi +47 -0
  186. streamlit/proto/Button_pb2.py +26 -0
  187. streamlit/proto/Button_pb2.pyi +82 -0
  188. streamlit/proto/CameraInput_pb2.py +26 -0
  189. streamlit/proto/CameraInput_pb2.pyi +66 -0
  190. streamlit/proto/ChatInput_pb2.py +27 -0
  191. streamlit/proto/ChatInput_pb2.pyi +113 -0
  192. streamlit/proto/Checkbox_pb2.py +28 -0
  193. streamlit/proto/Checkbox_pb2.pyi +99 -0
  194. streamlit/proto/ClientState_pb2.py +28 -0
  195. streamlit/proto/ClientState_pb2.pyi +137 -0
  196. streamlit/proto/Code_pb2.py +25 -0
  197. streamlit/proto/Code_pb2.pyi +59 -0
  198. streamlit/proto/ColorPicker_pb2.py +26 -0
  199. streamlit/proto/ColorPicker_pb2.pyi +82 -0
  200. streamlit/proto/Common_pb2.py +49 -0
  201. streamlit/proto/Common_pb2.pyi +325 -0
  202. streamlit/proto/Components_pb2.py +33 -0
  203. streamlit/proto/Components_pb2.pyi +196 -0
  204. streamlit/proto/Dataframe_pb2.py +30 -0
  205. streamlit/proto/Dataframe_pb2.pyi +172 -0
  206. streamlit/proto/DateInput_pb2.py +26 -0
  207. streamlit/proto/DateInput_pb2.pyi +91 -0
  208. streamlit/proto/DateTimeInput_pb2.py +26 -0
  209. streamlit/proto/DateTimeInput_pb2.pyi +94 -0
  210. streamlit/proto/DeckGlJsonChart_pb2.py +27 -0
  211. streamlit/proto/DeckGlJsonChart_pb2.pyi +91 -0
  212. streamlit/proto/Delta_pb2.py +29 -0
  213. streamlit/proto/Delta_pb2.pyi +82 -0
  214. streamlit/proto/DownloadButton_pb2.py +26 -0
  215. streamlit/proto/DownloadButton_pb2.pyi +92 -0
  216. streamlit/proto/Element_pb2.py +81 -0
  217. streamlit/proto/Element_pb2.pyi +348 -0
  218. streamlit/proto/Empty_pb2.py +25 -0
  219. streamlit/proto/Empty_pb2.pyi +42 -0
  220. streamlit/proto/Exception_pb2.py +26 -0
  221. streamlit/proto/Exception_pb2.pyi +88 -0
  222. streamlit/proto/Favicon_pb2.py +25 -0
  223. streamlit/proto/Favicon_pb2.pyi +47 -0
  224. streamlit/proto/Feedback_pb2.py +27 -0
  225. streamlit/proto/Feedback_pb2.pyi +93 -0
  226. streamlit/proto/FileUploader_pb2.py +26 -0
  227. streamlit/proto/FileUploader_pb2.pyi +90 -0
  228. streamlit/proto/ForwardMsg_pb2.py +48 -0
  229. streamlit/proto/ForwardMsg_pb2.pyi +296 -0
  230. streamlit/proto/GapSize_pb2.py +27 -0
  231. streamlit/proto/GapSize_pb2.pyi +82 -0
  232. streamlit/proto/GitInfo_pb2.py +27 -0
  233. streamlit/proto/GitInfo_pb2.pyi +84 -0
  234. streamlit/proto/GraphVizChart_pb2.py +25 -0
  235. streamlit/proto/GraphVizChart_pb2.pyi +56 -0
  236. streamlit/proto/Heading_pb2.py +25 -0
  237. streamlit/proto/Heading_pb2.pyi +63 -0
  238. streamlit/proto/HeightConfig_pb2.py +25 -0
  239. streamlit/proto/HeightConfig_pb2.pyi +61 -0
  240. streamlit/proto/Help_pb2.py +27 -0
  241. streamlit/proto/Help_pb2.pyi +104 -0
  242. streamlit/proto/Html_pb2.py +25 -0
  243. streamlit/proto/Html_pb2.pyi +52 -0
  244. streamlit/proto/IFrame_pb2.py +25 -0
  245. streamlit/proto/IFrame_pb2.pyi +68 -0
  246. streamlit/proto/Image_pb2.py +27 -0
  247. streamlit/proto/Image_pb2.pyi +73 -0
  248. streamlit/proto/Json_pb2.py +25 -0
  249. streamlit/proto/Json_pb2.pyi +63 -0
  250. streamlit/proto/LabelVisibility_pb2.py +27 -0
  251. streamlit/proto/LabelVisibility_pb2.pyi +69 -0
  252. streamlit/proto/LinkButton_pb2.py +26 -0
  253. streamlit/proto/LinkButton_pb2.pyi +76 -0
  254. streamlit/proto/Logo_pb2.py +27 -0
  255. streamlit/proto/Logo_pb2.pyi +82 -0
  256. streamlit/proto/Markdown_pb2.py +27 -0
  257. streamlit/proto/Markdown_pb2.pyi +83 -0
  258. streamlit/proto/Metric_pb2.py +32 -0
  259. streamlit/proto/Metric_pb2.pyi +145 -0
  260. streamlit/proto/MetricsEvent_pb2.py +28 -0
  261. streamlit/proto/MetricsEvent_pb2.pyi +224 -0
  262. streamlit/proto/MultiSelect_pb2.py +26 -0
  263. streamlit/proto/MultiSelect_pb2.pyi +108 -0
  264. streamlit/proto/Navigation_pb2.py +28 -0
  265. streamlit/proto/Navigation_pb2.pyi +89 -0
  266. streamlit/proto/NewSession_pb2.py +47 -0
  267. streamlit/proto/NewSession_pb2.pyi +753 -0
  268. streamlit/proto/NumberInput_pb2.py +28 -0
  269. streamlit/proto/NumberInput_pb2.pyi +138 -0
  270. streamlit/proto/PageConfig_pb2.py +33 -0
  271. streamlit/proto/PageConfig_pb2.pyi +179 -0
  272. streamlit/proto/PageInfo_pb2.py +25 -0
  273. streamlit/proto/PageInfo_pb2.pyi +50 -0
  274. streamlit/proto/PageLink_pb2.py +26 -0
  275. streamlit/proto/PageLink_pb2.pyi +80 -0
  276. streamlit/proto/PageNotFound_pb2.py +25 -0
  277. streamlit/proto/PageNotFound_pb2.pyi +49 -0
  278. streamlit/proto/PageProfile_pb2.py +29 -0
  279. streamlit/proto/PageProfile_pb2.pyi +146 -0
  280. streamlit/proto/ParentMessage_pb2.py +25 -0
  281. streamlit/proto/ParentMessage_pb2.pyi +53 -0
  282. streamlit/proto/PlotlyChart_pb2.py +27 -0
  283. streamlit/proto/PlotlyChart_pb2.pyi +96 -0
  284. streamlit/proto/Progress_pb2.py +25 -0
  285. streamlit/proto/Progress_pb2.pyi +50 -0
  286. streamlit/proto/Radio_pb2.py +26 -0
  287. streamlit/proto/Radio_pb2.pyi +104 -0
  288. streamlit/proto/RootContainer_pb2.py +25 -0
  289. streamlit/proto/RootContainer_pb2.pyi +56 -0
  290. streamlit/proto/Selectbox_pb2.py +26 -0
  291. streamlit/proto/Selectbox_pb2.pyi +107 -0
  292. streamlit/proto/SessionEvent_pb2.py +26 -0
  293. streamlit/proto/SessionEvent_pb2.pyi +72 -0
  294. streamlit/proto/SessionStatus_pb2.py +25 -0
  295. streamlit/proto/SessionStatus_pb2.pyi +64 -0
  296. streamlit/proto/Skeleton_pb2.py +27 -0
  297. streamlit/proto/Skeleton_pb2.pyi +75 -0
  298. streamlit/proto/Slider_pb2.py +30 -0
  299. streamlit/proto/Slider_pb2.pyi +161 -0
  300. streamlit/proto/Snow_pb2.py +25 -0
  301. streamlit/proto/Snow_pb2.pyi +48 -0
  302. streamlit/proto/Space_pb2.py +25 -0
  303. streamlit/proto/Space_pb2.pyi +48 -0
  304. streamlit/proto/Spinner_pb2.py +25 -0
  305. streamlit/proto/Spinner_pb2.pyi +56 -0
  306. streamlit/proto/Table_pb2.py +28 -0
  307. streamlit/proto/Table_pb2.pyi +83 -0
  308. streamlit/proto/TextAlignmentConfig_pb2.py +27 -0
  309. streamlit/proto/TextAlignmentConfig_pb2.pyi +69 -0
  310. streamlit/proto/TextArea_pb2.py +26 -0
  311. streamlit/proto/TextArea_pb2.pyi +97 -0
  312. streamlit/proto/TextInput_pb2.py +28 -0
  313. streamlit/proto/TextInput_pb2.pyi +124 -0
  314. streamlit/proto/Text_pb2.py +25 -0
  315. streamlit/proto/Text_pb2.pyi +53 -0
  316. streamlit/proto/TimeInput_pb2.py +26 -0
  317. streamlit/proto/TimeInput_pb2.pyi +86 -0
  318. streamlit/proto/Toast_pb2.py +25 -0
  319. streamlit/proto/Toast_pb2.pyi +64 -0
  320. streamlit/proto/Transient_pb2.py +26 -0
  321. streamlit/proto/Transient_pb2.pyi +53 -0
  322. streamlit/proto/VegaLiteChart_pb2.py +27 -0
  323. streamlit/proto/VegaLiteChart_pb2.pyi +92 -0
  324. streamlit/proto/Video_pb2.py +30 -0
  325. streamlit/proto/Video_pb2.pyi +129 -0
  326. streamlit/proto/WidgetStates_pb2.py +31 -0
  327. streamlit/proto/WidgetStates_pb2.pyi +157 -0
  328. streamlit/proto/WidthConfig_pb2.py +25 -0
  329. streamlit/proto/WidthConfig_pb2.pyi +61 -0
  330. streamlit/proto/__init__.py +15 -0
  331. streamlit/proto/openmetrics_data_model_pb2.py +58 -0
  332. streamlit/proto/openmetrics_data_model_pb2.pyi +558 -0
  333. streamlit/py.typed +0 -0
  334. streamlit/runtime/__init__.py +50 -0
  335. streamlit/runtime/app_session.py +1302 -0
  336. streamlit/runtime/caching/__init__.py +118 -0
  337. streamlit/runtime/caching/cache_data_api.py +775 -0
  338. streamlit/runtime/caching/cache_errors.py +143 -0
  339. streamlit/runtime/caching/cache_resource_api.py +756 -0
  340. streamlit/runtime/caching/cache_type.py +33 -0
  341. streamlit/runtime/caching/cache_utils.py +601 -0
  342. streamlit/runtime/caching/cached_message_replay.py +290 -0
  343. streamlit/runtime/caching/hashing.py +655 -0
  344. streamlit/runtime/caching/legacy_cache_api.py +170 -0
  345. streamlit/runtime/caching/storage/__init__.py +29 -0
  346. streamlit/runtime/caching/storage/cache_storage_protocol.py +236 -0
  347. streamlit/runtime/caching/storage/dummy_cache_storage.py +60 -0
  348. streamlit/runtime/caching/storage/in_memory_cache_storage_wrapper.py +159 -0
  349. streamlit/runtime/caching/storage/local_disk_cache_storage.py +223 -0
  350. streamlit/runtime/caching/ttl_cleanup_cache.py +85 -0
  351. streamlit/runtime/connection_factory.py +498 -0
  352. streamlit/runtime/context.py +449 -0
  353. streamlit/runtime/context_util.py +49 -0
  354. streamlit/runtime/credentials.py +355 -0
  355. streamlit/runtime/download_data_util.py +53 -0
  356. streamlit/runtime/forward_msg_cache.py +101 -0
  357. streamlit/runtime/forward_msg_queue.py +263 -0
  358. streamlit/runtime/fragment.py +441 -0
  359. streamlit/runtime/media_file_manager.py +409 -0
  360. streamlit/runtime/media_file_storage.py +143 -0
  361. streamlit/runtime/memory_media_file_storage.py +201 -0
  362. streamlit/runtime/memory_session_storage.py +77 -0
  363. streamlit/runtime/memory_uploaded_file_manager.py +149 -0
  364. streamlit/runtime/metrics_util.py +612 -0
  365. streamlit/runtime/pages_manager.py +160 -0
  366. streamlit/runtime/runtime.py +775 -0
  367. streamlit/runtime/runtime_util.py +108 -0
  368. streamlit/runtime/script_data.py +46 -0
  369. streamlit/runtime/scriptrunner/__init__.py +38 -0
  370. streamlit/runtime/scriptrunner/exec_code.py +167 -0
  371. streamlit/runtime/scriptrunner/magic.py +277 -0
  372. streamlit/runtime/scriptrunner/magic_funcs.py +32 -0
  373. streamlit/runtime/scriptrunner/script_cache.py +89 -0
  374. streamlit/runtime/scriptrunner/script_runner.py +805 -0
  375. streamlit/runtime/scriptrunner_utils/__init__.py +19 -0
  376. streamlit/runtime/scriptrunner_utils/exceptions.py +44 -0
  377. streamlit/runtime/scriptrunner_utils/script_requests.py +311 -0
  378. streamlit/runtime/scriptrunner_utils/script_run_context.py +277 -0
  379. streamlit/runtime/secrets.py +533 -0
  380. streamlit/runtime/session_manager.py +430 -0
  381. streamlit/runtime/state/__init__.py +47 -0
  382. streamlit/runtime/state/common.py +256 -0
  383. streamlit/runtime/state/presentation.py +85 -0
  384. streamlit/runtime/state/query_params.py +767 -0
  385. streamlit/runtime/state/query_params_proxy.py +222 -0
  386. streamlit/runtime/state/safe_session_state.py +146 -0
  387. streamlit/runtime/state/session_state.py +1299 -0
  388. streamlit/runtime/state/session_state_proxy.py +153 -0
  389. streamlit/runtime/state/widgets.py +197 -0
  390. streamlit/runtime/stats.py +356 -0
  391. streamlit/runtime/theme_util.py +148 -0
  392. streamlit/runtime/uploaded_file_manager.py +152 -0
  393. streamlit/runtime/websocket_session_manager.py +301 -0
  394. streamlit/source_util.py +97 -0
  395. streamlit/starlette.py +34 -0
  396. streamlit/string_util.py +264 -0
  397. streamlit/temporary_directory.py +67 -0
  398. streamlit/testing/__init__.py +13 -0
  399. streamlit/testing/v1/__init__.py +17 -0
  400. streamlit/testing/v1/app_test.py +1089 -0
  401. streamlit/testing/v1/element_tree.py +2272 -0
  402. streamlit/testing/v1/local_script_runner.py +176 -0
  403. streamlit/testing/v1/util.py +61 -0
  404. streamlit/time_util.py +73 -0
  405. streamlit/type_util.py +480 -0
  406. streamlit/url_util.py +120 -0
  407. streamlit/user_info.py +698 -0
  408. streamlit/util.py +108 -0
  409. streamlit/vendor/__init__.py +0 -0
  410. streamlit/vendor/pympler/__init__.py +0 -0
  411. streamlit/vendor/pympler/asizeof.py +2870 -0
  412. streamlit/version.py +18 -0
  413. streamlit/watcher/__init__.py +28 -0
  414. streamlit/watcher/event_based_path_watcher.py +500 -0
  415. streamlit/watcher/folder_black_list.py +82 -0
  416. streamlit/watcher/local_sources_watcher.py +297 -0
  417. streamlit/watcher/path_watcher.py +244 -0
  418. streamlit/watcher/polling_path_watcher.py +138 -0
  419. streamlit/watcher/util.py +223 -0
  420. streamlit/web/__init__.py +13 -0
  421. streamlit/web/bootstrap.py +463 -0
  422. streamlit/web/cache_storage_manager_config.py +38 -0
  423. streamlit/web/cli.py +465 -0
  424. streamlit/web/server/__init__.py +30 -0
  425. streamlit/web/server/app_discovery.py +422 -0
  426. streamlit/web/server/app_static_file_handler.py +102 -0
  427. streamlit/web/server/authlib_tornado_integration.py +125 -0
  428. streamlit/web/server/bidi_component_request_handler.py +193 -0
  429. streamlit/web/server/browser_websocket_handler.py +341 -0
  430. streamlit/web/server/component_file_utils.py +105 -0
  431. streamlit/web/server/component_request_handler.py +107 -0
  432. streamlit/web/server/media_file_handler.py +148 -0
  433. streamlit/web/server/oauth_authlib_routes.py +314 -0
  434. streamlit/web/server/oidc_mixin.py +138 -0
  435. streamlit/web/server/routes.py +257 -0
  436. streamlit/web/server/server.py +555 -0
  437. streamlit/web/server/server_util.py +235 -0
  438. streamlit/web/server/starlette/__init__.py +23 -0
  439. streamlit/web/server/starlette/starlette_app.py +541 -0
  440. streamlit/web/server/starlette/starlette_app_utils.py +306 -0
  441. streamlit/web/server/starlette/starlette_auth_routes.py +584 -0
  442. streamlit/web/server/starlette/starlette_gzip_middleware.py +121 -0
  443. streamlit/web/server/starlette/starlette_path_security_middleware.py +97 -0
  444. streamlit/web/server/starlette/starlette_routes.py +884 -0
  445. streamlit/web/server/starlette/starlette_server.py +496 -0
  446. streamlit/web/server/starlette/starlette_server_config.py +55 -0
  447. streamlit/web/server/starlette/starlette_static_routes.py +181 -0
  448. streamlit/web/server/starlette/starlette_websocket.py +560 -0
  449. streamlit/web/server/stats_request_handler.py +116 -0
  450. streamlit/web/server/upload_file_request_handler.py +158 -0
  451. streamlit/web/server/websocket_headers.py +56 -0
  452. streamlit_data_editor_plus-0.1.0.dist-info/METADATA +76 -0
  453. streamlit_data_editor_plus-0.1.0.dist-info/RECORD +456 -0
  454. streamlit_data_editor_plus-0.1.0.dist-info/WHEEL +5 -0
  455. streamlit_data_editor_plus-0.1.0.dist-info/entry_points.txt +2 -0
  456. streamlit_data_editor_plus-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,302 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections import defaultdict
18
+ from collections.abc import Mapping
19
+ from typing import TYPE_CHECKING, Any, TypeVar
20
+
21
+ from streamlit import dataframe_util
22
+ from streamlit.errors import StreamlitAPIException
23
+
24
+ if TYPE_CHECKING:
25
+ from pandas import DataFrame
26
+ from pandas.io.formats.style import Styler
27
+
28
+ from streamlit.proto.ArrowData_pb2 import ArrowData as ArrowDataProto
29
+
30
+ from enum import Enum
31
+
32
+
33
+ def marshall_styler(proto: ArrowDataProto, styler: Styler, default_uuid: str) -> None:
34
+ """Marshall pandas.Styler into an ArrowData proto.
35
+
36
+ Parameters
37
+ ----------
38
+ proto : proto.ArrowData
39
+ Output. The protobuf for Streamlit ArrowData proto.
40
+
41
+ styler : pandas.Styler
42
+ Helps style a DataFrame or Series according to the data with HTML and CSS.
43
+
44
+ default_uuid : str
45
+ If pandas.Styler uuid is not provided, this value will be used.
46
+
47
+ """
48
+ import pandas as pd
49
+
50
+ styler_data_df: pd.DataFrame = styler.data # type: ignore[attr-defined]
51
+ if styler_data_df.size > int(pd.options.styler.render.max_elements):
52
+ raise StreamlitAPIException(
53
+ f"The dataframe has `{styler_data_df.size}` cells, but the maximum number "
54
+ "of cells allowed to be rendered by Pandas Styler is configured to "
55
+ f"`{pd.options.styler.render.max_elements}`. To allow more cells to be "
56
+ 'styled, you can change the `"styler.render.max_elements"` config. For example: '
57
+ f'`pd.set_option("styler.render.max_elements", {styler_data_df.size})`'
58
+ )
59
+
60
+ # pandas.Styler uuid should be set before _compute is called.
61
+ _marshall_uuid(proto, styler, default_uuid)
62
+
63
+ # We're using protected members of pandas.Styler to get styles,
64
+ # which is not ideal and could break if the interface changes.
65
+ styler._compute() # type: ignore
66
+
67
+ pandas_styles = styler._translate(False, False) # type: ignore
68
+
69
+ _marshall_caption(proto, styler)
70
+ _marshall_styles(proto, styler, pandas_styles)
71
+ _marshall_display_values(proto, styler_data_df, pandas_styles)
72
+
73
+
74
+ def _marshall_uuid(proto: ArrowDataProto, styler: Styler, default_uuid: str) -> None:
75
+ """Marshall pandas.Styler uuid into an ArrowData proto.
76
+
77
+ Parameters
78
+ ----------
79
+ proto : proto.ArrowData
80
+ Output. The protobuf for Streamlit ArrowData proto.
81
+
82
+ styler : pandas.Styler
83
+ Helps style a DataFrame or Series according to the data with HTML and CSS.
84
+
85
+ default_uuid : str
86
+ If pandas.Styler uuid is not provided, this value will be used.
87
+
88
+ """
89
+ if styler.uuid is None: # type: ignore[attr-defined]
90
+ styler.set_uuid(default_uuid)
91
+
92
+ proto.styler.uuid = str(styler.uuid) # type: ignore[attr-defined]
93
+
94
+
95
+ def _marshall_caption(proto: ArrowDataProto, styler: Styler) -> None:
96
+ """Marshall pandas.Styler caption into an ArrowData proto.
97
+
98
+ Parameters
99
+ ----------
100
+ proto : proto.ArrowData
101
+ Output. The protobuf for Streamlit ArrowData proto.
102
+
103
+ styler : pandas.Styler
104
+ Helps style a DataFrame or Series according to the data with HTML and CSS.
105
+
106
+ """
107
+ if styler.caption is not None: # type: ignore[attr-defined]
108
+ proto.styler.caption = styler.caption # type: ignore[attr-defined]
109
+
110
+
111
+ def _marshall_styles(
112
+ proto: ArrowDataProto, styler: Styler, styles: Mapping[str, Any]
113
+ ) -> None:
114
+ """Marshall pandas.Styler styles into an ArrowData proto.
115
+
116
+ Parameters
117
+ ----------
118
+ proto : proto.ArrowData
119
+ Output. The protobuf for Streamlit ArrowData proto.
120
+
121
+ styler : pandas.Styler
122
+ Helps style a DataFrame or Series according to the data with HTML and CSS.
123
+
124
+ styles : dict
125
+ pandas.Styler translated styles.
126
+
127
+ """
128
+ css_rules = []
129
+
130
+ if "table_styles" in styles:
131
+ table_styles = styles["table_styles"]
132
+ table_styles = _trim_pandas_styles(table_styles)
133
+ for style in table_styles:
134
+ # styles in "table_styles" have a space
135
+ # between the uuid and selector.
136
+ rule = _pandas_style_to_css(
137
+ "table_styles",
138
+ style,
139
+ styler.uuid, # type: ignore[attr-defined]
140
+ separator=" ",
141
+ )
142
+ css_rules.append(rule)
143
+
144
+ if "cellstyle" in styles:
145
+ cellstyle = styles["cellstyle"]
146
+ cellstyle = _trim_pandas_styles(cellstyle)
147
+ for style in cellstyle:
148
+ rule = _pandas_style_to_css(
149
+ "cell_style",
150
+ style,
151
+ styler.uuid, # type: ignore[attr-defined]
152
+ separator="_",
153
+ )
154
+ css_rules.append(rule)
155
+
156
+ if len(css_rules) > 0:
157
+ proto.styler.styles = "\n".join(css_rules)
158
+
159
+
160
+ M = TypeVar("M", bound=Mapping[str, Any])
161
+
162
+
163
+ def _trim_pandas_styles(styles: list[M]) -> list[M]:
164
+ """Filter out empty styles.
165
+
166
+ Every cell will have a class, but the list of props
167
+ may just be [['', '']].
168
+
169
+ Parameters
170
+ ----------
171
+ styles : list
172
+ pandas.Styler translated styles.
173
+
174
+ """
175
+ return [x for x in styles if any(any(y) for y in x["props"])]
176
+
177
+
178
+ def _pandas_style_to_css(
179
+ style_type: str,
180
+ style: Mapping[str, Any],
181
+ uuid: str,
182
+ separator: str = "_",
183
+ ) -> str:
184
+ """Convert pandas.Styler translated style to CSS.
185
+
186
+ Parameters
187
+ ----------
188
+ style_type : str
189
+ Either "table_styles" or "cell_style".
190
+
191
+ style : dict
192
+ pandas.Styler translated style.
193
+
194
+ uuid : str
195
+ pandas.Styler uuid.
196
+
197
+ separator : str
198
+ A string separator used between table and cell selectors.
199
+
200
+ """
201
+ declarations = []
202
+ for css_property, css_value in style["props"]:
203
+ declaration = str(css_property).strip() + ": " + str(css_value).strip()
204
+ declarations.append(declaration)
205
+
206
+ table_selector = f"#T_{uuid}"
207
+
208
+ # In pandas >= 1.1.0
209
+ # translated_style["cellstyle"] has the following shape:
210
+ # > [
211
+ # > {
212
+ # > "props": [("color", " black"), ("background-color", "orange"), ("", "")],
213
+ # > "selectors": ["row0_col0"]
214
+ # > }
215
+ # > ...
216
+ # > ]
217
+ cell_selectors = (
218
+ [style["selector"]] if style_type == "table_styles" else style["selectors"]
219
+ )
220
+
221
+ selectors = [
222
+ table_selector + separator + cell_selector for cell_selector in cell_selectors
223
+ ]
224
+ selector = ", ".join(selectors)
225
+
226
+ declaration_block = "; ".join(declarations)
227
+ return selector + " { " + declaration_block + " }"
228
+
229
+
230
+ def _marshall_display_values(
231
+ proto: ArrowDataProto, df: DataFrame, styles: Mapping[str, Any]
232
+ ) -> None:
233
+ """Marshall pandas.Styler display values into an ArrowData proto.
234
+
235
+ Parameters
236
+ ----------
237
+ proto : proto.ArrowData
238
+ Output. The protobuf for Streamlit ArrowData proto.
239
+
240
+ df : pandas.DataFrame
241
+ A dataframe with original values.
242
+
243
+ styles : dict
244
+ pandas.Styler translated styles.
245
+
246
+ """
247
+ new_df = _use_display_values(df, styles)
248
+ proto.styler.display_values = dataframe_util.convert_pandas_df_to_arrow_bytes(
249
+ new_df
250
+ )
251
+
252
+
253
+ def _use_display_values(df: DataFrame, styles: Mapping[str, Any]) -> DataFrame:
254
+ """Create a new pandas.DataFrame where display values are used instead of original ones.
255
+
256
+ Parameters
257
+ ----------
258
+ df : pandas.DataFrame
259
+ A dataframe with original values.
260
+
261
+ styles : dict
262
+ pandas.Styler translated styles.
263
+
264
+ """
265
+ import re
266
+
267
+ # If values in a column are not of the same type, Arrow
268
+ # serialization would fail. Thus, we need to cast all values
269
+ # of the dataframe to strings before assigning them display values.
270
+
271
+ new_df = df.astype(str)
272
+ cell_selector_regex = re.compile(r"row(\d+)_col(\d+)")
273
+ # Outer key = column index; inner key = row index -> target string value
274
+ updates_by_col: defaultdict[int, dict[int, str]] = defaultdict(dict)
275
+ for row in styles.get("body", []):
276
+ for cell in row:
277
+ cell_id = cell.get("id")
278
+ if not cell_id:
279
+ continue
280
+ match = cell_selector_regex.match(cell_id)
281
+ if not match:
282
+ continue
283
+ row_idx, col_idx = map(int, match.groups())
284
+ display_value = cell.get("display_value")
285
+
286
+ str_value = (
287
+ str(display_value.value)
288
+ # Check if the display value is an Enum type. Enum values need to be
289
+ # converted to their `.value` attribute to ensure proper serialization
290
+ # and display logic.
291
+ if isinstance(display_value, Enum)
292
+ else str(display_value)
293
+ )
294
+ updates_by_col[col_idx][row_idx] = str_value
295
+
296
+ for col_idx, values_by_row in updates_by_col.items():
297
+ row_indices = list(values_by_row.keys())
298
+ values = list(values_by_row.values())
299
+ # Batch-assign updates for this column using iloc for performance.
300
+ new_df.iloc[row_indices, col_idx] = values
301
+
302
+ return new_df # type: ignore[no-any-return, unused-ignore]
@@ -0,0 +1,198 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import TYPE_CHECKING, Any, Final
18
+
19
+ from streamlit import config, errors, logger, runtime
20
+ from streamlit.elements.lib.form_utils import is_in_form
21
+ from streamlit.errors import (
22
+ StreamlitAPIWarning,
23
+ StreamlitFragmentWidgetsNotAllowedOutsideError,
24
+ StreamlitInvalidFormCallbackError,
25
+ StreamlitValueAssignmentNotAllowedError,
26
+ )
27
+ from streamlit.runtime.scriptrunner_utils.script_run_context import (
28
+ get_script_run_ctx,
29
+ in_cached_function,
30
+ )
31
+ from streamlit.runtime.state import WidgetCallback, get_session_state
32
+ from streamlit.runtime.state.common import require_valid_user_key
33
+
34
+ if TYPE_CHECKING:
35
+ from collections.abc import Sequence
36
+
37
+ from streamlit.delta_generator import DeltaGenerator
38
+
39
+
40
+ _LOGGER: Final = logger.get_logger(__name__)
41
+
42
+
43
+ def check_callback_rules(dg: DeltaGenerator, on_change: WidgetCallback | None) -> None:
44
+ """Ensures that widgets other than `st.form_submit_button` within a form don't have
45
+ an on_change callback set.
46
+
47
+ Raises
48
+ ------
49
+ StreamlitInvalidFormCallbackError:
50
+ Raised when the described rule is violated.
51
+ """
52
+
53
+ if runtime.exists() and is_in_form(dg) and on_change is not None:
54
+ raise StreamlitInvalidFormCallbackError()
55
+
56
+
57
+ _shown_default_value_warning: bool = False
58
+
59
+
60
+ def check_session_state_rules(
61
+ default_value: Any, key: str | None, writes_allowed: bool = True
62
+ ) -> None:
63
+ """Ensures that no values are set for widgets with the given key when writing
64
+ is not allowed.
65
+
66
+ Additionally, if `global.disableWidgetStateDuplicationWarning` is False a warning is
67
+ shown when a widget has a default value but its value is also set via session state.
68
+
69
+ Raises
70
+ ------
71
+ StreamlitAPIException:
72
+ Raised when the described rule is violated.
73
+ """
74
+ global _shown_default_value_warning # noqa: PLW0603
75
+
76
+ if key is None or not runtime.exists():
77
+ return
78
+
79
+ session_state = get_session_state()
80
+ if not session_state.is_new_state_value(key):
81
+ return
82
+
83
+ if not writes_allowed:
84
+ raise StreamlitValueAssignmentNotAllowedError(key=key)
85
+
86
+ if (
87
+ default_value is not None
88
+ and not _shown_default_value_warning
89
+ and not config.get_option("global.disableWidgetStateDuplicationWarning")
90
+ ):
91
+ from streamlit import warning
92
+
93
+ warning(
94
+ f'The widget with key "{key}" was created with a default value but'
95
+ " also had its value set via the Session State API."
96
+ )
97
+ _shown_default_value_warning = True
98
+
99
+
100
+ class CachedWidgetWarning(StreamlitAPIWarning):
101
+ def __init__(self) -> None:
102
+ super().__init__(
103
+ """
104
+ Your script uses a widget command in a cached function
105
+ (function decorated with `@st.cache_data` or `@st.cache_resource`).
106
+ This code will only be called when we detect a cache "miss",
107
+ which can lead to unexpected results.
108
+
109
+ To fix this, move all widget commands outside the cached function.
110
+ """
111
+ )
112
+
113
+
114
+ def check_cache_replay_rules() -> None:
115
+ """Check if a widget is allowed to be used in the current context.
116
+ More specifically, this checks if the current context is inside a
117
+ cached function that disallows widget usage. If so, it raises a warning.
118
+
119
+ If there are other similar checks in the future, we could extend this
120
+ function to check for those as well. And rename it to check_widget_usage_rules.
121
+ """
122
+ if in_cached_function.get():
123
+ from streamlit import exception
124
+
125
+ # We use an exception here to show a proper stack trace
126
+ # that indicates to the user where the issue is.
127
+ exception(CachedWidgetWarning())
128
+
129
+
130
+ def check_fragment_path_policy(dg: DeltaGenerator) -> None:
131
+ """Ensures that the current widget is not written outside of the
132
+ fragment's delta path.
133
+
134
+ Should be called by ever element that acts as a widget.
135
+ We don't allow writing widgets from within a widget to the outside path
136
+ because it can lead to unexpected behavior. For elements, this is okay
137
+ because they do not trigger a re-run.
138
+ """
139
+
140
+ ctx = get_script_run_ctx()
141
+ # Check is only relevant for fragments
142
+ if ctx is None or ctx.current_fragment_id is None:
143
+ return
144
+
145
+ current_fragment_delta_path = ctx.current_fragment_delta_path
146
+ current_cursor = dg._active_dg._cursor
147
+ if current_cursor is None:
148
+ return
149
+
150
+ current_cursor_delta_path = current_cursor.delta_path
151
+
152
+ # the elements delta path cannot be smaller than the fragment's delta path if it is
153
+ # inside of the fragment
154
+ if len(current_cursor_delta_path) < len(current_fragment_delta_path):
155
+ raise StreamlitFragmentWidgetsNotAllowedOutsideError()
156
+
157
+ # all path indices of the fragment-path must occur in the inner-elements delta path,
158
+ # otherwise it is outside of the fragment container
159
+ for index, path_index in enumerate(current_fragment_delta_path):
160
+ if current_cursor_delta_path[index] != path_index:
161
+ raise StreamlitFragmentWidgetsNotAllowedOutsideError()
162
+
163
+
164
+ def check_widget_policies(
165
+ dg: DeltaGenerator,
166
+ key: str | None,
167
+ on_change: WidgetCallback | None = None,
168
+ *,
169
+ default_value: Sequence[Any] | Any | None = None,
170
+ writes_allowed: bool = True,
171
+ enable_check_callback_rules: bool = True,
172
+ ) -> None:
173
+ """Check all widget policies for the given DeltaGenerator."""
174
+ check_fragment_path_policy(dg)
175
+ check_cache_replay_rules()
176
+ if enable_check_callback_rules:
177
+ check_callback_rules(dg, on_change)
178
+ if key is not None:
179
+ require_valid_user_key(key)
180
+ check_session_state_rules(
181
+ default_value=default_value, key=key, writes_allowed=writes_allowed
182
+ )
183
+
184
+
185
+ def maybe_raise_label_warnings(label: str | None, label_visibility: str | None) -> None:
186
+ if not label:
187
+ _LOGGER.warning(
188
+ "`label` got an empty value. This is discouraged for accessibility "
189
+ "reasons and may be disallowed in the future by raising an exception. "
190
+ "Please provide a non-empty label and hide it with label_visibility "
191
+ "if needed.",
192
+ stack_info=True,
193
+ )
194
+ if label_visibility not in {"visible", "hidden", "collapsed"}:
195
+ raise errors.StreamlitAPIException(
196
+ f"Unsupported label_visibility option '{label_visibility}'. "
197
+ f"Valid values are 'visible', 'hidden' or 'collapsed'."
198
+ )
@@ -0,0 +1,152 @@
1
+ # Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import (
19
+ Final,
20
+ )
21
+
22
+ from streamlit.errors import (
23
+ StreamlitAPIException,
24
+ )
25
+
26
+ _MODIFIER_ALIASES: Final[dict[str, str]] = {
27
+ "ctrl": "ctrl",
28
+ "control": "ctrl",
29
+ "cmd": "cmd",
30
+ "command": "cmd",
31
+ "meta": "cmd",
32
+ "alt": "alt",
33
+ "option": "alt",
34
+ "shift": "shift",
35
+ "mod": "ctrl",
36
+ }
37
+
38
+ _MODIFIER_ORDER: Final[tuple[str, ...]] = ("ctrl", "cmd", "alt", "shift")
39
+
40
+ _KEY_ALIASES: Final[dict[str, str]] = {
41
+ "enter": "enter",
42
+ "return": "enter",
43
+ "space": "space",
44
+ "spacebar": "space",
45
+ "tab": "tab",
46
+ "escape": "escape",
47
+ "esc": "escape",
48
+ "backspace": "backspace",
49
+ "delete": "delete",
50
+ "del": "delete",
51
+ "home": "home",
52
+ "end": "end",
53
+ "pageup": "pageup",
54
+ "pagedown": "pagedown",
55
+ "left": "left",
56
+ "arrowleft": "left",
57
+ "right": "right",
58
+ "arrowright": "right",
59
+ "up": "up",
60
+ "arrowup": "up",
61
+ "down": "down",
62
+ "arrowdown": "down",
63
+ }
64
+
65
+ _RESERVED_KEYS: Final[set[str]] = {"c", "r"}
66
+
67
+
68
+ def _normalize_key_token(lower_token: str) -> str:
69
+ """Normalize a key token to a format that can be used on the client side."""
70
+
71
+ if lower_token in _KEY_ALIASES:
72
+ return _KEY_ALIASES[lower_token]
73
+
74
+ if len(lower_token) == 1 and lower_token.isalnum():
75
+ return lower_token
76
+
77
+ if lower_token.startswith("f") and lower_token[1:].isdigit():
78
+ return lower_token
79
+
80
+ raise StreamlitAPIException(
81
+ "shortcut must include a single character or one of the supported keys "
82
+ "(e.g. Enter, Space, Tab, Escape)."
83
+ )
84
+
85
+
86
+ def normalize_shortcut(shortcut: str) -> str:
87
+ """Normalize a shortcut string to a format that can be used on the client side.
88
+
89
+ Parameters
90
+ ----------
91
+ shortcut : str
92
+ The shortcut string to normalize.
93
+
94
+ Returns
95
+ -------
96
+ str
97
+ The normalized shortcut string.
98
+
99
+ Raises
100
+ ------
101
+ StreamlitAPIException
102
+ If the shortcut is not a string value.
103
+ If the shortcut does not contain at least one key or modifier.
104
+ If the shortcut contains a single non-modifier key.
105
+ If the shortcut uses the keys 'C' or 'R', with or without modifiers.
106
+ If the shortcut does not include a non-modifier key.
107
+ """
108
+ if not isinstance(shortcut, str):
109
+ raise StreamlitAPIException("shortcut must be a string value.")
110
+
111
+ tokens = [token.strip() for token in shortcut.split("+") if token.strip()]
112
+ if not tokens:
113
+ raise StreamlitAPIException(
114
+ "The `shortcut` must contain at least one key or modifier."
115
+ )
116
+
117
+ modifiers: list[str] = []
118
+ key: str | None = None
119
+
120
+ for raw_token in tokens:
121
+ lower_token = raw_token.lower()
122
+ if lower_token in _MODIFIER_ALIASES:
123
+ normalized_modifier = _MODIFIER_ALIASES[lower_token]
124
+ if normalized_modifier not in modifiers:
125
+ modifiers.append(normalized_modifier)
126
+ continue
127
+
128
+ if key is not None:
129
+ raise StreamlitAPIException(
130
+ "The `shortcut` may only specify a single non-modifier key."
131
+ )
132
+
133
+ normalized_key = _normalize_key_token(lower_token)
134
+ if normalized_key in _RESERVED_KEYS:
135
+ raise StreamlitAPIException(
136
+ "The `shortcut` cannot use the keys 'C' or 'R', with or without modifiers."
137
+ )
138
+
139
+ key = normalized_key
140
+
141
+ if key is None:
142
+ raise StreamlitAPIException(
143
+ "The `shortcut` must include a non-modifier key such as 'K' or 'Ctrl+K'."
144
+ )
145
+
146
+ normalized_tokens: list[str] = [
147
+ modifier for modifier in _MODIFIER_ORDER if modifier in modifiers
148
+ ]
149
+ if key is not None:
150
+ normalized_tokens.append(key)
151
+
152
+ return "+".join(normalized_tokens)