Skip to content

Stop server: child processes on cleanup, skip servers when multi has nothing to do - #211

Open
simonw wants to merge 4 commits into
mainfrom
claude/modal-dialogs-screenshots-9apbe8
Open

simonw wants to merge 4 commits into
mainfrom
claude/modal-dialogs-screenshots-9apbe8

Conversation

@simonw

@simonw simonw commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Two fixes for server: handling, found while setting up shot-scraper multi shots.yml --no-clobber to generate Datasette's documentation screenshots.

Kill the whole server process group on cleanup

A string server: command runs via sh -c. On Linux, where /bin/sh is usually dash, the shell forks the command instead of exec-ing it. Killing the shell therefore left the real server running as an orphan: it held its port and kept any pipe on its stdout open. With a Datasette server, just shots | tail hung until the server was killed by hand. The existing test suite also left python -m http.server 9023 and slow_server.py running after every run.

  • _start_server() now passes start_new_session=True on POSIX, so each server gets its own process group.
  • The new _stop_server() kills that whole group with os.killpg(..., SIGKILL). On Windows it still calls process.kill().
  • _cleanup_servers() uses _stop_server(), so both multi and video are covered.
  • The video tests with fake server processes now mock _stop_server().
  • New test test_multi_server_string_child_process_is_stopped fails before the fix.

multi does nothing when every step is ruled out

Before this change, multi --no-clobber started every server: step and launched the browser before checking whether any shot was missing. When every output already existed, the run started a server, launched Chromium and immediately killed both.

Now, if --no-clobber or --output rule out every sh:, python: and url: step, multi returns before starting any servers or Playwright. The checks inside the loop are unchanged; they have moved into a _skipped_by_options() helper. In a local test, a run with nothing to do dropped from about 0.7s to about 0.25s, which is just Python imports.

  • New tests: test_multi_nothing_to_do_skips_server_and_browser, parametrized for -n and -o, fails before the fix. test_multi_no_clobber_starts_server_for_missing_shot checks that a remaining shot still starts the server.
  • docs/multi.md documents the new behavior and fixes the "aleady" typo.

One edge-case change: a YAML file with no sh:, python: or url: steps at all, such as one with only server: items, now exits without starting anything.

Testing

uv run pytest: 167 passed. No http.server or slow_server.py processes are left running afterwards. cog --check docs/*.md passes.

The branch history adds a Popen subclass in one commit and replaces it with the functions above in the next, so a squash merge gives a cleaner history.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VL4haavg5i8JXVFFrPU8p3


Generated by Claude Code


📚 Documentation preview 📚: https://shot-scraper--211.org.readthedocs.build/en/211/

A string server: command runs via "sh -c". On systems where /bin/sh is
dash the shell forks the command rather than exec-ing it, so killing
the shell left the actual server running, orphaned - holding its port
and keeping any pipe on its stdout open.

Servers now run in their own process group, which is killed as a whole
when shot-scraper multi or video finishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL4haavg5i8JXVFFrPU8p3
_start_server() passes start_new_session to Popen and the new
_stop_server() kills the process group. Tests that fake server
processes now mock _stop_server().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL4haavg5i8JXVFFrPU8p3
If --no-clobber or --output rule out every shot, shot-scraper multi now
exits before starting any server: processes or launching the browser.
This makes "multi shots.yml -n" a cheap way to create just the missing
screenshots, even when the file defines a server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL4haavg5i8JXVFFrPU8p3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL4haavg5i8JXVFFrPU8p3

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants