@@ -69,34 +69,43 @@ def _find_pyproject(start: Path, stop: Optional[Path] = None) -> Optional[Path]:
6969 current = current .parent
7070
7171
72- def _version_from_specifiers (specifiers ) -> Optional [str ]:
72+ def _version_from_specifiers (specifiers ) -> tuple [ Optional [str ], bool ]:
7373 """Extract a deterministic version from a SpecifierSet.
7474
75- Prioritises exact pins to avoid drift; otherwise falls back to the lowest
76- acceptable bound (>=, >, ~=) to stay compatible with the source tree.
75+ Returns (version, lower_only_flag).
76+ - If an exact pin or upper bound exists, returns that version and False.
77+ - If only lower bounds exist (>=, >, ~=), returns (None, True) so callers
78+ can opt for a fallback (e.g., latest pre-release) instead of trying a
79+ nonexistent lower bound.
7780 """
7881 for operator in ("==" , "===" ):
7982 for spec in specifiers :
8083 if spec .operator == operator :
81- return spec .version
84+ return spec .version , False
85+
86+ for operator in ("<" , "<=" ):
87+ for spec in specifiers :
88+ if spec .operator == operator :
89+ return spec .version , False
8290
8391 for operator in ("~=" , ">=" , ">" ):
8492 for spec in specifiers :
8593 if spec .operator == operator :
86- return spec . version
94+ return None , True
8795
88- return None
96+ return None , False
8997
9098
91- def _version_from_pyproject (pyproject : Path ) -> Optional [str ]:
99+ def _version_from_pyproject (pyproject : Path ) -> tuple [ Optional [str ], bool ]:
92100 """
93- Read the flet dependency from pyproject.toml and return a pinned/lowest version.
101+ Read the flet dependency from pyproject.toml
102+ and return a version plus lower-only flag.
94103 """
95104 try :
96105 data = tomllib .loads (pyproject .read_text ())
97106 except Exception as exc : # noqa: BLE001
98107 logger .debug ("Unable to parse %s: %s" , pyproject , exc )
99- return None
108+ return None , False
100109
101110 deps = data .get ("project" , {}).get ("dependencies" ) or []
102111 for raw in deps :
@@ -106,15 +115,15 @@ def _version_from_pyproject(pyproject: Path) -> Optional[str]:
106115 continue
107116 if req .name != "flet" :
108117 continue
109- candidate = _version_from_specifiers (req .specifier )
110- if candidate :
111- return candidate
112- return None
118+ candidate , lower_only = _version_from_specifiers (req .specifier )
119+ if candidate or lower_only :
120+ return candidate , lower_only
121+ return None , False
113122
114123
115124def _resolve_flet_version (
116125 env : Mapping [str , str ], src_root : Path , docs_dir : Path
117- ) -> str :
126+ ) -> tuple [ Optional [ str ], bool ] :
118127 """Resolve the Flet version similar to `flet publish`.
119128
120129 Order:
@@ -136,12 +145,16 @@ def _resolve_flet_version(
136145 logger .debug ("Unable to preload flet version defaults: %s" , exc )
137146
138147 if env .get ("FLET_WEB_VERSION" ):
139- return str (env ["FLET_WEB_VERSION" ])
148+ return str (env ["FLET_WEB_VERSION" ]), False
140149
141150 pyproject = _find_pyproject (src_root , stop = docs_dir .parent )
142- pinned = _version_from_pyproject (pyproject ) if pyproject else None
151+ pinned , lower_only = (
152+ _version_from_pyproject (pyproject ) if pyproject else (None , False )
153+ )
143154 if pinned :
144- return pinned
155+ return pinned , False
156+ if lower_only :
157+ return None , True
145158
146159 try :
147160 from flet import version as flet_version
@@ -151,12 +164,12 @@ def _resolve_flet_version(
151164 )
152165 resolved = resolved or default_version
153166 if resolved :
154- return str (resolved )
167+ return str (resolved ), False
155168 except Exception as exc : # noqa: BLE001
156169 logger .debug ("Unable to resolve flet version from package: %s" , exc )
157170
158171 if default_version :
159- return str (default_version )
172+ return str (default_version ), False
160173
161174 raise RuntimeError (
162175 "FLET_WEB_PATH is not set and Flet version could not be determined. "
@@ -190,13 +203,18 @@ def _locate_existing_web_client(env: Mapping[str, str]) -> Optional[Path]:
190203 return package_web if _web_path_ready (package_web ) else None
191204
192205
193- def _download_packaged_web_client (version : str ) -> Optional [Path ]:
206+ def _download_packaged_web_client (
207+ version : Optional [str ], pre_only : bool = False
208+ ) -> Optional [Path ]:
194209 """Download the pre-built flet-web wheel and expose its web folder.
195210
196211 Uses a temp/versioned cache to avoid repeated downloads during local dev.
212+ When `pre_only` is True (or version is None), skip the exact-version attempt
213+ and go straight to the latest available pre-release wheel.
197214 """
198215 cache_root = Path (tempfile .gettempdir ()) / "flet-web"
199- target_root = cache_root / version
216+ cache_key = version or "latest-pre"
217+ target_root = cache_root / cache_key
200218 web_dir = target_root / "flet_web" / "web"
201219
202220 if _web_path_ready (web_dir ):
@@ -208,26 +226,7 @@ def _download_packaged_web_client(version: str) -> Optional[Path]:
208226 target_root .mkdir (parents = True , exist_ok = True )
209227 _ensure_pip_available ()
210228
211- logger .info ("Downloading flet-web==%s into %s" , version , target_root )
212- try :
213- subprocess .run (
214- [
215- sys .executable ,
216- "-m" ,
217- "pip" ,
218- "install" ,
219- "--no-deps" ,
220- "--only-binary" ,
221- ":all:" ,
222- f"flet-web=={ version } " ,
223- "--target" ,
224- str (target_root ),
225- ],
226- check = True ,
227- )
228- except subprocess .CalledProcessError as exc :
229- logger .warning ("Unable to download flet-web==%s: %s" , version , exc )
230- logger .info ("Falling back to latest available pre-release flet-web wheel." )
229+ def _install_latest_pre () -> bool :
231230 try :
232231 subprocess .run (
233232 [
@@ -245,8 +244,37 @@ def _download_packaged_web_client(version: str) -> Optional[Path]:
245244 ],
246245 check = True ,
247246 )
247+ return True
248248 except subprocess .CalledProcessError as exc2 :
249249 logger .warning ("Unable to download a fallback flet-web wheel: %s" , exc2 )
250+ return False
251+
252+ if not pre_only and version :
253+ logger .info ("Downloading flet-web==%s into %s" , version , target_root )
254+ try :
255+ subprocess .run (
256+ [
257+ sys .executable ,
258+ "-m" ,
259+ "pip" ,
260+ "install" ,
261+ "--no-deps" ,
262+ "--only-binary" ,
263+ ":all:" ,
264+ f"flet-web=={ version } " ,
265+ "--target" ,
266+ str (target_root ),
267+ ],
268+ check = True ,
269+ )
270+ except subprocess .CalledProcessError as exc :
271+ logger .warning ("Unable to download flet-web==%s: %s" , version , exc )
272+ logger .info ("Falling back to latest available pre-release flet-web wheel." )
273+ if not _install_latest_pre ():
274+ return None
275+ else :
276+ logger .info ("No exact flet-web version to install; using latest pre-release." )
277+ if not _install_latest_pre ():
250278 return None
251279
252280 return web_dir if _web_path_ready (web_dir ) else None
@@ -262,8 +290,8 @@ def _ensure_web_client(env: dict[str, str], src_root: Path, docs_dir: Path) -> P
262290 if existing :
263291 return existing
264292
265- target_version = _resolve_flet_version (env , src_root , docs_dir )
266- downloaded = _download_packaged_web_client (target_version )
293+ target_version , lower_only = _resolve_flet_version (env , src_root , docs_dir )
294+ downloaded = _download_packaged_web_client (target_version , pre_only = lower_only )
267295 if downloaded :
268296 return downloaded
269297
@@ -300,14 +328,16 @@ class ExamplesGalleryPlugin(BasePlugin):
300328 """Build the examples gallery Flet app before MkDocs runs.
301329
302330 This plugin:
303- - Finds the examples gallery entry point (default: docs/apps/examples-gallery/src/main.py).
331+ - Finds the examples gallery entry point
332+ (default: docs/apps/examples-gallery/src/main.py).
304333 - Runs `flet publish` (via `uv run --active`) into the configured dist folder.
305334 - Skips the build when the dist/index.html is newer than the source tree.
306335
307336 Env opts:
308- - FLET_SKIP_EXAMPLES_GALLERY=1 — skip building (useful for fast local docs iteration).
337+ - FLET_SKIP_EXAMPLES_GALLERY=1 — skip building
338+ (useful for fast local docs iteration).
309339 - FLET_WEB_PATH — optional path to a built Flet web client; if unset the plugin
310- downloads the matching flet-web wheel into a temp cache.
340+ downloads the matching flet-web wheel into a temp cache.
311341 - FLET_WEB_VERSION — optional version override for the wheel download.
312342
313343 Config options (mkdocs.yml):
@@ -321,7 +351,7 @@ class ExamplesGalleryPlugin(BasePlugin):
321351
322352 config_scheme = (
323353 ("enabled" , config_options .Type (bool , default = True )),
324- ("src" , config_options .Type (str , default = "apps/examples-gallery/src/main.py " )),
354+ ("src" , config_options .Type (str , default = "apps/examples-gallery/src" )),
325355 ("dist" , config_options .Type (str , default = "apps/examples-gallery/dist" )),
326356 ("base_url" , config_options .Type (str , default = "apps/examples-gallery/dist" )),
327357 ("command" , config_options .Type (list , default = None )),
@@ -334,8 +364,8 @@ def on_pre_build(self, config: MkDocsConfig) -> None:
334364
335365 - Honours `enabled` and `FLET_SKIP_EXAMPLES_GALLERY`.
336366 - Skips when the existing dist/index.html is newer than the source tree.
337- - Runs the configured command (or the default `uv run --active flet publish ...`)
338- with `docs_dir` as the working directory and merged env vars.
367+ - Runs the configured command (or the default `uv run flet publish ...`)
368+ with `docs_dir` as the working directory and merged env vars.
339369 """
340370 if not self .config .get ("enabled" , True ):
341371 return
0 commit comments