Migrating from 0.22 to 0.23
Reserved namespaces reserved-namespaces
Starting with this release, the SDKs will log Rerun-related information to reserved entity path namespaces that are prefixed with __
.
Most notably, there is __warnings/
, which used to be called rerun/
and can be used to log exceptions that occurred in the SDKs.
We also introduced __properties/
, which stores recording-level information that is logged via the new set_property
methods in the SDKs.
Reserved namespaces are highlighted with a āļø icon in the viewer UI.
Timelines are uniquely identified by name timelines-are-uniquely-identified-by-name
Previously, you could (confusingly) have two timelines with the same name, as long as they had different types (sequence vs temporal). This is no longer possible. Timelines are now uniquely identified by name, and if you use different types on the same timeline, you will get a logged warning, and the latest type will be used to interpret the full set of time data.
Unify the names of time units unify-the-names-of-time-units
We have been wildly inconsistent with how we name our time units, and it is time we fixed it. So starting now, we're using:
secs
instead ofs
orseconds
nanos
instead ofns
ornanoseconds
millis
instead ofms
ormilliseconds
All function and parameters using the old names have been deprecated, and will be removed in a future version.
Why these names?
- They are short without being cryptic
- They are the ones the Rust standard library (mostly) use: https://doc.rust-lang.org/stable/std/time/struct.Duration.html
- Anything is better than being inconsistent :)
Differentiate between timestamps and durations differentiate-between-timestamps-and-durations
We've added a explicit API for setting time, where you need to explicitly specify if a time is either a timestamp (e.g. 2025-03-03T14:34:56.123456789
) or a duration (e.g. 123s
).
Before, Rerun would try to guess what you meant (small values were assumed to be durations, and large values were assumes to be durations since the Unix epoch, i.e. timestamps). Now you need to be explicit.
š¦ Rust: deprecated RecordingStream::set_time_secs
and set_time_nanos
-rust-deprecated-recordingstreamsettimesecs-and-settimenanos
Use one of these instead:
set_duration_secs
set_timestamp_secs_since_epoch
set_time
withstd::time::Duration
set_time
withstd::time::SystemTime
š C++ -c
We've deprecated the following functions, with the following replacements:
set_time
->set_time_duration
orset_time_timestamp
set_time_seconds
->set_time_duration_secs
orset_time_timestamp_secs_since_epoch
set_time_nanos
->set_time_duration_nanos
orset_time_timestamp_nanos_since_epoch
TimeColumn
also has deprecated functions.
š Python: replaced rr.set_time_*
functions with a single rr.set_time
-python-replaced-rrsettime-functions-with-a-single-rrsettime
We've deprecated rr.set_time_secs
, rr.set_time_nanos
, as well as rr.set_time_sequence
and replaced them with rr.set_time
.
set_time
takes either a sequence=
, duration=
or timestamp=
argument.
duration
must be either:
- seconds as
int
orfloat
datetime.timedelta
numpy.timedelta64
timestamp
must be either:
- seconds since unix epoch (1970-01-01) as
int
orfloat
datetime.datetime
numpy.datetime64
Migrating
rr.set_sequence("foo", 42)
New: rr.set_time("foo", sequence=42)
rr.set_time_secs("foo", duration_secs)
When using relative times (durations/timedeltas): rr.set_time("foo", duration=duration_secs)
You can also pass in a datetime.timedelta
or numpy.timedelta64
directly.
rr.set_time_secs("foo", seconds_since_epoch)
New: rr.set_time("foo", timestamp=seconds_since_epoch)
You can also pass in a datetime.datetime
or numpy.datetime64
directly.
rr.set_time_nanos("foo", duration_nanos)
Either:
rr.set_time("foo", duration=1e-9 * duration_nanos)
rr.set_time("foo", duration=np.timedelta64(duration_nanos, 'ns'))
The former is subject to (double-precision) floating point precision loss (but still nanosecond precision for timedeltas below less than 100 days in duration), while the latter is lossless.
rr.set_time_nanos("foo", nanos_since_epoch)
Either:
rr.set_time("foo", timestamp=1e-9 * nanos_since_epoch)
rr.set_time("foo", timestamp=np.datetime64(nanos_since_epoch, 'ns'))
The former is subject to (double-precision) floating point precision loss (still microsecond precision for the next century), while the latter is lossless.
š Python: replaced rr.Time*Column
with rr.TimeColumn
-python-replaced-rrtimecolumn-with-rrtimecolumn
Similarly to the above new set_time
API, there is also a new TimeColumn
class that replaces TimeSequenceColumn
, TimeSecondsColumn
, and TimeNanosColumn
.
The migration is very similar to the above.
Migration
rr.TimeSequenceColumn("foo", values)
New: rr.TimeColumn("foo", sequence=values)
rr.TimeSecondsColumn("foo", duration_secs)
New: rr.TimeColumn("foo", duration=duration_secs)
rr.TimeSecondsColumn("foo", seconds_since_epoch)
New: rr.TimeColumn("foo", timestamp=seconds_since_epoch)
rr.TimeNanosColumn("foo", duration_nanos)
Either:
rr.TimeColumn("foo", duration=1e-9 * duration_nanos)
rr.TimeColumn("foo", duration=np.timedelta64(duration_nanos, 'ns'))
The former is subject to (double-precision) floating point precision loss (but still nanosecond precision for timedeltas below less than 100 days in duration), while the latter is lossless.
rr.TimeNanosColumn("foo", nanos_since_epoch)
Either:
rr.TimeColumn("foo", duration=1e-9 * nanos_since_epoch)
rr.TimeColumn("foo", duration=np.timedelta64(nanos_since_epoch, 'ns'))
The former is subject to (double-precision) floating point precision loss (still microsecond precision for the next century), while the latter is lossless.
Dataloader time arguments dataloader-time-arguments
The CLI API for external dataloaders has changed the following argument names:
--sequence
->--time_sequence
--time
->--time_duration_nanos
or--time_timestamp_nanos
š Python: rr.new_recording
is now deprecated in favor of rr.RecordingStream
-python-rrnewrecording-is-now-deprecated-in-favor-of-rrrecordingstream
Previously, RecordingStream
instances could be created with the rr.new_recording()
function. This method is now deprecated in favor of directly using the RecordingStream
constructor. The RecordingStream
constructor is mostly backward compatible, so in most case it is matter of using RecordingStream
instead of new_recording
:
# before
rec = rr. new_recording("rerun_example")
# after
rec = rr.RecordingStream("my_app_id")
If you used the spawn=True
argument, you will now have to call the spawn()
method explicitly:
# before
rec = rr. new_recording("my_app_id", spawn=True)
# after
rec = rr.RecordingStream("my_app_id")
rec.spawn()
š Python: removed rr.log_components()
, rr.connect()
, rr.connect_tcp()
, and rr.serve()
-python-removed-rrlogcomponents-rrconnect-rrconnecttcp-and-rrserve
These functions were deprecated in 0.22 and are no longer available.
Calls to rr.log_components()
API are now superseded by the new partial update API. See the documentation and the migration instructions.
Calls to rr.connect()
and rr.connect_tcp()
must be changed to rr.connect_grpc()
.
Calls to rr.serve()
must be changed to rr.serve_web()
.
š C++: removed connect
and connect_tcp
from RecordingStream
-c-removed-connect-and-connecttcp-from-recordingstream
Calls to these functions must be changed to connect_grpc
. Note that the string passed to connect_grpc
must now be a valid Rerun URL. If you were previously calling connect_grpc("127.0.0.1:9876")
, it must be changed to connect_grpc("rerun+http://127.0.0.1:9876/proxy")
.
See the RecordingStream
docs for more information.
š¦ Rust: removed connect
and connect_tcp
from RecordingStream
and RecordingStreamBuilder
-rust-removed-connect-and-connecttcp-from-recordingstream-and-recordingstreambuilder
Calls to these functions must be changed to use connect_grpc
instead.
Note that the string passed to connect_grpc
must now be a valid Rerun URL. If you were previously calling connect("127.0.0.1:9876")
, it must be changed to connect_grpc("rerun+http://127.0.0.1:9876/proxy")
.
The following schemes are supported: rerun+http://
, rerun+https://
and rerun://
, which is an alias for rerun+https://
.
These schemes are then converted on the fly to either http://
or https://
.
Rerun uses gRPC-based protocols under the hood, which means that the paths (/catalog
, /recording/12345
, ā¦) are mapped to gRPC services and methods on the fly.
š Python: blueprint overrides & defaults are now archetype based -python-blueprint-overrides--defaults-are-now-archetype-based
Just like with send_columns
in the previous release, blueprint overrides and defaults are now archetype based.
Examples:
Setting default & override for radius
Before:
rrb.Spatial2DView(
name="Rect 1",
origin="/",
contents=["/**"],
defaults=[rr.components.Radius(2)],
overrides={"rect/0": [rr.components.Radius(1)]},
)
After:
rrb.Spatial2DView(
name="Rect 1",
origin="/",
contents=["/**"],
defaults=[rr.Boxes2D.from_fields(radii=1)],
overrides={"rect/0": rr.Boxes2D.from_fields(radii=2)},
)
Setting up styles for a plot.
Before:
# ā¦
rrb.TimeSeriesView(
name="Trig",
origin="/trig",
overrides={
"/trig/sin": [rr.components.Color([255, 0, 0]), rr.components.Name("sin(0.01t)")],
"/trig/cos": [rr.components.Color([0, 255, 0]), rr.components.Name("cos(0.01t)")],
},
),
rrb.TimeSeriesView(
name="Classification",
origin="/classification",
overrides={
"classification/line": [rr.components.Color([255, 255, 0]), rr.components.StrokeWidth(3.0)],
"classification/samples": [rrb.VisualizerOverrides("SeriesPoints")], # This ensures that the `SeriesPoints` visualizers is used for this entity.
},
),
# ā¦
After:
# ā¦
rrb.TimeSeriesView(
name="Trig",
origin="/trig",
overrides={
"/trig/sin": rr.SeriesLines.from_fields(colors=[255, 0, 0], names="sin(0.01t)"),
"/trig/cos": rr.SeriesLines.from_fields(colors=[0, 255, 0], names="cos(0.01t)"),
},
),
rrb.TimeSeriesView(
name="Classification",
origin="/classification",
overrides={
"classification/line": rr.SeriesLines.from_fields(colors=[255, 255, 0], widths=3.0),
"classification/samples": rrb.VisualizerOverrides("SeriesPoints"), # This ensures that the `SeriesPoints` visualizers is used for this entity.
},
),
# ā¦
ā ļø Warning: Just like regular log/send calls, overlapping component types still overwrite each other. E.g. overriding a box radius will also override point radius on the same entity. In a future release, components tagged with a different archetype or field name can live side by side, but for the moment the Viewer is not able to make this distinction. For details see #6889.
Overriding Visible
and Interactive
is now always recursive overriding-visible-and-interactive-is-now-always-recursive
Previously, it was possible to override visibility individually, but not recursively. Also, Viewer interaction was hampered by this.
Overrides for these two properties are now always recursive, and can be applied using the new EntityBehavior
archetype.
Before:
rr.send_blueprint(
rrb.Spatial2DView(
overrides={"points": [rrb.components.Visible(False)]}
overrides={
"hidden_subtree": [rrb.components.Visible(False)],
"hidden_subtree/child0": [rrb.components.Visible(False)],
"hidden_subtree/child1": [rrb.components.Visible(False)],
# ā¦
"non_interactive_subtree": [rrb.components.Interactive(False)],
"non_interactive_subtree/child0": [rrb.components.Interactive(False)],
"non_interactive_subtree/child1": [rrb.components.Interactive(False)],
# ā¦
}
),
)
After:
rr.send_blueprint(
rrb.Spatial2DView(
overrides={
"hidden_subtree": rrb.EntityBehavior(visible=False),
"hidden_subtree/not_hidden": rrb.EntityBehavior(visible=True),
"non_interactive_subtree": rrb.EntityBehavior(interactive=False),
}
)
)
Visible time range overrides have to specify the underlying archetype visible-time-range-overrides-have-to-specify-the-underlying-archetype
(Note that this functionality broken in at least Rerun 0.21 and 0.22 but is fixed now. See #8557)
Before:
# ā¦
overrides={
"helix/structure/scaffolding/beads": [
rrb.VisibleTimeRange(
"stable_time",
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-0.3),
end=rrb.TimeRangeBoundary.cursor_relative(seconds=0.3),
),
],
},
# ā¦
After:
# ā¦
overrides={
"helix/structure/scaffolding/beads": rrb.VisibleTimeRanges(
timeline="stable_time",
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-0.3),
end=rrb.TimeRangeBoundary.cursor_relative(seconds=0.3)
),
}
# ā¦
⦠or respectively for multiple timelines:
# ā¦
overrides={
"helix/structure/scaffolding/beads": rrb.VisibleTimeRanges([
rrb.VisibleTimeRange(
timeline="stable_time",
start=rrb.TimeRangeBoundary.cursor_relative(seconds=-0.3),
end=rrb.TimeRangeBoundary.cursor_relative(seconds=0.3)
),
rrb.VisibleTimeRange(
timeline="index",
start=rrb.TimeRangeBoundary.absolute(seq=10),
end=rrb.TimeRangeBoundary.absolute(seq=100)
),
])
}
# ā¦
Types for time series plots are now plural types-for-time-series-plots-are-now-plural
The Scalar
/SeriesPoints
/SeriesLines
archetypess have been deprecated in favor of
Scalars
/SeriesPoints
/SeriesLines
since you can now have a multiple
scatter plots or lines on the same archetype.
Before:
rr.log("trig/sin", rr.SeriesLines(color=[s0, 255, 0], name="cos(0.01t)", width=4), static=True)
for t in range(int(tau * 2 * 100.0)):
rr.set_time("step", sequence=t)
rr.log("trig/sin", rr.Scalar(sin(float(t) / 100.0)))
After:
rr.log("trig/sin", rr.SeriesLines(colors=[255, 0, 0], names="sin(0.01t)", widths=2), static=True)
for t in range(int(tau * 2 * 100.0)):
rr.set_time("step", sequence=t)
rr.log("trig/sin", rr.Scalars(sin(float(t) / 100.0)))
The old types still work for the moment but will be removed in a future release.
Consistent constructor naming of Asset3D
across C++ and Rust consistent-constructor-naming-of-asset3d-across-c-and-rust
We've deprecated inconsistent constructors with following replacements:
- š¦ Rust:
from_file
->from_file_path
- š C++:
from_file
->from_file_path
from_bytes
->from_file_contents