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
streamlit/type_util.py ADDED
@@ -0,0 +1,480 @@
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
+ """A bunch of useful utilities for dealing with types."""
16
+
17
+ from __future__ import annotations
18
+
19
+ import dataclasses
20
+ import re
21
+ import types
22
+ from collections import UserList, deque
23
+ from collections.abc import (
24
+ AsyncGenerator,
25
+ Generator,
26
+ ItemsView,
27
+ Iterable,
28
+ KeysView,
29
+ Mapping,
30
+ Sequence,
31
+ ValuesView,
32
+ )
33
+ from enum import EnumMeta
34
+ from typing import (
35
+ TYPE_CHECKING,
36
+ Any,
37
+ Final,
38
+ Literal,
39
+ NamedTuple,
40
+ Protocol,
41
+ TypeAlias,
42
+ TypeGuard,
43
+ overload,
44
+ )
45
+
46
+ from streamlit.errors import StreamlitAPIException
47
+
48
+ if TYPE_CHECKING:
49
+ import graphviz
50
+ import sympy
51
+ from plotly.graph_objs import Figure
52
+ from pydeck import Deck
53
+
54
+ from streamlit.delta_generator import DeltaGenerator
55
+
56
+ # we define our own type here because mypy doesn't seem to support the shape type and
57
+ # reports unreachable code. When mypy supports it, we can remove this custom type.
58
+ NumpyShape: TypeAlias = tuple[int, ...]
59
+
60
+
61
+ class SupportsStr(Protocol):
62
+ def __str__(self) -> str: ...
63
+
64
+
65
+ class SupportsReprHtml(Protocol):
66
+ def _repr_html_(self) -> str: ...
67
+
68
+
69
+ class CustomDict(Protocol):
70
+ """Protocol for Streamlit native custom dictionaries (e.g. session state, secrets, query params).
71
+ that can be converted to a dict.
72
+
73
+ All these implementations should provide a to_dict method.
74
+ """
75
+
76
+ def to_dict(self) -> dict[str, Any]: ...
77
+
78
+
79
+ @overload
80
+ def is_type(
81
+ obj: object, fqn_type_pattern: Literal["pydeck.bindings.deck.Deck"]
82
+ ) -> TypeGuard[Deck]: ...
83
+
84
+
85
+ @overload
86
+ def is_type(
87
+ obj: object, fqn_type_pattern: Literal["plotly.graph_objs._figure.Figure"]
88
+ ) -> TypeGuard[Figure]: ...
89
+
90
+
91
+ @overload
92
+ def is_type(obj: object, fqn_type_pattern: str | re.Pattern[str]) -> bool: ...
93
+
94
+
95
+ def is_type(obj: object, fqn_type_pattern: str | re.Pattern[str]) -> bool:
96
+ """Check type without importing expensive modules.
97
+
98
+ Parameters
99
+ ----------
100
+ obj : object
101
+ The object to type-check.
102
+ fqn_type_pattern : str or regex
103
+ The fully-qualified type string or a regular expression.
104
+ Regexes should start with `^` and end with `$`.
105
+
106
+ Example
107
+ -------
108
+
109
+ To check whether something is a Matplotlib Figure without importing
110
+ matplotlib, use:
111
+
112
+ >>> is_type(foo, "matplotlib.figure.Figure")
113
+
114
+ """
115
+ fqn_type = get_fqn_type(obj)
116
+ if isinstance(fqn_type_pattern, str):
117
+ return fqn_type_pattern == fqn_type
118
+ return fqn_type_pattern.match(fqn_type) is not None
119
+
120
+
121
+ def _is_type_instance(obj: object, type_to_check: str) -> bool:
122
+ """Check if instance of type without importing expensive modules."""
123
+ return type_to_check in [get_fqn(t) for t in type(obj).__mro__]
124
+
125
+
126
+ def get_object_name(obj: object) -> str:
127
+ """Get a simplified name of the given object."""
128
+ if hasattr(obj, "__qualname__") and isinstance(obj.__qualname__, str):
129
+ return obj.__qualname__
130
+ if hasattr(obj, "__name__") and isinstance(obj.__name__, str):
131
+ return obj.__name__
132
+
133
+ return type(obj).__qualname__
134
+
135
+
136
+ def get_fqn(the_type: type) -> str:
137
+ """Get module.type_name for a given type."""
138
+ return f"{the_type.__module__}.{the_type.__qualname__}"
139
+
140
+
141
+ def get_fqn_type(obj: object) -> str:
142
+ """Get module.type_name for a given object."""
143
+ return get_fqn(type(obj))
144
+
145
+
146
+ _BYTES_LIKE_TYPES: Final[tuple[type, ...]] = (
147
+ bytes,
148
+ bytearray,
149
+ )
150
+
151
+ BytesLike: TypeAlias = bytes | bytearray
152
+
153
+
154
+ def is_bytes_like(obj: object) -> TypeGuard[BytesLike]:
155
+ """True if the type is considered bytes-like for the purposes of
156
+ protobuf data marshalling.
157
+ """
158
+ return isinstance(obj, _BYTES_LIKE_TYPES)
159
+
160
+
161
+ def to_bytes(obj: BytesLike) -> bytes:
162
+ """Converts the given object to bytes.
163
+
164
+ Only types for which `is_bytes_like` is true can be converted; anything
165
+ else will result in an exception.
166
+ """
167
+ if isinstance(obj, bytearray):
168
+ return bytes(obj)
169
+ if isinstance(obj, bytes):
170
+ return obj
171
+
172
+ raise RuntimeError(f"{obj} is not convertible to bytes")
173
+
174
+
175
+ _SYMPY_RE: Final = re.compile(r"^sympy.*$")
176
+
177
+
178
+ def is_sympy_expression(obj: object) -> TypeGuard[sympy.Expr]:
179
+ """True if input is a SymPy expression."""
180
+ if not is_type(obj, _SYMPY_RE):
181
+ return False
182
+
183
+ try:
184
+ import sympy
185
+
186
+ return isinstance(obj, sympy.Expr)
187
+ except ImportError:
188
+ return False
189
+
190
+
191
+ _ALTAIR_RE: Final = re.compile(r"^altair\.vegalite\.v\d+\.api\.\w*Chart$")
192
+
193
+
194
+ def is_altair_chart(obj: object) -> bool:
195
+ """True if input looks like an Altair chart."""
196
+ return is_type(obj, _ALTAIR_RE)
197
+
198
+
199
+ _PILLOW_RE: Final = re.compile(r"^PIL\..*")
200
+
201
+
202
+ def is_pillow_image(obj: object) -> bool:
203
+ """True if input looks like a pillow image."""
204
+ return is_type(obj, _PILLOW_RE)
205
+
206
+
207
+ def is_keras_model(obj: object) -> bool:
208
+ """True if input looks like a Keras model."""
209
+ return (
210
+ is_type(obj, "keras.engine.sequential.Sequential")
211
+ or is_type(obj, "keras.engine.training.Model")
212
+ or is_type(obj, "tensorflow.python.keras.engine.sequential.Sequential")
213
+ or is_type(obj, "tensorflow.python.keras.engine.training.Model")
214
+ )
215
+
216
+
217
+ # We use a regex here to allow potential changes in the module path in the future.
218
+ _OPENAI_CHUNK_RE: Final = re.compile(r"^openai\..+\.ChatCompletionChunk$")
219
+
220
+
221
+ def is_openai_chunk(obj: object) -> bool:
222
+ """True if input looks like an OpenAI chat completion chunk."""
223
+ return is_type(obj, _OPENAI_CHUNK_RE)
224
+
225
+
226
+ def is_plotly_chart(obj: object) -> TypeGuard[Figure | list[Any] | dict[str, Any]]:
227
+ """True if input looks like a Plotly chart."""
228
+ return (
229
+ is_type(obj, "plotly.graph_objs._figure.Figure")
230
+ or _is_list_of_plotly_objs(obj)
231
+ or _is_probably_plotly_dict(obj)
232
+ )
233
+
234
+
235
+ def is_graphviz_chart(
236
+ obj: object,
237
+ ) -> TypeGuard[graphviz.Graph | graphviz.Digraph | graphviz.Source]:
238
+ """True if input looks like a GraphViz chart."""
239
+ return (
240
+ # In GraphViz < 0.18
241
+ is_type(obj, "graphviz.dot.Graph")
242
+ or is_type(obj, "graphviz.dot.Digraph")
243
+ # In GraphViz >= 0.18
244
+ or is_type(obj, "graphviz.graphs.Graph")
245
+ or is_type(obj, "graphviz.graphs.Digraph")
246
+ or is_type(obj, "graphviz.sources.Source")
247
+ )
248
+
249
+
250
+ def _is_plotly_obj(obj: object) -> bool:
251
+ """True if input if from a type that lives in plotly.plotly_objs."""
252
+ the_type = type(obj)
253
+ return the_type.__module__.startswith("plotly.graph_objs")
254
+
255
+
256
+ def _is_list_of_plotly_objs(obj: object) -> TypeGuard[list[Any]]:
257
+ if not isinstance(obj, list):
258
+ return False
259
+ if len(obj) == 0:
260
+ return False
261
+ return all(_is_plotly_obj(item) for item in obj)
262
+
263
+
264
+ def _is_probably_plotly_dict(obj: object) -> TypeGuard[dict[str, Any]]:
265
+ if not isinstance(obj, dict):
266
+ return False
267
+
268
+ if len(obj.keys()) == 0:
269
+ return False
270
+
271
+ if any(k not in {"config", "data", "frames", "layout"} for k in obj):
272
+ return False
273
+
274
+ if any(_is_plotly_obj(v) for v in obj.values()):
275
+ return True
276
+
277
+ return bool(any(_is_list_of_plotly_objs(v) for v in obj.values()))
278
+
279
+
280
+ def is_delta_generator(obj: object) -> TypeGuard[DeltaGenerator]:
281
+ """True if input looks like a DeltaGenerator."""
282
+
283
+ # We are using a string here to avoid circular import warnings
284
+ # when importing DeltaGenerator.
285
+ return is_type(obj, "streamlit.delta_generator.DeltaGenerator")
286
+
287
+
288
+ def is_function(x: object) -> TypeGuard[types.FunctionType]:
289
+ """Return True if x is a function."""
290
+ return isinstance(x, types.FunctionType)
291
+
292
+
293
+ def has_callable_attr(obj: object, name: str) -> bool:
294
+ """True if obj has the specified attribute that is callable."""
295
+ return (
296
+ hasattr(obj, name)
297
+ and callable(getattr(obj, name))
298
+ # DeltaGenerator will return a callable wrapper for any method name,
299
+ # even if it doesn't exist.
300
+ and not is_delta_generator(obj)
301
+ )
302
+
303
+
304
+ def is_namedtuple(x: object) -> TypeGuard[NamedTuple]:
305
+ """True if obj is an instance of a namedtuple."""
306
+ return isinstance(x, tuple) and has_callable_attr(x, "_asdict")
307
+
308
+
309
+ def is_dataclass_instance(obj: object) -> bool:
310
+ """True if obj is an instance of a dataclass."""
311
+ # The not isinstance(obj, type) check is needed to make sure that this
312
+ # is an instance of a dataclass and not the class itself.
313
+ # dataclasses.is_dataclass returns True for either instance or class.
314
+ return dataclasses.is_dataclass(obj) and not isinstance(obj, type)
315
+
316
+
317
+ def is_pydeck(obj: object) -> TypeGuard[Deck]:
318
+ """True if input looks like a pydeck chart."""
319
+ return is_type(obj, "pydeck.bindings.deck.Deck")
320
+
321
+
322
+ def is_pydantic_model(obj: object) -> bool:
323
+ """True if input looks like a Pydantic model instance."""
324
+
325
+ if isinstance(obj, type):
326
+ # The obj is a class, but we
327
+ # only want to check for instances
328
+ # of Pydantic models, so we return False.
329
+ return False
330
+
331
+ return _is_type_instance(obj, "pydantic.main.BaseModel")
332
+
333
+
334
+ def is_sequence_of_pydantic_models(obj: object) -> TypeGuard[Sequence[Any]]:
335
+ """True if obj is a non-empty list/tuple/set/frozenset of Pydantic model instances."""
336
+ if not isinstance(obj, (list, tuple, set, frozenset)) or len(obj) == 0:
337
+ return False
338
+ first_element = next(iter(obj))
339
+ return is_pydantic_model(first_element)
340
+
341
+
342
+ def dump_pydantic_sequence(obj: Sequence[object]) -> list[dict[str, Any]]:
343
+ """Dump a sequence of Pydantic models to a list of dictionaries."""
344
+ first_element = next(iter(obj))
345
+ # Pydantic v2 uses model_dump(), v1 uses dict()
346
+ if has_callable_attr(first_element, "model_dump"):
347
+ # Use mode="json" to ensure proper serialization of types like Decimal
348
+ return [item.model_dump(mode="json") for item in obj] # type: ignore
349
+ return [item.dict() for item in obj] # type: ignore
350
+
351
+
352
+ def _is_from_streamlit(obj: object) -> bool:
353
+ """True if the object is from the streamlit package."""
354
+ return obj.__class__.__module__.startswith("streamlit")
355
+
356
+
357
+ def is_custom_dict(obj: object) -> TypeGuard[CustomDict]:
358
+ """True if input looks like one of the Streamlit custom dictionaries."""
359
+ return (
360
+ isinstance(obj, Mapping)
361
+ and _is_from_streamlit(obj)
362
+ and has_callable_attr(obj, "to_dict")
363
+ )
364
+
365
+
366
+ def is_iterable(obj: object) -> TypeGuard[Iterable[Any]]:
367
+ try:
368
+ # The ignore statement here is intentional, as this is a
369
+ # perfectly fine way of checking for iterables.
370
+ iter(obj) # type: ignore[call-overload]
371
+ except TypeError:
372
+ return False
373
+ return True
374
+
375
+
376
+ def is_list_like(obj: object) -> TypeGuard[Sequence[Any]]:
377
+ """True if input looks like a list."""
378
+ import array
379
+
380
+ if isinstance(obj, str):
381
+ return False
382
+
383
+ if isinstance(obj, (list, set, tuple)):
384
+ # Optimization to check the most common types first
385
+ return True
386
+
387
+ return isinstance(
388
+ obj,
389
+ (
390
+ array.ArrayType,
391
+ deque,
392
+ EnumMeta,
393
+ enumerate,
394
+ frozenset,
395
+ ItemsView,
396
+ KeysView,
397
+ map,
398
+ range,
399
+ UserList,
400
+ ValuesView,
401
+ ),
402
+ )
403
+
404
+
405
+ def check_python_comparable(seq: Sequence[Any]) -> None:
406
+ """Check if the sequence elements support "python comparison".
407
+ That means that the equality operator (==) returns a boolean value.
408
+ Which is not True for e.g. numpy arrays and pandas series.
409
+ """
410
+ try:
411
+ bool(seq[0] == seq[0])
412
+ except LookupError:
413
+ # In case of empty sequences, the check not raise an exception.
414
+ pass
415
+ except ValueError:
416
+ raise StreamlitAPIException(
417
+ "Invalid option type provided. Options must be comparable, returning a "
418
+ f"boolean when used with *==*. \n\nGot **{type(seq[0]).__name__}**, "
419
+ "which cannot be compared. Refactor your code to use elements of "
420
+ "comparable types as options, e.g. use indices instead."
421
+ )
422
+
423
+
424
+ def is_altair_version_less_than(v: str) -> bool:
425
+ """Return True if the current Altair version is less than the input version.
426
+
427
+ Parameters
428
+ ----------
429
+ v : str
430
+ Version string, e.g. "0.25.0"
431
+
432
+ Returns
433
+ -------
434
+ bool
435
+
436
+
437
+ Raises
438
+ ------
439
+ InvalidVersion
440
+ If the version strings are not valid.
441
+
442
+ """
443
+ import altair as alt
444
+
445
+ return is_version_less_than(alt.__version__, v)
446
+
447
+
448
+ def is_version_less_than(v1: str, v2: str) -> bool:
449
+ """Return True if the v1 version string is less than the v2 version string
450
+ based on semantic versioning.
451
+
452
+ Raises
453
+ ------
454
+ InvalidVersion
455
+ If the version strings are not valid.
456
+ """
457
+ from packaging import version
458
+
459
+ return version.parse(v1) < version.parse(v2)
460
+
461
+
462
+ def async_generator_to_sync(
463
+ async_gen: AsyncGenerator[Any, Any],
464
+ ) -> Generator[Any, Any, Any]:
465
+ """Convert an async generator to a synchronous generator."""
466
+ import asyncio
467
+
468
+ # Create a new event loop.
469
+ # It is expected that there is no existing event loop in the user thread.
470
+ loop = asyncio.new_event_loop()
471
+
472
+ try:
473
+ # Iterate over the async generator until it raises StopAsyncIteration
474
+ while True:
475
+ yield loop.run_until_complete(anext(async_gen))
476
+ except StopAsyncIteration:
477
+ # The async generator has finished
478
+ pass
479
+ finally:
480
+ loop.close()
streamlit/url_util.py ADDED
@@ -0,0 +1,120 @@
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
+ import re
18
+ from typing import Final, Literal, TypeAlias
19
+ from urllib.parse import urlparse
20
+
21
+ UrlSchema: TypeAlias = Literal["http", "https", "mailto", "data"]
22
+
23
+
24
+ # Regular expression for process_gitblob_url
25
+ _GITBLOB_RE: Final = re.compile(
26
+ r"(?P<base>https:\/\/?(gist\.)?github.com\/)"
27
+ r"(?P<account>([\w\.]+\/){1,2})"
28
+ r"(?P<blob_or_raw>(blob|raw))?"
29
+ r"(?P<suffix>(.+)?)"
30
+ )
31
+
32
+
33
+ def process_gitblob_url(url: str) -> str:
34
+ """Check url to see if it describes a GitHub Gist "blob" URL.
35
+
36
+ If so, returns a new URL to get the "raw" script.
37
+ If not, returns URL unchanged.
38
+ """
39
+ # Matches github.com and gist.github.com. Will not match githubusercontent.com.
40
+ # See this regex with explainer and sample text here: https://regexr.com/4odk3
41
+ match = _GITBLOB_RE.match(url)
42
+ if match:
43
+ mdict = match.groupdict()
44
+ # If it has "blob" in the url, replace this with "raw" and we're done.
45
+ if mdict["blob_or_raw"] == "blob":
46
+ return "{base}{account}raw{suffix}".format(**mdict)
47
+
48
+ # If it is a "raw" url already, return untouched.
49
+ if mdict["blob_or_raw"] == "raw":
50
+ return url
51
+
52
+ # It's a gist. Just tack "raw" on the end.
53
+ return url + "/raw"
54
+
55
+ return url
56
+
57
+
58
+ def get_hostname(url: str) -> str | None:
59
+ """Return the hostname of a URL (with or without protocol)."""
60
+ # Just so urllib can parse the URL, make sure there's a protocol.
61
+ # (The actual protocol doesn't matter to us)
62
+ if "://" not in url:
63
+ url = f"http://{url}"
64
+
65
+ parsed = urlparse(url)
66
+ return parsed.hostname
67
+
68
+
69
+ def is_url(
70
+ url: str,
71
+ allowed_schemas: tuple[UrlSchema, ...] = ("http", "https"),
72
+ ) -> bool:
73
+ """Check if a string looks like an URL.
74
+
75
+ This doesn't check if the URL is actually valid or reachable.
76
+
77
+ Parameters
78
+ ----------
79
+ url : str
80
+ The URL to check.
81
+
82
+ allowed_schemas : Tuple[str]
83
+ The allowed URL schemas. Default is ("http", "https").
84
+ """
85
+ try:
86
+ result = urlparse(str(url))
87
+ if result.scheme not in allowed_schemas:
88
+ return False
89
+
90
+ if result.scheme in {"http", "https"}:
91
+ return bool(result.netloc)
92
+ if result.scheme in {"mailto", "data"}:
93
+ return bool(result.path)
94
+
95
+ except ValueError:
96
+ return False
97
+ return False
98
+
99
+
100
+ def make_url_path(base_url: str, path: str) -> str:
101
+ """Make a URL from a base URL and a path.
102
+
103
+ Parameters
104
+ ----------
105
+ base_url : str
106
+ The base URL.
107
+ path : str
108
+ The path to append to the base URL.
109
+
110
+ Returns
111
+ -------
112
+ str
113
+ The resulting URL.
114
+ """
115
+ base_url = base_url.strip("/")
116
+ if base_url:
117
+ base_url = "/" + base_url
118
+
119
+ path = path.lstrip("/")
120
+ return f"{base_url}/{path}"