This release is a pre-release and may not be stable for production use.
Outwrite
Standard Python logging and one live status line, across threads and processes.
Outwrite connects Python's logging module to Rich and relays worker output to one renderer in the parent process. Use your existing processes and ordinary loggers. No queue management in application code.
This is an early alpha. APIs may change; it is not production-certified. Python 3.11+ is required. Rich is the only runtime dependency.
Installation
The first release is a prerelease:
python -m pip install outwrite==0.1.0a1
Or use python -m pip install --pre outwrite to allow the latest prerelease.
These commands become available once the release is published to PyPI.
To install from a local checkout:
python -m pip install .
One process
import logging
from outwrite import setup_output, status
logger = logging.getLogger("my_app")
with setup_output(logger=logger) as output:
logger.info("Starting")
status.update("Processing files")
# Do some work.
status.clear()
logger.info("Done")
Worker processes
Pass the connection returned by the parent to each worker. Call the same setup function inside each worker:
import logging
import multiprocessing as mp
import signal
from outwrite import setup_output, status
logger = logging.getLogger("my_app")
def work(output, stop):
signal.signal(signal.SIGINT, signal.SIG_IGN)
setup_output(output)
logger.info("Worker started")
for index in range(10):
if stop.is_set():
break
status.update(f"Processing file {index + 1}")
stop.wait(0.1)
if __name__ == "__main__":
ctx = mp.get_context("spawn")
stop = ctx.Event()
with setup_output(logger=logger, mp_context=ctx) as output:
process = ctx.Process(target=work, args=(output, stop))
process.start()
try:
process.join()
finally:
stop.set()
process.join()
Always join workers and logging threads inside the parent context. On exit, Outwrite drains queued messages, clears the status, and releases its resources. Exit cleanup is also registered as a fallback; it does not stop or join workers for you. Forced process termination can lose messages or damage multiprocessing queues, so prefer a cooperative worker stop in real applications.
See the runnable example.
API
setup_output(logger=logger, level=logging.INFO, show_status=True, mp_context=ctx)starts parent output and returns anOutputConnectioncontext handle.setup_output(output)connects a worker. The parent's logger name and status setting are carried by the connection; keyword options are for parent setup.status.update(text)replaces the status line.Noneor""clears it.status.clear()clears the line without ending the session.
Status calls are safe from threads. Only one session and one status line are
active per process; concurrent writers replace the same line. Status calls before
setup or after parent cleanup raise RuntimeError.
The console uses stderr. Status is automatically disabled outside terminals;
logs still appear. show_status=False disables only status, and level controls
only the Rich console handler. Use integer logging levels, such as logging.DEBUG.
Logging behavior
Only the selected named logger and its propagating descendants are routed.
The root logger and unrelated loggers are not configured. Child loggers with
their own handlers or propagate=False retain their normal logging behavior.
Existing handlers on the selected logger also receive records in the parent.
For example, attach a standard logging.FileHandler before setup to retain file
logging. Outwrite does not close your handlers. It adds a Rich console handler,
so remove an existing console handler yourself if you do not want duplicate
console output. The logger's original handlers, level, and propagation are
restored when the parent context exits.
Records pass through standard QueueHandler preparation: exception tracebacks
become message text. Extra record attributes must be picklable. Cross-process
delivery is asynchronous; use a cooperative worker shutdown and join workers to
flush their messages. Ordering between different producers is not guaranteed.
The default queue context is spawn. If using forkserver, pass that same
context to setup and use it to create processes. fork is deliberately unsupported
because the parent has an active rendering thread. Do not send a connection to
an already-running pool; pass it when creating workers or through a pool initializer.
Rendering failures are reported to stderr and raised when the parent context exits. Standard logging handlers retain their usual error-reporting behavior. No arbitrary stdout/stderr capture is performed.
Why not just Rich?
Rich already supplies the columns, colors, links, and live display. Outwrite adds the connection between existing worker processes, standard logging, and one live status line. It is not a new renderer, process pool, or progress-bar framework.
Development
python -m pip install -e .
python examples/multiprocessing_demo.py
python -m pip install build
python -m build
No persisted test suite is included in this initial iteration. Add regression tests before a production release, particularly for process shutdown and terminal cleanup.
The initial wheel was verified on macOS ARM64 with Python 3.13.7 and 3.14.0, including spawn/forkserver workers and real-terminal Ctrl+C cleanup. Python 3.11/3.12 and other operating systems have not yet been runtime-tested.
License
MIT. The initial implementation adapts output coordination developed in Firefly, with its author's permission.
Publishing
Maintainers use PyPI Trusted Publishing; no permanent upload token is stored in
GitHub. The GitHub environment pypi requires approval before publication.
- Update the version in
pyproject.toml, and commit and push the change. - Create a GitHub release with a matching tag, such as
v0.1.0a1. Mark alpha/beta/RC versions as prereleases. - Review the build job and approve the
pypienvironment deployment. - Verify the uploaded version on PyPI and install it in a fresh environment.
The workflow builds and validates the distributions before publishing. Release
files cannot be replaced on PyPI; use a new version to correct a release.
The initial pending publisher must be configured on PyPI for repository
ariel-m-s/outwrite, workflow publish.yml, and environment pypi.
Metadata
Release files for outwrite 0.1.0a1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| outwrite-0.1.0a1.tar.gz | 10.2 kB | With provenance | Details for outwrite-0.1.0a1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| outwrite-0.1.0a1-py3-none-any.whl | Python 3 | none | any | Details for outwrite-0.1.0a1-py3-none-any.whl |
Total release size: 18.0 kB
Release files / outwrite-0.1.0a1.tar.gz
| Download URL | outwrite-0.1.0a1.tar.gz |
|---|---|
| Size | 10.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b407e42795be2657465bd99f28065893c82b11837d31346ad32cdfe9c643ba00
|
|
BLAKE2b-256 checksum How to use checksums |
0da4cfc9df09f4446a62975e20bdb6b7689bebc4e79e764833f125a3ee9123a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logRelease files / outwrite-0.1.0a1-py3-none-any.whl
| Download URL | outwrite-0.1.0a1-py3-none-any.whl |
|---|---|
| Size | 7.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0c8e7cef02cc97a9341127285a5cc2cedbfe041cd73e111a3a23f3d1359a0a96
|
|
BLAKE2b-256 checksum How to use checksums |
bc1b87321c41e5199b8d3cdc5ef269f5dbbc3d044d974293655f8cf20cc4a6cc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logSecurity for outwrite 0.1.0a1
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for outwrite-0.1.0a1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 29, 2026.
Transparency logA 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.