Skip to main content

subprocess-multitee

Multiplex subprocess stdout/stderr to multiple destinations simultaneously.

from subprocess_multitee import Popen, tee, PIPE
import sys

# Capture output AND print to terminal in real-time
proc = Popen(['make'], stdout=tee(PIPE, sys.stdout))
output = proc.stdout.read()
proc.wait()

Installation

pip install subprocess-multitee

Why?

Standard subprocess forces a choice: either capture output (stdout=PIPE) or display it (stdout=sys.stdout). This library lets you do both—plus log to files, discard with DEVNULL, or any combination.

The tee() function creates a pipe that spawns a background thread reading one byte at a time and writing to all destinations, ensuring real-time output even for line-buffered programs.

Usage

Basic: Capture + Display

from subprocess_multitee import Popen, tee, PIPE
import sys

proc = Popen(['python', 'train.py'], stdout=tee(PIPE, sys.stdout))
output = proc.stdout.read()  # Full output captured
proc.wait()                   # Terminal saw it in real-time

Log to File + Display + Capture

with open('build.log', 'wb') as log:
    proc = Popen(['make', '-j4'],
                 stdout=tee(PIPE, sys.stdout, log),
                 stderr=tee(PIPE, sys.stderr, log))
    stdout, stderr = proc.communicate()

With run() Helper

from subprocess_multitee import run, tee, PIPE
import sys

result = run(['pytest', '-v'], stdout=tee(PIPE, sys.stdout), check=True)
print(f"Captured {len(result.stdout)} bytes")

Discard + Capture (silent but recorded)

from subprocess_multitee import Popen, tee, PIPE, DEVNULL

proc = Popen(['noisy-command'], stdout=tee(DEVNULL, PIPE))
output = proc.stdout.read()  # Captured, but nothing printed

With stdlib Popen (manual cleanup)

import subprocess
from subprocess_multitee import tee, PIPE

t = tee(PIPE, open('log.txt', 'wb'))
proc = subprocess.Popen(['cmd'], stdout=t)
proc.wait()
t.close()  # Required with stdlib Popen
output = t.pipes[0].read()

API

tee(*destinations) -> _Tee

Creates a tee object for use as stdout or stderr in Popen.

Destinations can be any combination of:

  • PIPE — creates a readable pipe (accessible via .pipes list or Popen.stdout)
  • DEVNULL — discard output (no-op, skipped efficiently)
  • File objects — anything with .write() (binary mode recommended)
  • File descriptors — raw int fds
  • Text-mode files — automatically uses .buffer if available

Not supported: STDOUT (use stderr=tee(...) separately)

Popen

Drop-in subclass of subprocess.Popen with tee integration:

  • Automatically closes parent's write fd after fork
  • Exposes first PIPE destination as .stdout/.stderr
  • Joins tee threads on context manager exit

run(), call(), check_call(), check_output()

Drop-in replacements using the enhanced Popen.

How It Works

  1. tee() creates an os.pipe()
  2. Returns a object with fileno() pointing to the write end
  3. Spawns a daemon thread that reads 1 byte at a time and writes to all destinations
  4. Popen passes the fd to the subprocess, then closes the parent's copy
  5. When subprocess exits, the pipe closes, thread sees EOF and exits

Alternatives

  • subprocess-tee — Drop-in replacement for subprocess.run that prints output in real-time while capturing. Simple API (tee=False to disable), but implies universal_newlines=True (text mode only) and doesn't support multiple arbitrary destinations.

  • tee-subprocess — Also replaces subprocess.run with tee support, but adds async/await support via asyncio. Automatically detects async context. Supports redirecting tee output to file objects. Better static typing. More complex internals.

subprocess-multitee takes a different approach: a composable tee(*destinations) primitive that works with any number of destinations (multiple files, PIPE, DEVNULL, etc.) and a minimal Popen subclass. This gives more flexibility at the cost of slightly more verbose usage.

License

MIT

Metadata

Release files for subprocess-multitee 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for subprocess-multitee 0.2.1
File Size Uploaded Provenance Actions
subprocess_multitee-0.2.1.tar.gz 31.1 kB Without provenance Details for subprocess_multitee-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for subprocess-multitee 0.2.1
File Interpreter ABI Platform Actions
subprocess_multitee-0.2.1-py3-none-any.whl Python 3 none any Details for subprocess_multitee-0.2.1-py3-none-any.whl

Total release size: 39.3 kB

Release files / subprocess_multitee-0.2.1.tar.gz

Download URL subprocess_multitee-0.2.1.tar.gz
Size 31.1 kB
Tags Source
SHA-256 checksum
How to use checksums
69343c2f3db64b207db590eec0f7184048f8ebc342ab9b385f84fcd396e906e1
BLAKE2b-256 checksum
How to use checksums
692bf12e452b78931eaa6e1595bacf84836d7731689c18e0bec8fd7bd10834b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / subprocess_multitee-0.2.1-py3-none-any.whl

Download URL subprocess_multitee-0.2.1-py3-none-any.whl
Size 8.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2e4b309a24dcc507d52a91cffc16205c046cb62849ee7bf939aa13af82647f6f
BLAKE2b-256 checksum
How to use checksums
077d15540139bc4c9bc78883412a1fd175dcd3c6517f9ecdd5ac9d4db01896b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for subprocess-multitee 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page