π‘ unaiverse.utils.os_fd_capture
What this module does π‘
Provides the FdCapture utility that redirects and reads OS-level file descriptors (stdout/stderr) via a background reader thread, used to surface native Go library output through the Python logger.
os_fd_capture
¶
βββββ βββββ ββββββ βββββ βββββ βββββ βββββ ββββββββββ βββββββββββ βββββββββ ββββββββββ βββββ βββββ ββββββββ βββββ βββββ βββββ βββββ ββββββββββββββββββββββββ ββββββββββββββββββββββ ββββ ββββ ββββββββ ββββ ββββββ ββββ ββββ ββββ ββββ β β ββββ ββββ ββββ βββ ββββ β β ββββ ββββ βββββββββββββ ββββββββ ββββ ββββ ββββ βββββββ βββββββββββ βββββββββββ βββββββ ββββ ββββ ββββ ββββββββ βββββββ ββββ βββββ βββ βββββββ ββββββββββββ βββββββββββ βββββββ ββββ ββββ ββββ βββββββ ββββββββ ββββ βββββββββ ββββ β β ββββ ββββ βββ ββββ ββββ β β ββββββββββ βββββ βββββββββββββββββ βββββ βββββ ββββββββββ βββββ ββββββββββββββββ ββββββββββ ββββββββ βββββ βββββ ββββββββ βββββ βββ ββββββββββ βββββ βββββ βββββββββ ββββββββββ A Collectionless AI Project (https://collectionless.ai) Registration/Login: https://unaiverse.io Code Repositories: https://github.com/collectionlessai/ Main Developers: Stefano Melacci (Project Leader), Christian Di Maio, Tommaso Guidi
FdCapture
¶
FdCapture(fd: int, callback: Callable[[str], None], encoding: str = 'utf-8', errors: str = 'replace')
OS-level file-descriptor redirector for capturing C and Go library output.
The Go libp2p library writes its log lines directly to the OS stdout/stderr file
descriptors (fd 1 and fd 2), bypassing Python's sys.stdout/sys.stderr
entirely. A simple sys.stdout reassignment therefore has no effect on that output.
FdCapture solves this by operating at the OS level using POSIX dup/dup2
primitives:
- An
os.pipeis created, producing a (read_fd, write_fd) pair. - The original target file descriptor (e.g. fd 1 for stdout) is saved via
os.dupand exposed asreal_fd. - The pipe's write end is installed over the target fd with
os.dup2, so every write to that fd -- from any language or library -- flows into the pipe instead. sys.stdoutorsys.stderris replaced with a wrapper aroundreal_fdso that Python's own logging and print statements still reach the real terminal.- A background daemon thread reads from the pipe line-by-line and invokes the
caller-supplied
callbackfor each non-blank line. - On
stop, the pipe's write end is closed (signalling EOF to the reader thread), the reader drains remaining data, and the original fd is restored.
Both stdout (fd 1) and stderr (fd 2) can be captured simultaneously by creating two
independent FdCapture instances.
FdCapture supports the context manager protocol: start is called on entry and
stop is called on exit (see __enter__/__exit__).
Attributes:
| Name | Type | Description |
|---|---|---|
real_fd |
int | None
|
The duplicated copy of the original file descriptor, available while
capture is active (see |
Note
start and stop are idempotent and protected by an internal threading.Lock,
making them safe to call from multiple threads.
Initialize an FdCapture instance without starting the capture.
Validates that fd is either 1 (stdout) or 2 (stderr) and stores all
configuration for later use by start. No OS-level operations are performed
here; the pipe and dup machinery are set up only when start is called.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fd
|
int
|
The OS file descriptor to capture. Must be |
required |
callback
|
Callable[[str], None]
|
Called for every complete line read from the captured fd. Receives
the decoded line as a |
required |
encoding
|
str
|
Byte encoding used to decode captured bytes. Defaults to
|
'utf-8'
|
errors
|
str
|
Error handler name passed to |
'replace'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in unaiverse/utils/os_fd_capture.py
real_fd
property
¶
The duped copy of the original fd, valid only while capture is active.
After start is called, the original OS file descriptor (e.g. fd 1 for stdout)
is saved by calling os.dup before it is overwritten with the pipe's write end.
This saved descriptor is exposed here so that callers can write directly to the
real terminal without the output being intercepted by the pipe.
Returns:
| Type | Description |
|---|---|
int | None
|
The duplicated file descriptor integer when the capture is active, or |
int | None
|
if |
start
¶
Begin capturing output written to the target file descriptor.
Performs the following sequence under an internal lock, so concurrent calls are safe and the method is idempotent (a second call while already active is a no-op):
- Flushes the Python-level stream (
sys.stdoutorsys.stderr) to avoid interleaving buffered data with the new pipe. - Saves the original OS file descriptor via
os.dup(accessible viareal_fd). - Creates a new
os.pipe(read end and write end). - Redirects the target fd to the write end of the pipe with
os.dup2, so that all future writes to that fd (including from C and Go libraries) flow into the pipe.sys.stdout/sys.stderrare then reassigned to wrapreal_fddirectly, ensuring that Python-level logging still reaches the terminal. - Launches a background daemon thread (
_reader_loop) that reads lines from the pipe and invokes thecallbackfor each non-blank line.
Note
After start returns, any write to fd 1 or fd 2 (depending on which fd was
captured) goes into the pipe and is delivered to the callback. Direct writes to
the Python sys.stdout/sys.stderr objects bypass the pipe and reach the
real terminal through real_fd.
Source code in unaiverse/utils/os_fd_capture.py
stop
¶
Stop capturing and restore the original file descriptor.
Idempotent: a call while capture is not active returns immediately without performing any OS operations. The shutdown sequence is:
- Flush and restore
sys.stdout/sys.stderrto the original Python stream object saved duringstart. - Restore the original OS-level fd with
os.dup2(saved_fd, target_fd), then closesaved_fd. - Close the write end of the pipe so that the reader thread sees an EOF and exits naturally.
- Wait up to
timeoutseconds for the reader thread to drain any remaining buffered data and terminate. - Close the read end of the pipe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float
|
Maximum number of seconds to wait for the background reader thread
to drain and exit before returning. Defaults to |
2.0
|
Note
Any lines already in the pipe buffer when stop is called are flushed to
the callback by the reader thread before it exits (subject to the timeout
limit). Lines still unread when the thread is abandoned are discarded.