Skip to content

Commit 2ff9078

Browse files
committed
Refactor version resolution and fallback logic in examples_gallery.py
1 parent af2c9ec commit 2ff9078

2 files changed

Lines changed: 81 additions & 51 deletions

File tree

.github/workflows/docs.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
deploy:
9-
name: Deploy Documentation
9+
name: Build Documentation
1010
runs-on: ubuntu-latest
1111
steps:
1212
- name: Checkout repository
@@ -18,4 +18,4 @@ jobs:
1818
- name: Build docs
1919
working-directory: sdk/python/packages/flet
2020
run: |
21-
uv run --group docs mkdocs build -v
21+
uv run --group docs mkdocs build

sdk/python/packages/flet/src/flet/docs_plugins/examples_gallery.py

Lines changed: 79 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -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

115124
def _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

Comments
 (0)