Capture C-level stdout/stderr pipes in Python via os.dup2
Enable and disable permanent C-level output forwarding without context managers, useful for global configuration and long-running applications.
Start permanent forwarding of all C-level output to Python sys streams.
def sys_pipes_forever(encoding='utf-8', bufsize=None):
"""Redirect all C output to sys.stdout/err permanently.
Args:
encoding: Text encoding for output (default: 'utf-8')
bufsize: Pipe buffer size in bytes (default: auto-detected)
Note:
This is not a context manager; it turns on C-forwarding permanently
until stop_sys_pipes() is called.
"""Example usage:
from wurlitzer import sys_pipes_forever
# Enable permanent redirection at application startup
sys_pipes_forever()
# All subsequent C-level output is forwarded
c_library_function_1()
other_c_operations()
c_library_function_2()
# Output forwarding continues until explicitly stoppedStop permanent redirection and restore original C-level stream behavior.
def stop_sys_pipes():
"""Stop permanent redirection started by sys_pipes_forever"""Example usage:
from wurlitzer import sys_pipes_forever, stop_sys_pipes
# Start permanent forwarding
sys_pipes_forever()
# ... application runs with C output forwarded ...
# Stop forwarding at application shutdown or when no longer needed
stop_sys_pipes()
# C output now goes back to original destinationsThe permanent redirection system uses thread-safe locks to manage global state.
from wurlitzer import sys_pipes_forever, stop_sys_pipes
import threading
def worker_function():
# Multiple threads can safely call these functions
sys_pipes_forever() # Only activates once globally
c_work_function()
stop_sys_pipes() # Only deactivates when last thread calls it
# Safe in multi-threaded environments
threads = [threading.Thread(target=worker_function) for _ in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()Configure C-level output forwarding during application startup:
from wurlitzer import sys_pipes_forever
import logging
def initialize_app():
# Setup logging
logging.basicConfig(level=logging.INFO)
# Enable C output forwarding for entire application
sys_pipes_forever()
print("Application initialized with C output forwarding")
def main():
initialize_app()
# All C library calls throughout the application
# will have their output forwarded to sys.stdout/stderr
run_application_logic()Long-running services that need consistent C output handling:
import signal
import sys
from wurlitzer import sys_pipes_forever, stop_sys_pipes
class ServiceApplication:
def __init__(self):
# Enable C output forwarding for service lifetime
sys_pipes_forever()
def start(self):
# Setup signal handlers for clean shutdown
signal.signal(signal.SIGTERM, self._shutdown)
signal.signal(signal.SIGINT, self._shutdown)
# Service main loop
while self.running:
self.process_requests()
def _shutdown(self, signum, frame):
print("Shutting down service...")
stop_sys_pipes()
self.running = False
sys.exit(0)
def process_requests(self):
# C library calls have output forwarded automatically
c_request_processor.handle_batch()Configure test environments with global C output forwarding:
import pytest
from wurlitzer import sys_pipes_forever, stop_sys_pipes
@pytest.fixture(scope="session", autouse=True)
def setup_c_output_forwarding():
"""Enable C output forwarding for entire test session"""
sys_pipes_forever()
yield
stop_sys_pipes()
def test_c_function_1():
# C output automatically forwarded, captured by pytest
c_function_under_test()
def test_c_function_2():
# All tests benefit from automatic C output forwarding
another_c_function()Enable forwarding when importing libraries that use C extensions:
# mylib/__init__.py
from wurlitzer import sys_pipes_forever
# Automatically enable C output forwarding when library is imported
sys_pipes_forever()
from .core import main_functions
from .extensions import c_extensions
__all__ = ['main_functions', 'c_extensions']Enable permanent redirection based on environment or configuration:
import os
from wurlitzer import sys_pipes_forever
def configure_output_handling():
# Enable based on environment variable
if os.getenv('ENABLE_C_OUTPUT_FORWARDING', '').lower() == 'true':
sys_pipes_forever()
print("C output forwarding enabled")
# Or based on configuration file
if config.get('debug', {}).get('capture_c_output', False):
sys_pipes_forever()
print("Debug mode: C output forwarding enabled")
# Call during application setup
configure_output_handling()Multiple calls to sys_pipes_forever() are safe and idempotent:
from wurlitzer import sys_pipes_forever, stop_sys_pipes
# Multiple calls don't create multiple redirections
sys_pipes_forever() # Enables forwarding
sys_pipes_forever() # No additional effect
sys_pipes_forever() # Still no additional effect
stop_sys_pipes() # Disables forwarding
stop_sys_pipes() # Safe to call multiple timesCheck if permanent redirection is currently active:
from wurlitzer import sys_pipes_forever, stop_sys_pipes
import wurlitzer
# Check global state (internal variable)
def is_permanent_forwarding_active():
return wurlitzer._mighty_wurlitzer is not None
# Example usage
if not is_permanent_forwarding_active():
sys_pipes_forever()
# Later...
if is_permanent_forwarding_active():
print("C output is being forwarded")
stop_sys_pipes()Combine permanent redirection with temporary context managers:
from wurlitzer import sys_pipes_forever, stop_sys_pipes, pipes
# Global forwarding for most operations
sys_pipes_forever()
# Temporary override for specific capture
with pipes() as (out, err):
# This temporarily overrides the permanent forwarding
special_c_function()
captured_output = out.read()
# Back to permanent forwarding after context manager exits
normal_c_operations()Install with Tessl CLI
npx tessl i tessl/pypi-wurlitzer