Metadata-Version: 2.4
Name: live-tee-and-capture
Version: 0.1.0a1
Summary: Run a command and tee its stdout/stderr in real time to your terminal while also capturing them.
Author-email: Jifeng Wu <jifengwu2k@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/jifengwu2k/live-tee-and-capture
Project-URL: Bug Tracker, https://github.com/jifengwu2k/live-tee-and-capture/issues
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=2
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: ctypes-unicode-proclaunch
Requires-Dist: posix-or-nt
Requires-Dist: threading-value-event
Requires-Dist: typing; python_version < "3.5"
Dynamic: license-file

# `live-tee-and-capture`

Run a command and tee its stdout/stderr in real time to your terminal while also capturing them.

- Outputs are relayed in real time with no artificial buffering (a dedicated thread drains each pipe as data arrives) - ideal for real-time logs and progress bars.
- You can enable/disable teeing stdout/stderr independently.
- Optional `timeout`: the command's direct process is killed and `TimeoutExpired` is raised if it does not exit in time.
- File descriptors and reader threads are always cleaned up, even if launching fails or the command times out.
- Descendant processes are not killed automatically; inherited output pipes cannot delay completion after the direct process exits.

## Usage

### Example

```python
from live_tee_and_capture import live_tee_and_capture

exit_code, stdout_bytes, stderr_bytes = live_tee_and_capture(
    ['ls', '-la'],
    tee_stdout=True,
    tee_stderr=True,
)

print('Exit code:', exit_code)
print('Captured stdout:', stdout_bytes.decode())
print('Captured stderr:', stderr_bytes.decode())
```

### Function Signature

```python
def live_tee_and_capture(
    command: Sequence[Text],
    tee_stdout: bool = True,
    tee_stderr: bool = True,
    timeout: Optional[float] = None,
) -> Tuple[int, bytearray, bytearray]:
    ...
```

If `timeout` (in seconds) is given and the command does not exit within that time, its
direct process is killed and a `TimeoutExpired` exception is raised. Descendant processes
are not terminated automatically:

```python
from live_tee_and_capture import live_tee_and_capture, TimeoutExpired

try:
    live_tee_and_capture(['sleep', '30'], timeout=5)
except TimeoutExpired as e:
    print('Timed out:', e.command, e.timeout)
    # Output captured before the command was killed:
    print('Partial stdout:', e.stdout.decode())
    print('Partial stderr:', e.stderr.decode())
```

## Contributing

Contributions are welcome! Please submit pull requests or open issues on the GitHub repository.

## License

This project is licensed under the [MIT License](LICENSE).
