Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standard input/output support 3: Python SDK stdout impl/examples/docs #4513

Merged
merged 7 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/content/reference/sdk-operating-modes.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ Use [`RecordingStream::save`](https://docs.rs/rerun/latest/rerun/struct.Recordin

Streams all logging data to standard output, which can then be loaded by the Rerun Viewer by streaming it from standard input.

#### Python

Use [`rr.stdout`](https://ref.rerun.io/docs/python/stable/common/initialization_functions/#rerun.stdout?speculative-link).

Check out our [dedicated example](https://github.com/rerun-io/rerun/tree/latest/examples/python/stdio/main.py?speculative-link).

#### Rust

Use [`RecordingStream::stdout`](https://docs.rs/rerun/latest/rerun/struct.RecordingStream.html#method.stdout?speculative-link).
Expand Down
1 change: 1 addition & 0 deletions examples/python/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
-r segment_anything_model/requirements.txt
-r shared_recording/requirements.txt
-r signed_distance_fields/requirements.txt
-r stdio/requirements.txt
-r structure_from_motion/requirements.txt
-r template/requirements.txt
22 changes: 22 additions & 0 deletions examples/python/stdio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: Standard Input/Output example
python: https://github.com/rerun-io/rerun/tree/latest/examples/python/stdio/main.py?speculative-link
rust: https://github.com/rerun-io/rerun/tree/latest/examples/rust/stdio/src/main.rs?speculative-link
cpp: https://github.com/rerun-io/rerun/tree/latest/examples/cpp/stdio/main.cpp?speculative-link
thumbnail: https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/480w.png
thumbnail_dimensions: [480, 298]
---

<picture>
<img src="https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/full.png" alt="">
<source media="(max-width: 480px)" srcset="https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/480w.png">
<source media="(max-width: 768px)" srcset="https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/768w.png">
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/1024w.png">
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/stdio/25c5aba992d4c8b3861386d8d9539a4823dca117/1200w.png">
</picture>

Demonstrates how to log data to standard output with the Rerun SDK, and then visualize it from standard input with the Rerun Viewer.

```bash
echo 'hello from stdin!' | python main.py | rerun -
```
21 changes: 21 additions & 0 deletions examples/python/stdio/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""
Demonstrates how to use standard input/output with the Rerun SDK/Viewer.

Usage: `echo 'hello from stdin!' | python main.py | rerun -`
"""
from __future__ import annotations

import sys

import rerun as rr # pip install rerun-sdk

# sanity-check since all other example scripts take arguments:
assert len(sys.argv) == 1, f"{sys.argv[0]} does not take any arguments"

rr.init("rerun_example_stdio")
rr.stdout()

input = sys.stdin.buffer.read()

rr.log("stdin", rr.TextDocument(input.decode("utf-8")))
1 change: 1 addition & 0 deletions examples/python/stdio/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rerun-sdk
4 changes: 2 additions & 2 deletions rerun_py/rerun_sdk/rerun/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
set_thread_local_data_recording,
)
from .script_helpers import script_add_args, script_setup, script_teardown
from .sinks import connect, disconnect, memory_recording, save, serve, spawn
from .sinks import connect, disconnect, memory_recording, save, serve, spawn, stdout
from .time import (
disable_timeline,
reset_time,
Expand Down Expand Up @@ -192,7 +192,7 @@ def _init_recording_stream() -> None:
from rerun.recording_stream import _patch as recording_stream_patch

recording_stream_patch(
[connect, save, disconnect, memory_recording, serve, spawn]
[connect, save, stdout, disconnect, memory_recording, serve, spawn]
+ [
set_time_sequence,
set_time_seconds,
Expand Down
28 changes: 28 additions & 0 deletions rerun_py/rerun_sdk/rerun/sinks.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,34 @@ def save(path: str | pathlib.Path, recording: RecordingStream | None = None) ->
bindings.save(path=str(path), recording=recording)


def stdout(recording: RecordingStream | None = None) -> None:
"""
Stream all log-data to stdout.

Pipe it into a Rerun Viewer to visualize it.

Call this _before_ you log any data!

If there isn't any listener at the other end of the pipe, the `RecordingStream` will
default back to `buffered` mode, in order not to break the user's terminal.

Parameters
----------
recording:
Specifies the [`rerun.RecordingStream`][] to use.
If left unspecified, defaults to the current active data recording, if there is one.
See also: [`rerun.init`][], [`rerun.set_global_data_recording`][].

"""

if not bindings.is_enabled():
logging.warning("Rerun is disabled - save() call ignored. You must call rerun.init before saving a recording.")
return

recording = RecordingStream.to_native(recording)
bindings.stdout(recording=recording)


def disconnect(recording: RecordingStream | None = None) -> None:
"""
Closes all TCP connections, servers, and files.
Expand Down
17 changes: 17 additions & 0 deletions rerun_py/src/python_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ fn rerun_bindings(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(is_enabled, m)?)?;
m.add_function(wrap_pyfunction!(connect, m)?)?;
m.add_function(wrap_pyfunction!(save, m)?)?;
m.add_function(wrap_pyfunction!(stdout, m)?)?;
m.add_function(wrap_pyfunction!(memory_recording, m)?)?;
m.add_function(wrap_pyfunction!(serve, m)?)?;
m.add_function(wrap_pyfunction!(disconnect, m)?)?;
Expand Down Expand Up @@ -525,6 +526,22 @@ fn save(path: &str, recording: Option<&PyRecordingStream>, py: Python<'_>) -> Py
})
}

#[pyfunction]
#[pyo3(signature = (recording = None))]
fn stdout(recording: Option<&PyRecordingStream>, py: Python<'_>) -> PyResult<()> {
let Some(recording) = get_data_recording(recording) else {
return Ok(());
};

// The call to stdout may internally flush.
// Release the GIL in case any flushing behavior needs to cleanup a python object.
py.allow_threads(|| {
recording
.stdout()
.map_err(|err| PyRuntimeError::new_err(err.to_string()))
})
}

/// Create an in-memory rrd file
#[pyfunction]
#[pyo3(signature = (recording = None))]
Expand Down
Loading