Files
ComfyAssets-ComfyUI_PromptM…/tests/open_handles.py
T
Vito Sansevero b1bfce702d fix: first CI run on Windows and macOS; tests now enforce closed database files
The first push of the branch ran the Tests matrix on all three operating
systems. Every Python job failed; the causes were:

- numpy missing in CI: the WD14 engine tests import it (requirements-dev)
- macOS: temp directories resolve through /private; the admin API tests
  now use the real path like the gallery roots they compare against
- Windows: the startup banner printed an emoji, which raises
  UnicodeEncodeError on a legacy-code-page console and aborted loading
  the whole node; the banner is ASCII now and a test loads the package
  under a cp1252 stdout
- Windows: gallery subfolders came back with backslashes, which neither
  the folder tree nor the search filter understand; they are "/" joined
  on every platform. A relative DEFAULT_DB_PATH kept its forward slashes
  after being joined to the extension root; it is normalised now
- Windows: sqlite connections opened with `with sqlite3.connect()` are
  not closed by the context manager and linger until garbage collection,
  which holds a file lock there. Production sites (vacuum, diagnostics,
  restore inspection) close explicitly; a restore that swaps the API's
  database object closes the previous one; PromptModel keeps a registry
  so a shutdown or a test can close every instance
- Windows: served files were still open when their temp directory was
  removed (tests now read the body), a log download test compared text
  mode newlines, two tests compared paths with a hard-coded separator

tests/open_handles.py makes the Linux run fail the way Windows would:
every database test base checks, right before deleting its files, that
nothing in the process still holds them open.
2026-10-08 18:24:18 -07:00

44 lines
1.5 KiB
Python

"""Teardown guard: fail when a file about to be deleted is still open.
Windows refuses to delete an open file, so a sqlite connection that was not
closed explicitly turns into a PermissionError in CI there. Linux would let
the deletion through silently; this check makes the Linux run catch the same
mistake. It inspects /proc/self/fd and is a no-op where that does not exist.
"""
import os
def open_handles_under(path):
"""Basenames of this process's open files at *path* or below it."""
fd_dir = "/proc/self/fd"
if not os.path.isdir(fd_dir):
return []
real = os.path.realpath(str(path))
hits = []
for fd in os.listdir(fd_dir):
try:
target = os.readlink(os.path.join(fd_dir, fd))
except OSError:
continue
if not target.startswith(os.sep):
continue # sockets, pipes, anon inodes
target = target.replace(" (deleted)", "")
if (
target == real
or target.startswith(real + os.sep)
or target.startswith(real + "-")
):
hits.append(os.path.basename(target))
return sorted(hits)
def assert_closed(testcase, *paths):
"""Fail *testcase* if any of *paths* (files or directories) is still open."""
hits = [hit for path in paths for hit in open_handles_under(path)]
if hits:
testcase.fail(
"Files still open at teardown (Windows could not delete them): "
+ ", ".join(hits)
)