streamlit-nightly 1.53.1.dev20260115__py3-none-any.whl → 1.53.1.dev20260117__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.
- streamlit/auth_util.py +90 -1
- streamlit/cli_util.py +2 -1
- streamlit/commands/echo.py +2 -2
- streamlit/commands/execution_control.py +1 -1
- streamlit/commands/navigation.py +1 -1
- streamlit/components/types/base_custom_component.py +0 -2
- streamlit/components/v1/custom_component.py +0 -2
- streamlit/components/v2/component_path_utils.py +1 -1
- streamlit/components/v2/manifest_scanner.py +8 -3
- streamlit/components/v2/presentation.py +1 -1
- streamlit/config.py +15 -13
- streamlit/config_util.py +5 -5
- streamlit/dataframe_util.py +5 -5
- streamlit/elements/arrow.py +11 -6
- streamlit/elements/deck_gl_json_chart.py +1 -1
- streamlit/elements/exception.py +4 -2
- streamlit/elements/form.py +1 -1
- streamlit/elements/layouts.py +1 -1
- streamlit/elements/lib/built_in_chart_utils.py +7 -7
- streamlit/elements/lib/column_config_utils.py +6 -6
- streamlit/elements/lib/dialog.py +1 -1
- streamlit/elements/lib/image_utils.py +4 -4
- streamlit/elements/lib/layout_utils.py +1 -1
- streamlit/elements/lib/policies.py +1 -1
- streamlit/elements/lib/utils.py +1 -1
- streamlit/elements/map.py +6 -6
- streamlit/elements/plotly_chart.py +2 -2
- streamlit/elements/toast.py +1 -1
- streamlit/elements/vega_charts.py +2 -2
- streamlit/elements/widgets/button.py +3 -3
- streamlit/elements/widgets/button_group.py +3 -3
- streamlit/elements/widgets/chat.py +1 -1
- streamlit/elements/widgets/data_editor.py +6 -6
- streamlit/elements/widgets/number_input.py +1 -1
- streamlit/elements/widgets/slider.py +5 -5
- streamlit/elements/widgets/time_widgets.py +91 -10
- streamlit/elements/write.py +1 -1
- streamlit/env_util.py +1 -1
- streamlit/errors.py +0 -14
- streamlit/runtime/app_session.py +0 -1
- streamlit/runtime/caching/cache_data_api.py +3 -3
- streamlit/runtime/caching/cache_errors.py +0 -2
- streamlit/runtime/caching/cache_resource_api.py +1 -1
- streamlit/runtime/caching/cache_utils.py +2 -2
- streamlit/runtime/caching/hashing.py +1 -3
- streamlit/runtime/caching/storage/cache_storage_protocol.py +0 -3
- streamlit/runtime/connection_factory.py +1 -1
- streamlit/runtime/runtime.py +6 -6
- streamlit/runtime/scriptrunner_utils/exceptions.py +0 -4
- streamlit/runtime/secrets.py +2 -3
- streamlit/runtime/state/session_state.py +1 -1
- streamlit/runtime/stats.py +0 -7
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +300 -300
- streamlit/static/static/js/{ErrorOutline.esm.CiSfK8ht.js → ErrorOutline.esm.CqVEQ-uJ.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.ItNjcEfs.js → FileDownload.esm.G-HJG6nt.js} +1 -1
- streamlit/static/static/js/{FileHelper.xS7f19FE.js → FileHelper.Bd4bPp0Y.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.N8_BCinn.js → FormClearHelper.B74VElyu.js} +1 -1
- streamlit/static/static/js/{InputInstructions.CrsdK7CQ.js → InputInstructions.ClBLOwfm.js} +1 -1
- streamlit/static/static/js/{Particles.BDlTHC3I.js → Particles.CyYskark.js} +1 -1
- streamlit/static/static/js/{ProgressBar.Bk2qeHfS.js → ProgressBar.vWX1Igzt.js} +1 -1
- streamlit/static/static/js/{StreamlitSyntaxHighlighter.BcuPrPcw.js → StreamlitSyntaxHighlighter.BNhyeEtl.js} +1 -1
- streamlit/static/static/js/{TableChart.esm.CP7XPz8I.js → TableChart.esm.Mk-Hvy1t.js} +1 -1
- streamlit/static/static/js/{Toolbar.DCJcLwve.js → Toolbar.NhlhlTzK.js} +1 -1
- streamlit/static/static/js/{WidgetLabelHelpIconInline.BPqxu1b-.js → WidgetLabelHelpIconInline.Ch9VDnrM.js} +1 -1
- streamlit/static/static/js/{base-input.x4muJJ13.js → base-input.DSh9H2uv.js} +1 -1
- streamlit/static/static/js/{checkbox.C5r4AIaH.js → checkbox.DCFAQrRB.js} +1 -1
- streamlit/static/static/js/{createDownloadLinkElement.C83kkEeT.js → createDownloadLinkElement.D5zpcd24.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.0sOWm4IB.js → data-grid-overlay-editor.GAqbIurd.js} +1 -1
- streamlit/static/static/js/{downloader.C_6FfoEv.js → downloader.C1MBZFo2.js} +1 -1
- streamlit/static/static/js/{embed.BnJ6-SK4.js → embed.CZG6oOsc.js} +1 -1
- streamlit/static/static/js/{es6.44_BWfFd.js → es6.toHnBzIb.js} +2 -2
- streamlit/static/static/js/{formatNumber.DhHZAosz.js → formatNumber.BsU-5o3V.js} +1 -1
- streamlit/static/static/js/{iconPosition.shOoQuRR.js → iconPosition.OKOYrH6A.js} +1 -1
- streamlit/static/static/js/{iframeResizer.contentWindow.savEE5Vs.js → iframeResizer.contentWindow.D6yxU4DL.js} +1 -1
- streamlit/static/static/js/{index.SjKkVSL7.js → index.-pgbbiGJ.js} +1 -1
- streamlit/static/static/js/{index.Dblsdn8d.js → index.0iCXW13-.js} +1 -1
- streamlit/static/static/js/{index.DfHm3P22.js → index.1sBZt_1I.js} +5 -5
- streamlit/static/static/js/{index.BCaCSVj6.js → index.5CwvMIZq.js} +1 -1
- streamlit/static/static/js/{index.CySNwKoN.js → index.AkleAg_w.js} +1 -1
- streamlit/static/static/js/{index.hX8_3tMP.js → index.B8mzpc1l.js} +1 -1
- streamlit/static/static/js/{index.Ch1TdQMG.js → index.BHWCIpbw.js} +1 -1
- streamlit/static/static/js/{index.BNzuyXeN.js → index.BU8aUWlo.js} +1 -1
- streamlit/static/static/js/{index.C6uOmY9O.js → index.BVbT0yWJ.js} +1 -1
- streamlit/static/static/js/{index.CQgiNpC7.js → index.BW0F_Pbs.js} +1 -1
- streamlit/static/static/js/{index.BYY3iuVE.js → index.BXqtIpgi.js} +1 -1
- streamlit/static/static/js/{index.SVJGpnpy.js → index.Bh0_7PvD.js} +1 -1
- streamlit/static/static/js/{index.BHpLZ7X6.js → index.Bhr3IHkD.js} +1 -1
- streamlit/static/static/js/{index.B6ewTTej.js → index.BkYhb--A.js} +1 -1
- streamlit/static/static/js/{index.DIsNrJpC.js → index.Blo8DAcU.js} +1 -1
- streamlit/static/static/js/{index.DcdD0ROn.js → index.Bs4E_0D7.js} +1 -1
- streamlit/static/static/js/{index.AdijUi9Z.js → index.C6LOxzFC.js} +1 -1
- streamlit/static/static/js/{index.B8Of3XXR.js → index.CGt5hnLi.js} +1 -1
- streamlit/static/static/js/{index.jZin0w6_.js → index.CJCEiqv2.js} +1 -1
- streamlit/static/static/js/{index.B5HlOnvq.js → index.COcj-WpN.js} +1 -1
- streamlit/static/static/js/{index.BNvP8E5Q.js → index.CV4niwLB.js} +1 -1
- streamlit/static/static/js/{index.C1coXTgf.js → index.CXLtvxrQ.js} +1 -1
- streamlit/static/static/js/{index.pzQTLx9j.js → index.C_w1nWis.js} +1 -1
- streamlit/static/static/js/{index.BZXk3Qxq.js → index.Ch4X_YdF.js} +1 -1
- streamlit/static/static/js/{index.DBtWixG1.js → index.CqywI2eW.js} +1 -1
- streamlit/static/static/js/{index.D_ziFad8.js → index.D0h2EJ_T.js} +1 -1
- streamlit/static/static/js/{index.D8BQH06Z.js → index.D7Hgmilz.js} +1 -1
- streamlit/static/static/js/{index.HjydQ4bj.js → index.D94k70Hg.js} +1 -1
- streamlit/static/static/js/{index.CF88nImM.js → index.DGrQJk9x.js} +1 -1
- streamlit/static/static/js/{index.CFlGfFvq.js → index.DJ8OTQbk.js} +1 -1
- streamlit/static/static/js/{index.BqtgAGnG.js → index.DR8Ty3j4.js} +1 -1
- streamlit/static/static/js/{index.BT7o4A9V.js → index.DRGDE5oo.js} +1 -1
- streamlit/static/static/js/{index.BhQvq6YD.js → index.DbCS2N3T.js} +1 -1
- streamlit/static/static/js/{index.BZI7jTDf.js → index.DhGqPx_f.js} +1 -1
- streamlit/static/static/js/{index.BkIReLGJ.js → index.Dl2MB5Tx.js} +1 -1
- streamlit/static/static/js/{index.BwnLQnAk.js → index.Dq0ZbGYZ.js} +1 -1
- streamlit/static/static/js/{index.DvZDNiBV.js → index.DwgEkDLk.js} +1 -1
- streamlit/static/static/js/{index.CrD2lwZG.js → index.E4x2UBkh.js} +1 -1
- streamlit/static/static/js/{index.D3LEkXqN.js → index.EOvPT8N-.js} +1 -1
- streamlit/static/static/js/{index.DO3HICut.js → index.MZ7ugsN-.js} +1 -1
- streamlit/static/static/js/{index.Bl7SMZUw.js → index.P0_E3iNi.js} +1 -1
- streamlit/static/static/js/{index.s44_OVd6.js → index.SBwNLdli.js} +1 -1
- streamlit/static/static/js/{index.BE7Rhig1.js → index.XgBfXgN1.js} +1 -1
- streamlit/static/static/js/{index.Ck2mQvBi.js → index.YRiVxrFw.js} +1 -1
- streamlit/static/static/js/{index.CZJkiRFm.js → index._t3w-7R0.js} +1 -1
- streamlit/static/static/js/{index.HsxzYHNf.js → index.dGk9EWLh.js} +1 -1
- streamlit/static/static/js/{index.pQvPlPcA.js → index.ia5Ub9p7.js} +1 -1
- streamlit/static/static/js/{index.DiwhD0Ic.js → index.qjO5OK90.js} +5 -5
- streamlit/static/static/js/{input.jAD-v9D6.js → input.Bavj6HHJ.js} +1 -1
- streamlit/static/static/js/{main.Cas0fuOs.js → main.dnUTEH_j.js} +1 -1
- streamlit/static/static/js/{memory.ENruL8Qk.js → memory.Czf1Sxzc.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.DAOTdulh.js → number-overlay-editor.CRQIke3a.js} +1 -1
- streamlit/static/static/js/{pandasStylerUtils.DChYgcPq.js → pandasStylerUtils.agmr-LQ2.js} +1 -1
- streamlit/static/static/js/{sandbox.DWZYRCm0.js → sandbox.BlCiIomw.js} +1 -1
- streamlit/static/static/js/{styled-components.3lwVqgRW.js → styled-components.Bz3KSbhj.js} +1 -1
- streamlit/static/static/js/{throttle.DfOW1Cns.js → throttle.B1o314FW.js} +1 -1
- streamlit/static/static/js/{timepicker.CXHgL5f9.js → timepicker.3x4ndo9E.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.DiW0GSDV.js → toConsumableArray.BeHbBK8g.js} +1 -1
- streamlit/static/static/js/uniqueId.Zn1vZBLX.js +1 -0
- streamlit/static/static/js/{useBasicWidgetState.DnPEt_f3.js → useBasicWidgetState.CFP4_PTk.js} +1 -1
- streamlit/static/static/js/{useIntlLocale.BZEGSOVD.js → useIntlLocale.BJubkaPQ.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.MGVZMqyt.js → useTextInputAutoExpand.DBGwhM4R.js} +1 -1
- streamlit/static/static/js/{useUpdateUiValue.CPl6CEx3.js → useUpdateUiValue.CyufNQfR.js} +1 -1
- streamlit/static/static/js/{useWaveformController.DOpzDyZu.js → useWaveformController.Dj5h0D8Y.js} +1 -1
- streamlit/static/static/js/{withCalculatedWidth.DgKFTeif.js → withCalculatedWidth.D0IRb-7w.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.K1jkwhPu.js → withFullScreenWrapper.CHBnw0Co.js} +1 -1
- streamlit/string_util.py +2 -2
- streamlit/testing/v1/element_tree.py +8 -10
- streamlit/type_util.py +2 -2
- streamlit/url_util.py +2 -2
- streamlit/user_info.py +2 -2
- streamlit/util.py +1 -1
- streamlit/watcher/path_watcher.py +1 -1
- streamlit/web/cli.py +1 -4
- streamlit/web/server/app_discovery.py +2 -1
- streamlit/web/server/oauth_authlib_routes.py +14 -42
- streamlit/web/server/server.py +1 -1
- streamlit/web/server/server_util.py +1 -1
- streamlit/web/server/starlette/starlette_auth_routes.py +94 -16
- streamlit/web/server/starlette/starlette_routes.py +9 -3
- streamlit/web/server/starlette/starlette_server.py +2 -2
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260117.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260117.dist-info}/RECORD +162 -162
- streamlit/static/static/js/uniqueId.Dfi3SGKZ.js +0 -1
- {streamlit_nightly-1.53.1.dev20260115.data → streamlit_nightly-1.53.1.dev20260117.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260117.dist-info}/WHEEL +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260117.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.53.1.dev20260115.dist-info → streamlit_nightly-1.53.1.dev20260117.dist-info}/top_level.txt +0 -0
streamlit/static/static/js/{useWaveformController.DOpzDyZu.js → useWaveformController.Dj5h0D8Y.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.DiwhD0Ic.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
|
1
|
+
import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.qjO5OK90.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
streamlit/static/static/js/{withCalculatedWidth.DgKFTeif.js → withCalculatedWidth.D0IRb-7w.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{h as l,k as h,j as s,d as n}from"./index.
|
|
1
|
+
import{h as l,k as h,j as s,d as n}from"./index.qjO5OK90.js";const o=t=>{const a=e=>{const{width:i,elementRef:c}=h();return s(n,{ref:c,children:s(t,{...e,width:i})})};return a.displayName=`withCalculatedWidth(${t.displayName||t.name})`,l(a,t)};export{o as w};
|
streamlit/static/static/js/{withFullScreenWrapper.K1jkwhPu.js → withFullScreenWrapper.CHBnw0Co.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.
|
|
1
|
+
import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.qjO5OK90.js";const m=n.createContext(null);m.displayName="ElementFullscreenContext";const w=f("div",{target:"e5bcvgj0"})(({theme:e,isExpanded:t})=>({width:"100%",height:"100%",...t?{position:"fixed",top:0,left:0,bottom:0,right:0,background:e.colors.bgColor,zIndex:e.zIndices.fullscreenWrapper,padding:e.spacing.md,paddingTop:e.sizes.fullScreenHeaderHeight,overflow:"auto",display:"flex",alignItems:"center",justifyContent:"center"}:{}})),C=()=>{const{setFullScreen:e}=n.useContext(p),[t,s]=n.useState(!1),{fullHeight:a,fullWidth:c}=h(),l=n.useCallback(r=>{s(r),e(r)},[e]),u=n.useCallback(()=>{document.body.style.overflow="hidden",l(!0)},[l]),o=n.useCallback(()=>{document.body.style.overflow="unset",l(!1)},[l]),d=n.useCallback(r=>{r.keyCode===27&&t&&o()},[o,t]);return n.useEffect(()=>(document.addEventListener("keydown",d,!1),()=>{document.removeEventListener("keydown",d,!1)}),[d]),n.useMemo(()=>({expanded:t,zoomIn:u,zoomOut:o,fullHeight:a,fullWidth:c}),[t,u,o,a,c])},b=({children:e})=>{const t=x(),{expanded:s,fullHeight:a,fullWidth:c,zoomIn:l,zoomOut:u}=C(),{width:o,elementRef:d}=y(),r=n.useMemo(()=>({width:s?c:o,height:s?a:void 0,expanded:s,expand:l,collapse:u}),[s,a,c,o,l,u]);return i(m.Provider,{value:r,children:i(w,{ref:d,isExpanded:s,"data-testid":"stFullScreenFrame",theme:t,children:e})})};function S(e){const t=s=>i(b,{children:i(e,{...s})});return t.displayName=`withFullScreenWrapper(${e.displayName||e.name})`,g(t,e)}export{m as E,S as w};
|
streamlit/string_util.py
CHANGED
|
@@ -209,8 +209,8 @@ def to_snake_case(camel_case_str: str) -> str:
|
|
|
209
209
|
BazBang -> baz_bang
|
|
210
210
|
|
|
211
211
|
"""
|
|
212
|
-
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
|
|
213
|
-
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
212
|
+
s1 = re.sub(r"(.)([A-Z][a-z]+)", r"\1_\2", camel_case_str)
|
|
213
|
+
return re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
214
214
|
|
|
215
215
|
|
|
216
216
|
AnyNumber: TypeAlias = Union[
|
|
@@ -90,8 +90,6 @@ T = TypeVar("T")
|
|
|
90
90
|
class InitialValue:
|
|
91
91
|
"""Used to represent the initial value of a widget."""
|
|
92
92
|
|
|
93
|
-
pass
|
|
94
|
-
|
|
95
93
|
|
|
96
94
|
# TODO: This class serves as a fallback option for elements that have not
|
|
97
95
|
# been implemented yet, as well as providing implementations of some
|
|
@@ -515,7 +513,7 @@ DateValue: TypeAlias = SingleDateValue | Sequence[SingleDateValue] | None
|
|
|
515
513
|
class DateInput(Widget):
|
|
516
514
|
"""A representation of ``st.date_input``."""
|
|
517
515
|
|
|
518
|
-
_value: DateValue |
|
|
516
|
+
_value: DateValue | InitialValue | None
|
|
519
517
|
proto: DateInputProto = field(repr=False)
|
|
520
518
|
label: str
|
|
521
519
|
min: date
|
|
@@ -884,7 +882,7 @@ Number: TypeAlias = int | float
|
|
|
884
882
|
class NumberInput(Widget):
|
|
885
883
|
"""A representation of ``st.number_input``."""
|
|
886
884
|
|
|
887
|
-
_value: Number |
|
|
885
|
+
_value: Number | InitialValue | None
|
|
888
886
|
proto: NumberInputProto = field(repr=False)
|
|
889
887
|
label: str
|
|
890
888
|
min: Number | None
|
|
@@ -945,7 +943,7 @@ class NumberInput(Widget):
|
|
|
945
943
|
class Radio(Widget, Generic[T]):
|
|
946
944
|
"""A representation of ``st.radio``."""
|
|
947
945
|
|
|
948
|
-
_value: T |
|
|
946
|
+
_value: T | InitialValue | None
|
|
949
947
|
|
|
950
948
|
proto: RadioProto = field(repr=False)
|
|
951
949
|
label: str
|
|
@@ -1004,7 +1002,7 @@ class Radio(Widget, Generic[T]):
|
|
|
1004
1002
|
class Selectbox(Widget, Generic[T]):
|
|
1005
1003
|
"""A representation of ``st.selectbox``."""
|
|
1006
1004
|
|
|
1007
|
-
_value: T |
|
|
1005
|
+
_value: T | InitialValue | None
|
|
1008
1006
|
|
|
1009
1007
|
proto: SelectboxProto = field(repr=False)
|
|
1010
1008
|
label: str
|
|
@@ -1224,7 +1222,7 @@ class Text(Element):
|
|
|
1224
1222
|
class TextArea(Widget):
|
|
1225
1223
|
"""A representation of ``st.text_area``."""
|
|
1226
1224
|
|
|
1227
|
-
_value: str |
|
|
1225
|
+
_value: str | InitialValue | None
|
|
1228
1226
|
|
|
1229
1227
|
proto: TextAreaProto = field(repr=False)
|
|
1230
1228
|
label: str
|
|
@@ -1276,7 +1274,7 @@ class TextArea(Widget):
|
|
|
1276
1274
|
class TextInput(Widget):
|
|
1277
1275
|
"""A representation of ``st.text_input``."""
|
|
1278
1276
|
|
|
1279
|
-
_value: str |
|
|
1277
|
+
_value: str | InitialValue | None
|
|
1280
1278
|
proto: TextInputProto = field(repr=False)
|
|
1281
1279
|
label: str
|
|
1282
1280
|
max_chars: int
|
|
@@ -1332,7 +1330,7 @@ DateTimeWidgetValue: TypeAlias = datetime
|
|
|
1332
1330
|
class TimeInput(Widget):
|
|
1333
1331
|
"""A representation of ``st.time_input``."""
|
|
1334
1332
|
|
|
1335
|
-
_value: TimeValue |
|
|
1333
|
+
_value: TimeValue | InitialValue | None
|
|
1336
1334
|
proto: TimeInputProto = field(repr=False)
|
|
1337
1335
|
label: str
|
|
1338
1336
|
step: int
|
|
@@ -1389,7 +1387,7 @@ class TimeInput(Widget):
|
|
|
1389
1387
|
class DateTimeInput(Widget):
|
|
1390
1388
|
"""A representation of ``st.datetime_input``."""
|
|
1391
1389
|
|
|
1392
|
-
_value: DateTimeWidgetValue |
|
|
1390
|
+
_value: DateTimeWidgetValue | InitialValue | None
|
|
1393
1391
|
proto: DateTimeInputProto = field(repr=False)
|
|
1394
1392
|
label: str
|
|
1395
1393
|
format: str
|
streamlit/type_util.py
CHANGED
|
@@ -268,7 +268,7 @@ def _is_probably_plotly_dict(obj: object) -> TypeGuard[dict[str, Any]]:
|
|
|
268
268
|
if len(obj.keys()) == 0:
|
|
269
269
|
return False
|
|
270
270
|
|
|
271
|
-
if any(k not in
|
|
271
|
+
if any(k not in {"config", "data", "frames", "layout"} for k in obj):
|
|
272
272
|
return False
|
|
273
273
|
|
|
274
274
|
if any(_is_plotly_obj(v) for v in obj.values()):
|
|
@@ -472,7 +472,7 @@ def async_generator_to_sync(
|
|
|
472
472
|
try:
|
|
473
473
|
# Iterate over the async generator until it raises StopAsyncIteration
|
|
474
474
|
while True:
|
|
475
|
-
yield loop.run_until_complete(async_gen
|
|
475
|
+
yield loop.run_until_complete(anext(async_gen))
|
|
476
476
|
except StopAsyncIteration:
|
|
477
477
|
# The async generator has finished
|
|
478
478
|
pass
|
streamlit/url_util.py
CHANGED
|
@@ -87,9 +87,9 @@ def is_url(
|
|
|
87
87
|
if result.scheme not in allowed_schemas:
|
|
88
88
|
return False
|
|
89
89
|
|
|
90
|
-
if result.scheme in
|
|
90
|
+
if result.scheme in {"http", "https"}:
|
|
91
91
|
return bool(result.netloc)
|
|
92
|
-
if result.scheme in
|
|
92
|
+
if result.scheme in {"mailto", "data"}:
|
|
93
93
|
return bool(result.path)
|
|
94
94
|
|
|
95
95
|
except ValueError:
|
streamlit/user_info.py
CHANGED
|
@@ -652,7 +652,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
|
|
|
652
652
|
}
|
|
653
653
|
"""
|
|
654
654
|
|
|
655
|
-
def __getitem__(self, key: str) -> str | bool |
|
|
655
|
+
def __getitem__(self, key: str) -> str | bool | TokensProxy | None:
|
|
656
656
|
if key == "tokens":
|
|
657
657
|
return self.tokens
|
|
658
658
|
try:
|
|
@@ -660,7 +660,7 @@ class UserInfoProxy(Mapping[str, str | bool | TokensProxy | None]):
|
|
|
660
660
|
except KeyError:
|
|
661
661
|
raise KeyError(f'st.user has no key "{key}".')
|
|
662
662
|
|
|
663
|
-
def __getattr__(self, key: str) -> str | bool |
|
|
663
|
+
def __getattr__(self, key: str) -> str | bool | TokensProxy | None:
|
|
664
664
|
if key == "tokens":
|
|
665
665
|
return self.tokens
|
|
666
666
|
try:
|
streamlit/util.py
CHANGED
|
@@ -66,7 +66,7 @@ def _is_watchdog_available() -> bool:
|
|
|
66
66
|
|
|
67
67
|
def report_watchdog_availability() -> None:
|
|
68
68
|
if (
|
|
69
|
-
config.get_option("server.fileWatcherType") not in
|
|
69
|
+
config.get_option("server.fileWatcherType") not in {"poll", "none"}
|
|
70
70
|
and not _is_watchdog_available()
|
|
71
71
|
):
|
|
72
72
|
msg = "\n $ xcode-select --install" if env_util.IS_DARWIN else ""
|
streamlit/web/cli.py
CHANGED
|
@@ -267,7 +267,7 @@ def _check_extension_or_raise(path_str: str) -> None:
|
|
|
267
267
|
|
|
268
268
|
|
|
269
269
|
def _get_command_line_as_string() -> str | None:
|
|
270
|
-
import subprocess
|
|
270
|
+
import subprocess # noqa: S404
|
|
271
271
|
|
|
272
272
|
parent = click.get_current_context().parent
|
|
273
273
|
if parent is None:
|
|
@@ -331,7 +331,6 @@ def _main_run(
|
|
|
331
331
|
@main.group("cache")
|
|
332
332
|
def cache() -> None:
|
|
333
333
|
"""Manage the Streamlit cache."""
|
|
334
|
-
pass
|
|
335
334
|
|
|
336
335
|
|
|
337
336
|
@cache.command("clear")
|
|
@@ -354,7 +353,6 @@ def cache_clear() -> None:
|
|
|
354
353
|
@main.group("config")
|
|
355
354
|
def config() -> None:
|
|
356
355
|
"""Manage Streamlit's config settings."""
|
|
357
|
-
pass
|
|
358
356
|
|
|
359
357
|
|
|
360
358
|
@config.command("show")
|
|
@@ -393,7 +391,6 @@ def test() -> None:
|
|
|
393
391
|
|
|
394
392
|
These commands are not included in the output of `streamlit help`.
|
|
395
393
|
"""
|
|
396
|
-
pass
|
|
397
394
|
|
|
398
395
|
|
|
399
396
|
@test.command("prog_name")
|
|
@@ -31,6 +31,7 @@ the source code without executing it.
|
|
|
31
31
|
from __future__ import annotations
|
|
32
32
|
|
|
33
33
|
import ast
|
|
34
|
+
import operator
|
|
34
35
|
from dataclasses import dataclass
|
|
35
36
|
from typing import TYPE_CHECKING, Final
|
|
36
37
|
|
|
@@ -404,7 +405,7 @@ def discover_asgi_app(
|
|
|
404
405
|
)
|
|
405
406
|
|
|
406
407
|
# Fall back to the first discovered app (by line number)
|
|
407
|
-
first_app = min(app_assignments.items(), key=
|
|
408
|
+
first_app = min(app_assignments.items(), key=operator.itemgetter(1))
|
|
408
409
|
_LOGGER.debug(
|
|
409
410
|
"Found ASGI app at %s:%s (fallback, line %d)",
|
|
410
411
|
module_str,
|
|
@@ -15,18 +15,20 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import json
|
|
17
17
|
from typing import Any, Final, cast
|
|
18
|
-
from urllib.parse import urlencode, urlparse
|
|
19
18
|
|
|
20
19
|
import tornado.web
|
|
21
20
|
|
|
22
21
|
from streamlit.auth_util import (
|
|
23
22
|
AuthCache,
|
|
23
|
+
build_logout_url,
|
|
24
24
|
clear_cookie_and_chunks,
|
|
25
25
|
decode_provider_token,
|
|
26
26
|
generate_default_provider_section,
|
|
27
27
|
get_cookie_with_chunks,
|
|
28
|
+
get_origin_from_redirect_uri,
|
|
28
29
|
get_redirect_uri,
|
|
29
30
|
get_secrets_auth_section,
|
|
31
|
+
get_validated_redirect_uri,
|
|
30
32
|
set_cookie_with_chunks,
|
|
31
33
|
)
|
|
32
34
|
from streamlit.errors import StreamlitAuthError
|
|
@@ -179,21 +181,6 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
179
181
|
else:
|
|
180
182
|
self.redirect_to_base()
|
|
181
183
|
|
|
182
|
-
def _get_redirect_uri(self) -> str | None:
|
|
183
|
-
auth_section = get_secrets_auth_section()
|
|
184
|
-
if not auth_section:
|
|
185
|
-
return None
|
|
186
|
-
|
|
187
|
-
redirect_uri = get_redirect_uri(auth_section)
|
|
188
|
-
if not redirect_uri:
|
|
189
|
-
return None
|
|
190
|
-
|
|
191
|
-
if not redirect_uri.endswith("/oauth2callback"):
|
|
192
|
-
_LOGGER.warning("Redirect URI does not end with /oauth2callback")
|
|
193
|
-
return None
|
|
194
|
-
|
|
195
|
-
return redirect_uri
|
|
196
|
-
|
|
197
184
|
def _get_provider_logout_url(self) -> str | None:
|
|
198
185
|
"""Get the OAuth provider's logout URL from OIDC metadata."""
|
|
199
186
|
cookie_value = get_cookie_with_chunks(self._get_signed_cookie, AUTH_COOKIE_NAME)
|
|
@@ -219,17 +206,13 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
219
206
|
# Use redirect_uri (i.e. /oauth2callback) for post_logout_redirect_uri
|
|
220
207
|
# This is safer than redirecting to root as some providers seem to
|
|
221
208
|
# require url to be in a whitelist /oauth2callback should be whitelisted
|
|
222
|
-
redirect_uri =
|
|
209
|
+
redirect_uri = get_validated_redirect_uri()
|
|
223
210
|
if redirect_uri is None:
|
|
224
211
|
_LOGGER.info("Redirect url could not be determined")
|
|
225
212
|
return None
|
|
226
213
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
"post_logout_redirect_uri": redirect_uri,
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
# Add id_token_hint to logout params if it is available
|
|
214
|
+
# Get id_token_hint from tokens cookie if available
|
|
215
|
+
id_token: str | None = None
|
|
233
216
|
tokens_cookie_value = get_cookie_with_chunks(
|
|
234
217
|
self._get_signed_cookie, TOKENS_COOKIE_NAME
|
|
235
218
|
)
|
|
@@ -237,15 +220,16 @@ class AuthLogoutHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
237
220
|
try:
|
|
238
221
|
tokens = json.loads(tokens_cookie_value)
|
|
239
222
|
id_token = tokens.get("id_token")
|
|
240
|
-
if id_token:
|
|
241
|
-
logout_params["id_token_hint"] = id_token
|
|
242
223
|
except (json.JSONDecodeError, TypeError):
|
|
243
|
-
_LOGGER.exception(
|
|
244
|
-
"Error, invalid tokens cookie value.",
|
|
245
|
-
)
|
|
224
|
+
_LOGGER.exception("Error, invalid tokens cookie value.")
|
|
246
225
|
return None
|
|
247
226
|
|
|
248
|
-
return
|
|
227
|
+
return build_logout_url(
|
|
228
|
+
end_session_endpoint=end_session_endpoint,
|
|
229
|
+
client_id=client.client_id,
|
|
230
|
+
post_logout_redirect_uri=redirect_uri,
|
|
231
|
+
id_token=id_token,
|
|
232
|
+
)
|
|
249
233
|
|
|
250
234
|
except Exception as e:
|
|
251
235
|
_LOGGER.warning("Failed to get provider logout URL: %s", e)
|
|
@@ -327,16 +311,4 @@ class AuthCallbackHandler(AuthHandlerMixin, tornado.web.RequestHandler):
|
|
|
327
311
|
return provider
|
|
328
312
|
|
|
329
313
|
def _get_origin_from_secrets(self) -> str | None:
|
|
330
|
-
|
|
331
|
-
auth_section = get_secrets_auth_section()
|
|
332
|
-
if auth_section:
|
|
333
|
-
redirect_uri = get_redirect_uri(auth_section)
|
|
334
|
-
|
|
335
|
-
if not redirect_uri:
|
|
336
|
-
return None
|
|
337
|
-
|
|
338
|
-
redirect_uri_parsed = urlparse(redirect_uri)
|
|
339
|
-
origin_from_redirect_uri: str = (
|
|
340
|
-
redirect_uri_parsed.scheme + "://" + redirect_uri_parsed.netloc
|
|
341
|
-
)
|
|
342
|
-
return origin_from_redirect_uri
|
|
314
|
+
return get_origin_from_redirect_uri()
|
streamlit/web/server/server.py
CHANGED
|
@@ -263,7 +263,7 @@ def start_listening_tcp_socket(http_server: HTTPServer) -> None:
|
|
|
263
263
|
except OSError as e:
|
|
264
264
|
# EADDRINUSE: port in use by another process
|
|
265
265
|
# EACCES: port reserved by system (common on Windows, see #13521)
|
|
266
|
-
if e.errno in
|
|
266
|
+
if e.errno in {errno.EADDRINUSE, errno.EACCES}:
|
|
267
267
|
if server_port_is_manually_set():
|
|
268
268
|
_LOGGER.error("Port %s is not available", port) # noqa: TRY400
|
|
269
269
|
sys.exit(1)
|
|
@@ -82,7 +82,7 @@ def is_url_from_allowed_origins(url: str) -> bool:
|
|
|
82
82
|
url_util.get_hostname(origin) for origin in allowlisted_origins()
|
|
83
83
|
]
|
|
84
84
|
|
|
85
|
-
allowed_domains: list[str |
|
|
85
|
+
allowed_domains: list[str | Callable[[], str | None] | None] = [
|
|
86
86
|
# Check localhost first.
|
|
87
87
|
"localhost",
|
|
88
88
|
"0.0.0.0", # noqa: S104
|
|
@@ -12,19 +12,26 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
# ruff: noqa: RUF029 # Async route handlers are idiomatic even without await
|
|
16
|
+
|
|
15
17
|
"""Starlette app authentication routes."""
|
|
16
18
|
|
|
17
19
|
from __future__ import annotations
|
|
18
20
|
|
|
21
|
+
import json
|
|
19
22
|
import time
|
|
20
23
|
from typing import TYPE_CHECKING, Any, Final, cast
|
|
21
|
-
from urllib.parse import urlparse
|
|
22
24
|
|
|
23
25
|
from streamlit.auth_util import (
|
|
26
|
+
build_logout_url,
|
|
24
27
|
clear_cookie_and_chunks,
|
|
25
28
|
decode_provider_token,
|
|
26
29
|
generate_default_provider_section,
|
|
30
|
+
get_cookie_with_chunks,
|
|
31
|
+
get_origin_from_redirect_uri,
|
|
32
|
+
get_redirect_uri,
|
|
27
33
|
get_secrets_auth_section,
|
|
34
|
+
get_validated_redirect_uri,
|
|
28
35
|
set_cookie_with_chunks,
|
|
29
36
|
)
|
|
30
37
|
from streamlit.errors import StreamlitAuthError
|
|
@@ -139,7 +146,7 @@ def _looks_like_provider_section(value: dict[str, Any]) -> bool:
|
|
|
139
146
|
return any(key in value for key in provider_keys)
|
|
140
147
|
|
|
141
148
|
|
|
142
|
-
class _AuthlibConfig(dict[str, Any]):
|
|
149
|
+
class _AuthlibConfig(dict[str, Any]): # noqa: FURB189
|
|
143
150
|
"""Config adapter that exposes provider data via Authlib's flat lookup.
|
|
144
151
|
|
|
145
152
|
Authlib expects a flat configuration dictionary (e.g. "GOOGLE_CLIENT_ID").
|
|
@@ -307,7 +314,7 @@ def _create_oauth_client(provider: str) -> tuple[Any, str]:
|
|
|
307
314
|
|
|
308
315
|
auth_section = get_secrets_auth_section()
|
|
309
316
|
if auth_section:
|
|
310
|
-
redirect_uri = auth_section
|
|
317
|
+
redirect_uri = get_redirect_uri(auth_section) or "/"
|
|
311
318
|
config = auth_section.to_dict()
|
|
312
319
|
else:
|
|
313
320
|
config = {}
|
|
@@ -385,20 +392,80 @@ def _get_provider_by_state(state_code_from_url: str | None) -> str | None:
|
|
|
385
392
|
|
|
386
393
|
def _get_origin_from_secrets() -> str | None:
|
|
387
394
|
"""Extract the origin from the redirect URI in the secrets."""
|
|
395
|
+
return get_origin_from_redirect_uri()
|
|
388
396
|
|
|
389
|
-
redirect_uri = None
|
|
390
|
-
auth_section = get_secrets_auth_section()
|
|
391
|
-
if auth_section:
|
|
392
|
-
redirect_uri = auth_section.get("redirect_uri", None)
|
|
393
397
|
|
|
394
|
-
|
|
398
|
+
def _get_cookie_value_from_request(request: Request, cookie_name: str) -> bytes | None:
|
|
399
|
+
"""Get a signed cookie value from the request, handling chunked cookies."""
|
|
400
|
+
|
|
401
|
+
def get_single_cookie(name: str) -> bytes | None:
|
|
402
|
+
return _get_signed_cookie_from_request(request, name)
|
|
403
|
+
|
|
404
|
+
return get_cookie_with_chunks(get_single_cookie, cookie_name)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _get_provider_logout_url(request: Request) -> str | None:
|
|
408
|
+
"""Get the OAuth provider's logout URL from OIDC metadata.
|
|
409
|
+
|
|
410
|
+
Returns the end_session_endpoint URL with proper parameters for OIDC logout,
|
|
411
|
+
or None if the provider doesn't support it or required data is unavailable.
|
|
412
|
+
|
|
413
|
+
This function returns None (rather than raising exceptions) to allow graceful
|
|
414
|
+
fallback to a simple base URL redirect when OIDC logout isn't possible.
|
|
415
|
+
"""
|
|
416
|
+
cookie_value = _get_cookie_value_from_request(request, USER_COOKIE_NAME)
|
|
417
|
+
|
|
418
|
+
if not cookie_value:
|
|
395
419
|
return None
|
|
396
420
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
421
|
+
try:
|
|
422
|
+
user_info = json.loads(cookie_value)
|
|
423
|
+
provider = user_info.get("provider")
|
|
424
|
+
if not provider:
|
|
425
|
+
return None
|
|
426
|
+
|
|
427
|
+
client, _ = _create_oauth_client(provider)
|
|
428
|
+
|
|
429
|
+
# Load OIDC metadata - Authlib's Starlette client uses async methods
|
|
430
|
+
# but load_server_metadata is synchronous in both implementations
|
|
431
|
+
metadata = client.load_server_metadata()
|
|
432
|
+
end_session_endpoint = metadata.get("end_session_endpoint")
|
|
433
|
+
|
|
434
|
+
if not end_session_endpoint:
|
|
435
|
+
_LOGGER.info("No end_session_endpoint found for provider %s", provider)
|
|
436
|
+
return None
|
|
437
|
+
|
|
438
|
+
# Use redirect_uri (i.e. /oauth2callback) for post_logout_redirect_uri
|
|
439
|
+
# This is safer than redirecting to root as some providers seem to
|
|
440
|
+
# require URL to be in a whitelist - /oauth2callback should be whitelisted
|
|
441
|
+
redirect_uri = get_validated_redirect_uri()
|
|
442
|
+
if redirect_uri is None:
|
|
443
|
+
_LOGGER.info("Redirect url could not be determined")
|
|
444
|
+
return None
|
|
445
|
+
|
|
446
|
+
# Get id_token_hint from tokens cookie if available
|
|
447
|
+
id_token: str | None = None
|
|
448
|
+
tokens_cookie_value = _get_cookie_value_from_request(
|
|
449
|
+
request, TOKENS_COOKIE_NAME
|
|
450
|
+
)
|
|
451
|
+
if tokens_cookie_value:
|
|
452
|
+
try:
|
|
453
|
+
tokens = json.loads(tokens_cookie_value)
|
|
454
|
+
id_token = tokens.get("id_token")
|
|
455
|
+
except (json.JSONDecodeError, TypeError):
|
|
456
|
+
_LOGGER.exception("Error, invalid tokens cookie value.")
|
|
457
|
+
return None
|
|
458
|
+
|
|
459
|
+
return build_logout_url(
|
|
460
|
+
end_session_endpoint=end_session_endpoint,
|
|
461
|
+
client_id=client.client_id,
|
|
462
|
+
post_logout_redirect_uri=redirect_uri,
|
|
463
|
+
id_token=id_token,
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
except Exception as e:
|
|
467
|
+
_LOGGER.warning("Failed to get provider logout URL: %s", e)
|
|
468
|
+
return None
|
|
402
469
|
|
|
403
470
|
|
|
404
471
|
async def _auth_login(request: Request, base_url: str) -> Response:
|
|
@@ -421,9 +488,20 @@ async def _auth_login(request: Request, base_url: str) -> Response:
|
|
|
421
488
|
|
|
422
489
|
|
|
423
490
|
async def _auth_logout(request: Request, base_url: str) -> Response:
|
|
424
|
-
"""Logout the user by clearing the auth cookie and redirecting
|
|
491
|
+
"""Logout the user by clearing the auth cookie and redirecting.
|
|
492
|
+
|
|
493
|
+
If the OAuth provider supports end_session_endpoint, redirects there for
|
|
494
|
+
proper OIDC logout. Otherwise, redirects to the base URL.
|
|
495
|
+
"""
|
|
496
|
+
from starlette.responses import RedirectResponse
|
|
497
|
+
|
|
498
|
+
provider_logout_url = _get_provider_logout_url(request)
|
|
499
|
+
|
|
500
|
+
if provider_logout_url:
|
|
501
|
+
response = RedirectResponse(provider_logout_url, status_code=302)
|
|
502
|
+
else:
|
|
503
|
+
response = await _redirect_to_base(base_url)
|
|
425
504
|
|
|
426
|
-
response = await _redirect_to_base(base_url)
|
|
427
505
|
_clear_auth_cookie(response, request)
|
|
428
506
|
return response
|
|
429
507
|
|
|
@@ -471,7 +549,7 @@ async def _auth_callback(request: Request, base_url: str) -> Response:
|
|
|
471
549
|
|
|
472
550
|
response = await _redirect_to_base(base_url)
|
|
473
551
|
|
|
474
|
-
cookie_value = dict(user, origin=origin, is_logged_in=True)
|
|
552
|
+
cookie_value = dict(user, origin=origin, is_logged_in=True, provider=provider)
|
|
475
553
|
tokens = {k: token[k] for k in ["id_token", "access_token"] if k in token}
|
|
476
554
|
if user:
|
|
477
555
|
await _set_auth_cookie(response, cookie_value, tokens)
|
|
@@ -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
|
+
# ruff: noqa: RUF029 # Async route handlers are idiomatic even without await
|
|
16
|
+
|
|
15
17
|
"""Route handlers for the Starlette server."""
|
|
16
18
|
|
|
17
19
|
from __future__ import annotations
|
|
@@ -741,6 +743,7 @@ def create_bidi_component_routes(
|
|
|
741
743
|
) -> list[BaseRoute]:
|
|
742
744
|
"""Create bidirectional component route handlers."""
|
|
743
745
|
import anyio
|
|
746
|
+
from anyio import Path as AsyncPath
|
|
744
747
|
from starlette.responses import PlainTextResponse, Response
|
|
745
748
|
from starlette.routing import Route
|
|
746
749
|
|
|
@@ -771,7 +774,7 @@ def create_bidi_component_routes(
|
|
|
771
774
|
if abspath is None:
|
|
772
775
|
return await _text_response("forbidden", 403)
|
|
773
776
|
|
|
774
|
-
if
|
|
777
|
+
if await AsyncPath(abspath).is_dir():
|
|
775
778
|
return await _text_response("not found", 404)
|
|
776
779
|
|
|
777
780
|
try:
|
|
@@ -817,6 +820,7 @@ def create_app_static_serving_routes(
|
|
|
817
820
|
main_script_path: str | None, base_url: str | None
|
|
818
821
|
) -> list[BaseRoute]:
|
|
819
822
|
"""Create app static serving file route handlers."""
|
|
823
|
+
from anyio import Path as AsyncPath
|
|
820
824
|
from starlette.exceptions import HTTPException
|
|
821
825
|
from starlette.responses import FileResponse, Response
|
|
822
826
|
from starlette.routing import Route
|
|
@@ -836,10 +840,12 @@ def create_app_static_serving_routes(
|
|
|
836
840
|
if safe_path is None:
|
|
837
841
|
raise HTTPException(status_code=404, detail="File not found")
|
|
838
842
|
|
|
839
|
-
|
|
843
|
+
async_path = AsyncPath(safe_path)
|
|
844
|
+
if not await async_path.exists() or await async_path.is_dir():
|
|
840
845
|
raise HTTPException(status_code=404, detail="File not found")
|
|
841
846
|
|
|
842
|
-
|
|
847
|
+
file_stat = await async_path.stat()
|
|
848
|
+
if file_stat.st_size > MAX_APP_STATIC_FILE_SIZE:
|
|
843
849
|
raise HTTPException(
|
|
844
850
|
status_code=404,
|
|
845
851
|
detail="File is too large",
|
|
@@ -288,7 +288,7 @@ class UvicornServer:
|
|
|
288
288
|
last_exception = exc
|
|
289
289
|
# EADDRINUSE: port in use by another process
|
|
290
290
|
# EACCES: port reserved by system (common on Windows, see #13521)
|
|
291
|
-
if exc.errno in
|
|
291
|
+
if exc.errno in {errno.EADDRINUSE, errno.EACCES}:
|
|
292
292
|
if _is_port_manually_set():
|
|
293
293
|
_LOGGER.error("Port %s is not available", port) # noqa: TRY400
|
|
294
294
|
sys.exit(1)
|
|
@@ -476,7 +476,7 @@ class UvicornRunner:
|
|
|
476
476
|
except OSError as exc:
|
|
477
477
|
# EADDRINUSE: port in use by another process
|
|
478
478
|
# EACCES: port reserved by system (common on Windows)
|
|
479
|
-
if exc.errno in
|
|
479
|
+
if exc.errno in {errno.EADDRINUSE, errno.EACCES}:
|
|
480
480
|
if _is_port_manually_set():
|
|
481
481
|
_LOGGER.error("Port %s is not available", port) # noqa: TRY400
|
|
482
482
|
sys.exit(1)
|