fix(images): keep the 3.10-3.13 suffix rule in the scandir walk and harden the warm-up (#348)

Review of the walk speedup:
- os.path.splitext drops names like ..png that Path.suffix listed on Python
  3.10-3.13 (3.14 changed Path.suffix, which hid it from the parity test on
  a 3.14 venv). _name_suffix spells out the 3.10-3.13 rule; the test now
  asserts ..png and ...webp are listed and g. is not.
- _warm_workspace reads the roots inside its try, so a failing
  _get_all_output_dirs warns instead of leaving an unretrieved task error.
- The folder_sets fallback test now takes the fallback (a Windows case
  mismatch); the coalescing test pins the walk count.
This commit is contained in:
Vito Sansevero
2026-10-26 11:23:41 -07:00
parent 3dd45c0efd
commit 5a9971784c
3 changed files with 51 additions and 12 deletions
+15 -4
View File
@@ -772,6 +772,17 @@ def _favorite_root_key(root) -> str:
return _favorite_relative_key(os.path.realpath(str(root)))
def _name_suffix(name):
"""``Path(name).suffix`` as Python 3.10-3.13 compute it.
The last ``.``-part unless the dot is the first or last character, so
``..png`` is ``.png`` and ``.png`` has none. Spelled out because 3.14
changed ``Path.suffix`` and ``os.path.splitext`` differs for leading dots.
"""
dot = name.rfind(".")
return name[dot:] if 0 < dot < len(name) - 1 else ""
def _walk_media(output_path, max_files):
"""Yield the ``os.DirEntry`` of each media file under *output_path*.
@@ -801,7 +812,7 @@ def _walk_media(output_path, max_files):
except OSError:
continue # as os.walk: an unreadable folder is skipped
for entry in sorted(files, key=lambda item: item.name):
if os.path.splitext(entry.name)[1].lower() not in MEDIA_EXTENSIONS:
if _name_suffix(entry.name).lower() not in MEDIA_EXTENSIONS:
continue
if entry.is_symlink():
continue # never read through a link out of the output tree
@@ -1330,10 +1341,10 @@ class ImageRoutesMixin:
async def _warm_workspace(self):
while True:
self._workspace_warm_again = False
listings = self._stored_listings()
if listings is None:
return # a root has no listing yet; its walk asks again
try:
listings = self._stored_listings()
if listings is None:
return # a root has no listing yet; its walk asks again
await self._run_in_executor(self._build_workspace_caches, listings)
except Exception as e:
self.logger.warning(f"Could not prepare the Images page: {e}")