Repository navigation
Use servers/_process.py for the CLI's process checks - #518
Conversation
lean_probe kept its own copies of three helpers the Lean runtime daemon has in servers/_process.py: the clean child environment, the process group liveness check and the scan for processes carrying a token. The copies had drifted. The CLI now uses the daemon's versions, so both sides follow one evidence rule: anything uncertain counts as live, or the scan fails. lean_probe loads servers/_process.py by path from beside its package and registers it as autoform_cli._process_helpers, instead of importing the top-level servers package. Under `python -m autoform_cli`, the working directory comes first on sys.path, so a consumer's own `servers` package would otherwise be imported, or would run in place of ours. The loader's docstring says how tests reach the CLI's copy. servers/_process.py says it is loaded this way and so imports only the standard library and psutil. Behavior changes in the CLI: - The token scan skips only a process that has exited or whose environment access is denied. Another environment error now fails the scan attempt, as it does in the daemon; after two attempts the success gate fails with the existing "repeated process-table errors" message, and cleanup carries on without that pass's finds. - The success gate turns an error from the process group check into "<context> could not inspect its process group". It catches RuntimeError, OSError, SystemError and psutil.Error only, so other exceptions still surface as bugs. Before, a killpg error other than ESRCH or EPERM counted as a gone group and an unreadable process table counted as a live one. `_process_group_is_alive`, `_process_group_has_live_members` and `_probe_environment` go away; `_process_is_alive` stays, since the "still running" rule is not shared. Tests: the five token scan tests patch psutil.pids and psutil.Process instead of psutil.process_iter, with unchanged assertions. New tests cover an unreadable environment and a process group the gate cannot inspect. tests/test_lean_boundary.py pins that the CLI loads our _process.py from a root holding its own `servers` package without importing that package, and that _process.py imports only the standard library and psutil. The wheel test checks that the wheel ships servers/_process.py and that lean_probe loads it from the installed tree. CHANGELOG gains a "Process checks." entry.
|
Review at 1263d6f: merge. Nothing blocks merge. The PR does what Jack decided on 10-29: it loads
Notes
Checks
Posted by Claude Code (session jackmccarthy-eb). |
Review note on #518: the docstring ran 15 lines for a 7-line function and said the module is registered because dataclasses look it up, but servers/_process.py defines no dataclass.
|
Applied the docstring note in 22cbee2: the loader's docstring is now the six lines you suggested, without the dataclass sentence. The relative-import rule it dropped is still pinned by Posted by Claude Code (session jackmccarthy-e8). |
|
Head check at 22cbee2: merge. 22cbee2, on top of the reviewed 1263d6f, changes only the Posted by Claude Code (session jackmccarthy-eb). |
A follow-up to #482: the CLI half of sharing the process helpers.
lean_probedrops its own copies of the clean environment, the process group check and the token scan, and uses the ones inservers/_process.pythat #502 made public, so the CLI and the Lean runtime daemon follow one rule: anything uncertain counts as live, or the scan fails.Change
lean_probeloadsservers/_process.pyfrom beside its own package,Path(__file__).resolve().parent.parent / "servers" / "_process.py", the expression the daemon uses forPACKAGE_ROOT. The module is registered insys.modulesasautoform_cli._process_helpersbefore it runs, as the importlib recipe does.import serverswould find a consumer's own top-levelserversfirst underpython -m autoform_cliorpython -, which put the working directory first onsys.path. Since__main__importslean_probe, every command would then fail, or run the consumer's_process.py.os,psutilorlean_probe._process_helpers, neverservers._process.servers/_process.py's docstring now says it is loaded this way and so imports only the standard library and psutil._remember_tagged_processeskeeps its signature and callsprocesses_with_token(_PROCESS_TOKEN_ENV, token). It still tries twice, then fails with the existing "repeated process-table errors" message, or returns silently in cleanup.SystemErroralready did; before, the CLI skipped that one process.process_group_has_live_members. An error from it becomesSkeletonError("<context> could not inspect its process group"). The gate catchesRuntimeError,OSError,SystemErrorandpsutil.Erroronly, so other exceptions still surface as bugs.killpg(pgid, 0)error other than ESRCH or EPERM counted as a gone group, and an unreadable process table counted as a live one._process_group_is_alive,_process_group_has_live_membersand_probe_environment;run_probeand the helper build useinherit_clean_env()._process_is_alivestays: the "still running" rule is not shared, so the daemon's behavior is unchanged.psutil.pidsandpsutil.Processinstead ofpsutil.process_iter. Their assertions are unchanged.test_tagged_process_scan_fails_closed_on_an_unreadable_environment: anEIOfromenviron()fails a strict scan and is silent in cleanup; a denied process beside a tagged one is skipped and the tagged one is found.test_bounded_command_reports_a_process_group_it_cannot_inspect(POSIX), for an unreadable process table and forkillpg(_, 0)failing with EINVAL.tests/test_lean_boundary.pygains two pins and a docstring sentence. From a temporary root holding its ownserverspackage, whose files exit if run, a subprocess withoutPYTHONSAFEPATH(so the working directory comes first onsys.path) importsautoform_cli.__main__andlean_probe; the CLI loads this repository's_process.pyand never importsservers. An AST check holds_process.pyto absolute imports of the standard library and psutil.servers/_process.pyin the wheel and checks thatlean_probeloads it from the extracted tree._process.pyis in the daemon's code fingerprint, so each install's daemon restarts once after the upgrade.Net: +191/−71 across six files.
Verification
checkpasses onautoform_cli servers tests, as CI runs it, and the touched files compile.ruff format --checkflagslean_probe.py,_process.py,test_lean_probe.pyandtest_plugin_runtime.pyin places this PR does not touch, as it does on the base; CI does not run the formatter._process_group_has_live_members,_inherit_clean_env,_processes_with_token,_probe_environmentor_process_group_is_alive, and noautoform_climodule imports the top-levelserverspackage.serverspackage, aservers.pymodule and a namespaceserversdirectory, importingautoform_cli.__main__with the working directory first onsys.pathloads this repository'sservers/_process.pyand leavesserversout ofsys.modules.python -m autoform_cli --helpexits 0 from the regular-package root, and the consumer's code never runs.-Ias the wheel test does, loads_process.pyfrom that directory. CI runs the wheel test itself.tests/test_lean_boundary.py: 6 passed, including Test that within autoform_cli only lean_probe starts Lean, and that autoform_cli and servers never import each other #499's check thatlean_probeimports nothing fromautoform_cli(registering the loaded module insys.modulesis not an import) and its check that the scan sees exactlylean_probe'sPopenandshutil.whichcalls;tests/test_lean_probe.py: 81 passed, 3 skipped that need real Lean, 1 failed: a Lake configuration snapshot test that compares against the unresolved temporary path when called directly, and fails the same way on main;tests/test_lsp_protocol.py: 12 passed;tests/test_lean_probe.pyand all oftests/test_lean_boundary.pypass._process.py's docstring, to keep Move the LSP token scan into servers/_process.py and drop the underscore from its shared names #502's first line):from servers import _process as _process_helpers: the loader pin and the "never imports servers" test fail, and the loader pin still fails withPYTHONSAFEPATH=1set;tryremoved: both cases of the group test fail, with anOSErrorand aRuntimeError;processes_with_tokenskipping everypsutil.ErrorandOSError, as the CLI's old scan did: the environment test fails;from servers import lean_clientadded to_process.py: the import pin and the loader pin fail.test(Python 3.10 and 3.13): the scan, gate and boundary tests, and the wheel test;real-lean, impact and library steps:run_probewith the shared clean environment and gate on real Lean runs;real-lean-skeleton:tests/test_skeleton.pyandtests/test_lean_probe.pyagainst real Lake and Lean, including the success gate;windows-hardeningruns only the node IDs it lists. Several of its modules importautoform_cli.__main__orlean_probe, so the loader runs on Windows. Of its six bounded-command tests, at leasttest_bounded_command_rejects_invalid_utf8andtest_bounded_command_rejects_a_successful_parent_with_a_live_descendantreach the success gate, where the strict token scan reads Windows' process table. The group check is POSIX-only, and the two new tests are not in that list.Overlap
tests/test_lean_probe.py, which Move the lean_probe runner tests into tests/test_lean_probe.py #504 created.autoform_cli/lean_probe.pyandtests/test_lean_probe.py, and most open PRs add CHANGELOG entries. Each merges with this branch without conflict, and none adds a conflict it doesn't already have with main.tests/test_plugin_runtime.pyin other hunks; it already conflicts with main in that file, not in this PR's lines.