1414
1515import os
1616import re
17+ import subprocess
18+
1719import requests
1820import semver
19- import subprocess
21+ import toml
2022
21- SETUP_PY_PATH = "setup.py"
23+ PYPROJECT_TOML_PATH = "pyproject.toml"
24+ PACKAGE_PYPROJECT_TOML_PATH = "streamlit_bokeh/pyproject.toml"
2225
2326
2427def get_latest_bokeh_version ():
@@ -31,29 +34,31 @@ def get_latest_bokeh_version():
3134
3235
3336def get_component_version ():
34- with open (SETUP_PY_PATH , "r" ) as f :
35- setup_content = f . read ( )
37+ with open (PYPROJECT_TOML_PATH , "r" ) as f :
38+ pyproject_data = toml . load ( f )
3639
37- # Extract version from setup.py
38- match = re . search ( r"version\s*=\s*['\"]([\d\.]+)(['\"])" , setup_content )
40+ # Extract version from pyproject.toml
41+ version = pyproject_data . get ( "project" , {}). get ( "version" )
3942
40- if match :
41- return match . group ( 1 )
43+ if version :
44+ return version
4245 else :
43- raise ValueError ("Bokeh version not found in the file " )
46+ raise ValueError ("Component version not found in pyproject.toml " )
4447
4548
4649def get_dependency_bokeh_version ():
47- with open (SETUP_PY_PATH , "r" ) as f :
48- setup_content = f . read ( )
50+ with open (PYPROJECT_TOML_PATH , "r" ) as f :
51+ pyproject_data = toml . load ( f )
4952
50- # Extract Bokeh version from dependency line (e.g., bokeh==2.4.3)
51- match = re . search ( r"bokeh\s*==\s*([\d\.]+)" , setup_content )
53+ # Extract Bokeh version from dependencies list
54+ dependencies = pyproject_data . get ( "project" , {}). get ( "dependencies" , [] )
5255
53- if match :
54- return match .group (1 )
55- else :
56- raise ValueError ("Bokeh version not found in the file" )
56+ for dep in dependencies :
57+ match = re .search (r"bokeh\s*==\s*([\d\.]+)" , dep )
58+ if match :
59+ return match .group (1 )
60+
61+ raise ValueError ("Bokeh version not found in dependencies" )
5762
5863
5964def download_files (new_version , destination ):
@@ -67,6 +72,9 @@ def download_files(new_version, destination):
6772 f"https://raw.githubusercontent.com/bokeh/bokeh/refs/tags/{ new_version } /LICENSE.txt" ,
6873 ]
6974
75+ # Ensure destination/bokeh directory exists
76+ os .makedirs (os .path .join (destination , "bokeh" ), exist_ok = True )
77+
7078 for url in files_to_download :
7179 filename = os .path .basename (url )
7280 print (f"Downloading { filename } " )
@@ -77,28 +85,30 @@ def download_files(new_version, destination):
7785 f .write (chunk )
7886
7987
80- def update_setup_py (new_version , old_bokeh_version , new_bokeh_version ):
81- with open (SETUP_PY_PATH , "r" ) as f :
82- setup_content = f .read ()
88+ def update_pyproject_toml (new_version , old_bokeh_version , new_bokeh_version ):
89+ for path in (PYPROJECT_TOML_PATH , PACKAGE_PYPROJECT_TOML_PATH ):
90+ if not os .path .exists (path ):
91+ raise FileNotFoundError (f"File { path } not found" )
8392
84- # Replace package version in `version='...'`
85- # This pattern is naive; adapt as needed for your file structure.
86- setup_content = re .sub (
87- r"(version\s*=\s*['\"])([\d\.]+)(['\"])" ,
88- rf"\g<1>{ new_version } \g<3>" ,
89- setup_content ,
90- )
93+ with open (path , "r" ) as f :
94+ pyproject_data = toml .load (f )
9195
92- # Replace bokeh==old_version with bokeh==new_version
93- if old_bokeh_version :
94- setup_content = re .sub (
95- rf"(bokeh\s*==\s*){ old_bokeh_version } " ,
96- rf"\g<1>{ new_bokeh_version } " ,
97- setup_content ,
98- )
96+ project = pyproject_data .get ("project" )
97+
98+ # Update project version if present
99+ if project and "version" in project :
100+ project ["version" ] = new_version
101+
102+ # Update bokeh dependency version if present in this file
103+ if old_bokeh_version and "dependencies" in project :
104+ dependencies = project ["dependencies" ]
105+ for i , dep in enumerate (dependencies ):
106+ if isinstance (dep , str ) and dep .startswith ("bokeh==" ):
107+ dependencies [i ] = f"bokeh=={ new_bokeh_version } "
108+ break
99109
100- with open (SETUP_PY_PATH , "w" ) as f :
101- f . write ( setup_content )
110+ with open (path , "w" ) as f :
111+ toml . dump ( pyproject_data , f )
102112
103113
104114def update_test_requirements (
@@ -160,16 +170,56 @@ def update_init_py(old_bokeh_version, new_bokeh_version):
160170 f .write (init_py_contents )
161171
162172
163- def update_index_html (public_dir , old_version , new_version ):
164- index_html_path = os .path .join (public_dir , "index.html" )
165- if os .path .exists (index_html_path ):
166- with open (index_html_path , "r" , encoding = "utf-8" ) as f :
173+ def update_loader_imports (old_bokeh_version , new_bokeh_version ):
174+ """
175+ Update versioned Bokeh asset import paths in the TypeScript loader to the new version.
176+
177+ This replaces occurrences like `bokeh-3.8.0.min.js` with `bokeh-<new>.min.js`
178+ in `streamlit_bokeh/frontend/src/loaders.ts`.
179+ """
180+ loader_path = "streamlit_bokeh/frontend/src/loaders.ts"
181+ with open (loader_path , "r" , encoding = "utf-8" ) as f :
182+ contents = f .read ()
183+
184+ suffixes = ["mathjax" , "gl" , "api" , "tables" , "widgets" , "" ]
185+ for suffix in suffixes :
186+ old_str = (
187+ f"bokeh-{ suffix } -{ old_bokeh_version } .min.js"
188+ if suffix
189+ else f"bokeh-{ old_bokeh_version } .min.js"
190+ )
191+ new_str = (
192+ f"bokeh-{ suffix } -{ new_bokeh_version } .min.js"
193+ if suffix
194+ else f"bokeh-{ new_bokeh_version } .min.js"
195+ )
196+ contents = contents .replace (old_str , new_str )
197+
198+ with open (loader_path , "w" , encoding = "utf-8" ) as f :
199+ f .write (contents )
200+
201+
202+ def update_index_html (frontend_dir , old_version , new_version ):
203+ """
204+ Update the index.html files to reference the new Bokeh asset version.
205+ Only the source Vite entry point (`frontend/index.html`) needs to be updated,
206+ since build artifacts are regenerated.
207+ """
208+
209+ index_html_paths = [os .path .join (frontend_dir , "index.html" )]
210+
211+ cdn_suffixes = ["mathjax" , "gl" , "api" , "tables" , "widgets" , "" ]
212+ found_index = False
213+
214+ for path in index_html_paths :
215+ if not os .path .exists (path ):
216+ continue
217+
218+ found_index = True
219+ with open (path , "r" , encoding = "utf-8" ) as f :
167220 html_content = f .read ()
168221
169- # If old_version is known, do a direct replacement
170222 if old_version :
171- # Replace each script reference with the new version
172- cdn_suffixes = ["mathjax" , "gl" , "api" , "tables" , "widgets" , "" ]
173223 for suffix in cdn_suffixes :
174224 old_str = (
175225 f"bokeh-{ suffix } -{ old_version } .min.js"
@@ -183,10 +233,13 @@ def update_index_html(public_dir, old_version, new_version):
183233 )
184234 html_content = html_content .replace (old_str , new_str )
185235
186- with open (index_html_path , "w" , encoding = "utf-8" ) as f :
236+ with open (path , "w" , encoding = "utf-8" ) as f :
187237 f .write (html_content )
188- else :
189- print ("No index.html found in frontend/public. Skipping HTML update." )
238+
239+ if not found_index :
240+ print (
241+ "No index.html found under streamlit_bokeh/frontend. Skipping HTML update."
242+ )
190243
191244
192245def check_remote_branch_exists (remote : str , new_version : str ) -> bool :
@@ -242,21 +295,24 @@ def check_remote_branch_exists(remote: str, new_version: str) -> bool:
242295 exit (0 )
243296
244297 print ("New version available!" )
245- public_dir = "streamlit_bokeh/frontend/public"
298+ assets_dir = "streamlit_bokeh/frontend/src/assets"
299+ frontend_dir = "streamlit_bokeh/frontend"
246300
247301 # Remove original files
248- bokeh_dir = os .path .join (public_dir , "bokeh" )
302+ bokeh_dir = os .path .join (assets_dir , "bokeh" )
303+ os .makedirs (bokeh_dir , exist_ok = True )
249304 for filename in os .listdir (bokeh_dir ):
250305 if "bokeh" in filename and filename .endswith (".js" ):
251306 os .remove (os .path .join (bokeh_dir , filename ))
252307
253- download_files (new_bokeh_version , public_dir )
254- # Update the bokeh dependency version in index.html and __init__.py
255- update_index_html (public_dir , old_bokeh_version , new_bokeh_version )
308+ download_files (new_bokeh_version , assets_dir )
309+ # Update the bokeh dependency version in index.html, TS loader and __init__.py
310+ update_index_html (frontend_dir , old_bokeh_version , new_bokeh_version )
311+ update_loader_imports (old_bokeh_version , new_bokeh_version )
256312 update_init_py (old_bokeh_version , new_bokeh_version )
257313
258- # Update the bokeh dependency version and component version in setup.py and test-requirements.txt
259- update_setup_py (new_version , old_bokeh_version , new_bokeh_version )
314+ # Update the bokeh dependency version in pyproject.toml and test-requirements.txt
315+ update_pyproject_toml (new_version , old_bokeh_version , new_bokeh_version )
260316 update_test_requirements (
261317 old_bokeh_version , new_bokeh_version , old_version , new_version
262318 )
0 commit comments