Skip to content

Fix timeout child-process lookup on non-GNU systems - #2236

Open
aprylewu wants to merge 1 commit into
gitpython-developers:mainfrom
aprylewu:codex-fix-portable-timeout-child-lookup
Open

Fix timeout child-process lookup on non-GNU systems#2236
aprylewu wants to merge 1 commit into
gitpython-developers:mainfrom
aprylewu:codex-fix-portable-timeout-child-lookup

Conversation

@aprylewu

Copy link
Copy Markdown

Git.execute(..., kill_after_timeout=...) currently finds direct child processes with ps --ppid. On macOS, that command fails and only the parent receives SIGKILL; a child can continue running and keep the captured output pipes open after the timeout.

Use pgrep -P to enumerate direct children, falling back to POSIX ps -A -o pid= -o ppid= when pgrep is not installed. The fallback filters by the target parent's PID. Both enumeration subprocesses are waited for and their pipes closed. The existing parent-first signal order and direct-child scope are preserved.

The regression runs a real parent/child pair through Git.execute. Before the change, the child survives the timeout and writes a marker; after the change, it is terminated, both with native pgrep and with pgrep unavailable. A separate test checks that the fallback excludes unrelated processes and grandchildren.

Fixes #1756.

Validation on macOS 27.0, Python 3.13.5:

  • The two real-process regressions failed on the original code.
  • python -m pytest test/test_git.py -q --no-cov: 105 passed, 1 skipped.
  • ruff check . and ruff format --check . (0.16.5).
  • codespell git/cmd.py test/test_git.py (2.4.3).
  • mypy (1.18.2): 45 source files.
  • basedpyright --warnings (1.39.9): no errors or warnings.
  • git diff --check.

Linux and Cygwin were not run locally. The real-process regression skips Cygwin, whose default ps does not support these POSIX options; the documented tool requirements still apply. This does not add native Windows support or change the separate timeout implementation used by Remote.fetch, pull, and push.

I am an AI agent (OpenAI Codex) acting on behalf of Mingyang Wu (aprylewu). I prepared this implementation and ran the checks above.

Git.execute used ps --ppid to find direct children before enforcing
kill_after_timeout. On macOS this option is rejected: the parent is killed,
but a child can continue running and hold captured output pipes open.

Use pgrep -P for the child lookup, with POSIX ps PID/PPID output as a
fallback when pgrep is absent. Filter the fallback by the original parent
PID and reap the lookup subprocess in both paths. Keep the existing
parent-first SIGKILL order, direct-child scope, and Windows guard, and
update the documented command requirements. Systems without either lookup
facility and the existing PID-reuse race remain limitations.

Add real-process regressions for native pgrep and the ps fallback, plus a
test that excludes unrelated processes and grandchildren from the fallback.
Both real-process cases failed on the original code on macOS. The command
module now passes 105 tests with 1 skip on macOS 27.0 / Python 3.13.5.
Ruff check and format, codespell, mypy (45 files), basedpyright, and diff
whitespace checks pass. Linux and Cygwin were not run locally; Cygwin's
default ps lacks the required options, so the real-process cases skip it.

Fixes gitpython-developers#1756

Signed-off-by: Mingyang Wu <129849514+aprylewu@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Git.execute's kill_after_timeout callback assumes procps

1 participant