Metadata-Version: 2.4
Name: process-watchman
Version: 0.2.1
Summary: A small, focused Python library for tracking Linux process trees rooted at a specific PID.
Author: ATTMOS Inc
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: psutil>=5.9.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Dynamic: license-file

# process-watchman

**process-watchman** is a small, focused Python library for tracking Linux process trees rooted at a specific PID.

It answers questions like:
- Which subprocesses are currently alive?
- Which processes exited since the last poll?
- Is the root process still valid (PID reuse safe)?
- What is the current live descendant set?

---

## Why process-watchman?

Workflow monitoring often fails because:
- parent wrappers exit while compute children keep running
- PIDs get reused
- process discovery is unreliable across polling intervals

process-watchman solves this by:
- tracking process identity as `(pid + create_time)`
- maintaining a live descendant set
- emitting clean deltas (added / removed processes)

---

## Installation

```bash
pip install process-watchman
```

For development:

```bash
git clone https://github.com/ATTMOS/process-watchman.git
cd process-watchman
pip install -e ".[dev]"
```

---

## Quick start

```python
from process_watchman import ProcessTreeWatcher, WatcherConfig

# Attach to a running process by PID
watcher = ProcessTreeWatcher.attach(root_pid=12345)

# Poll the process tree
snapshot = watcher.poll()

print(f"Root alive: {snapshot.root_alive}")
print(f"Descendants: {len(snapshot.processes)}")
print(f"New PIDs: {snapshot.delta.added_pids}")

# Poll again later to see what changed
snapshot2 = watcher.poll()
print(f"Added: {snapshot2.delta.added_pids}")
print(f"Removed: {snapshot2.delta.removed_pids}")

# Get the current live set at any time
live = watcher.live_pids()
```

---

## Configuration

Pass a `WatcherConfig` to customize behaviour:

```python
from process_watchman import ProcessTreeWatcher, WatcherConfig

cfg = WatcherConfig(
    max_depth=8,              # limit tree traversal depth
    include_root=True,        # include the root process in results
    max_descendants=1000,     # cap collected descendants per poll
    strategy="scan_ppid",     # use BFS fallback instead of default
    capture_cmdline=True,     # collect command lines
    cmdline_max_chars=100,    # truncate long command lines
    keep_tombstones=True,     # retain info for removed processes
)

watcher = ProcessTreeWatcher.attach(root_pid=12345, config=cfg)
```

See [docs/api.md](docs/api.md) for the full list of configuration options.

---

## API overview

| Type | Description |
|------|-------------|
| `ProcessTreeWatcher` | Main class — `attach()`, `poll()`, `live_pids()` |
| `WatcherConfig` | Configuration with sensible defaults |
| `Snapshot` | Point-in-time observation returned by `poll()` |
| `Delta` | Changes between consecutive polls |
| `ProcessIdentity` | PID + create_time (safe against PID reuse) |
| `ProcessInfo` | Per-process metadata (name, cmdline, status) |

Full API reference: [docs/api.md](docs/api.md)

---

## License

MIT
