The getstate test called Summary.__getstate__(), which rebuilt the
PagedList pages in tools.CACHE_PATH/summary_dir with exist_ok=True.
Since CACHE_PATH is relative to the current directory, running the
tests clobbered the real .eris cache and caused eris to crash on the
next startup when sync_with_filesystem hit the stale summary.
The shiv zipapp's shebang points at the system python3 (GIL-enabled),
which cannot load the cp314t extensions built under the free-threaded
3.14 project venv, so ./dist/eris failed at runtime. pyinstaller bundles
its own 3.14t interpreter and works, so the Dockerfiles now run the
pyinstaller binary directly instead of via a python wrapper, and the
debian image no longer needs to install uv/python. Remove build-shiv and
the shiv dev dependency; build comments use podman.
flock on .eris/lock prevents two eris processes racing the cache, which
previously produced a mixed-format summary (int-list pages) and crashed
sync_with_filesystem. The lock is acquired before manage_cache, the lock
file survives checksum-mismatch cleanup, and the holder's PID is written
to the lock file so a refused instance can report it.
Removes the shell's "Killed" message and lets fill3's terminal-restore
codes flush via normal interpreter exit. Worker.kill() now SIGKILLs only
the worker thread's subprocess descendants (via /proc/PID/task/TID/children,
recursing through shell=True grandchildren), so editor and xdg-open spawns
from the main thread survive quit. A _killed flag skips persisting result
pickles for aborted tool runs. start_new_session=True is no longer needed
on editor/xdg-open launches since the kill is worker-thread-scoped.
Verify the interpreter is free-threaded (Py_GIL_DISABLED) and the GIL is
not re-enabled after loading the full eris module chain, so a dependency
that re-enables the GIL (PEP 703) or a non-free-threaded interpreter
fails the suite.
A bare 3.14 previously resolved to the freethreaded build only because it
was the sole final 3.14 in uv's managed cache. Use 3.14t to lock in
free-threading against future plain 3.14 installs.
__getstate__ emitted an empty entry list whenever the cursor was on the
top row (y == 0), so quitting from the top silently discarded all
previously calculated results. The guard was intended to rotate the
cursor row to the front, but the y == 0 case should simply keep the whole
list as-is. Remove the guard so all entries are always persisted.
sys.stdout is line-buffered on a tty, and fill3's terminal restore
context managers (alternate_buffer, mouse_tracking, title) only write
escape sequences without newlines. killpg(SIGKILL) killed the process
before those buffered codes were flushed, leaving the terminal stuck
in the alternate buffer with mouse tracking on. Flush first.
--version printed a stale __version__ (2026.7.28); bump it to 2026.8.3
and update the README install tag.
The killpg-on-self guard required eris to be its own process-group
leader, which is false under 'uv run eris' (uv is the leader), so the
fast shutdown never triggered. Also accept getpgrp() == getppid(),
covering the uv-run case while non-tty runs still skip killpg.
When eris quits, _python_exit joins executor threads, which block on
in-flight subprocess.run calls for up to TIMEOUT seconds. After saving
state, kill the process group to reap all tool subprocesses instantly.
Isolate editor/xdg-open in their own sessions so they survive.
Convert each Worker from a subprocess to a thread running the tool in a
ThreadPoolExecutor, so eris can run free-threaded under CPython 3.14t.
Bump fill3 and lscolors deps to 2026.8.3 for the GIL-free cwcwidth chain.
Pair image rows directly from the flattened pixel stream in blocks of
width*2, dropping the intermediate rows list and the range(0, height, 2)
indexing.
Align the minimum Python version with .python-version, the Dockerfiles,
and the cp314 wheels the build produces, since itertools.batched
(python 3.12+) is now used in paged_list.
Replace the groupby-based pure-Python batching with the C-implemented
itertools.batched (Python 3.12+), wrapped in map(list, ...) to preserve
list-typed pages.
ProcessPoolExecutor relied on private API, lost os.setpriority and
auto-restart. Revert to the subprocess worker but spawn the current
executable with --worker instead of a separate eris-worker console
script, keeping a single entry point for shiv/pyinstaller. Bump
.python-version to 3.14.
Remove all build-dependency packages (glfw-dev, cmake, git, python3-dev,
libX11, mesa, etc.) that were only needed for building eris's Python deps
from source. Replace uv + source install with COPY dist/eris. Use
python3 /usr/local/bin/eris for ENTRYPOINT to avoid shebang portability
issues.
Replace the native pydoc() function (+ @in_separate_process, _call_in_process)
with a TOML tool definition running python3 -m pydoc directly. No ANSI
styling, no TermDoc, no process isolation decorator. This eliminates the
grandchild worker process (from nested ProcessPoolExecutor) that caused
TUI corruption on shutdown.
Also remove now-unused import concurrent.futures from tools.py.
Regenerate the pydoc golden file to match plain-text output.
- Rewrite worker.py: each Worker uses a ProcessPoolExecutor(max_workers=1),
tools run via loop.run_in_executor() in a forked OS subprocess
- Remove create_process(), main(), stdin/stdout IPC, os.killpg
- Remove eris-worker entry point from [project.scripts] in pyproject.toml
- Update tests/worker_test.py for new Worker API
- Guard _sweep_up/_sweep_down against IndexError when _entries is empty;
keep cursor_position() try/except for stale y after row deletion