Skip to content

Commit e0bc4ca

Browse files
committed
wip
1 parent 470cbd3 commit e0bc4ca

File tree

13 files changed

+294
-74
lines changed

13 files changed

+294
-74
lines changed

crates/store/re_chunk/src/chunk.rs

+1-17
Original file line numberDiff line numberDiff line change
@@ -284,23 +284,7 @@ impl Chunk {
284284
.collect();
285285
timelines == rhs_timelines
286286
}
287-
// TODO(cmc): we cannot compare tags yet, need to support Python & C++ first
288-
// && *components == rhs.components
289-
&& {
290-
let lhs_components_no_tags: ChunkComponents = components
291-
.clone()
292-
.into_iter_flattened()
293-
.map(|(descr, list_array)| (ComponentDescriptor::new(descr.component_name), list_array))
294-
.collect();
295-
let rhs_components_no_tags: ChunkComponents = rhs
296-
.components
297-
.clone()
298-
.into_iter_flattened()
299-
.map(|(descr, list_array)| (ComponentDescriptor::new(descr.component_name), list_array))
300-
.collect();
301-
302-
lhs_components_no_tags == rhs_components_no_tags
303-
}
287+
&& *components == rhs.components
304288
}
305289

306290
/// Check for equality while ignoring possible `Extension` type information

crates/top/rerun/src/commands/rrd/print.rs

+27-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::Context;
2-
use itertools::Itertools as _;
2+
use itertools::Itertools;
33

44
use re_log_types::{LogMsg, SetStoreInfo};
55
use re_sdk::log::Chunk;
@@ -15,8 +15,10 @@ pub struct PrintCommand {
1515
path_to_input_rrds: Vec<String>,
1616

1717
/// If set, print out table contents.
18-
#[clap(long, short, default_value_t = false)]
19-
verbose: bool,
18+
///
19+
/// This can be specified more than once to toggle more and more verbose levels (e.g. -vvv).
20+
#[clap(long, short, action = clap::ArgAction::Count)]
21+
verbose: u8,
2022

2123
/// If set, will try to proceed even in the face of IO and/or decoding errors in the input data.
2224
#[clap(long = "continue-on-error", default_value_t = true)]
@@ -63,7 +65,7 @@ impl PrintCommand {
6365
}
6466
}
6567

66-
fn print_msg(verbose: bool, msg: LogMsg) -> anyhow::Result<()> {
68+
fn print_msg(verbose: u8, msg: LogMsg) -> anyhow::Result<()> {
6769
match msg {
6870
LogMsg::SetStoreInfo(msg) => {
6971
let SetStoreInfo { row_id: _, info } = msg;
@@ -73,21 +75,31 @@ fn print_msg(verbose: bool, msg: LogMsg) -> anyhow::Result<()> {
7375
LogMsg::ArrowMsg(_row_id, arrow_msg) => {
7476
let chunk = Chunk::from_arrow_msg(&arrow_msg).context("skipped corrupt chunk")?;
7577

76-
if verbose {
77-
println!("{chunk}");
78-
} else {
78+
print!(
79+
"Chunk({}) with {} rows ({}) - {:?} - ",
80+
chunk.id(),
81+
chunk.num_rows(),
82+
re_format::format_bytes(chunk.total_size_bytes() as _),
83+
chunk.entity_path(),
84+
);
85+
86+
if verbose == 0 {
7987
let column_names = chunk
8088
.component_names()
8189
.map(|name| name.short_name())
8290
.join(" ");
83-
84-
println!(
85-
"Chunk({}) with {} rows ({}) - {:?} - columns: [{column_names}]",
86-
chunk.id(),
87-
chunk.num_rows(),
88-
re_format::format_bytes(chunk.total_size_bytes() as _),
89-
chunk.entity_path(),
90-
);
91+
println!("columns: [{column_names}]");
92+
} else if verbose == 1 {
93+
let column_descriptors = chunk
94+
.component_descriptors()
95+
.map(|descr| descr.short_name())
96+
.collect_vec()
97+
.join(" ");
98+
println!("columns: [{column_descriptors}]",);
99+
} else if verbose == 2 {
100+
println!("\n{}", chunk.emptied()); // headers only
101+
} else {
102+
println!("\n{chunk}");
91103
}
92104
}
93105

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import rerun as rr # pip install rerun-sdk
6+
7+
rr.init("rerun_example_descriptors_builtin_archetype_vanilla")
8+
rr.spawn()
9+
10+
rr.log("data", rr.Points3D([[1, 2, 3]], radii=[0.3, 0.2, 0.1]), static=True)
11+
12+
# The tags are indirectly checked by the Rust version (have a look over there for more info).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
import rerun as rr # pip install rerun-sdk
6+
7+
rr.init("rerun_example_descriptors_builtin_component_vanilla")
8+
rr.spawn()
9+
10+
rr.log_components("data", [rr.components.Position3DBatch([1, 2, 3])], static=True)
11+
12+
# The tags are indirectly checked by the Rust version (have a look over there for more info).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
import pyarrow as pa
8+
import rerun as rr # pip install rerun-sdk
9+
10+
11+
class CustomPosition3DBatch(rr.ComponentBatchLike):
12+
def __init__(self: Any, position: rr.components.Position3DBatch) -> None:
13+
self.position = position
14+
15+
def component_name(self) -> str:
16+
return "user.CustomPosition3D"
17+
18+
def as_arrow_array(self) -> pa.Array:
19+
return self.position.as_arrow_array()
20+
21+
22+
rr.init("rerun_example_descriptors_custom_component_vanilla")
23+
rr.spawn()
24+
25+
position = CustomPosition3DBatch(rr.components.Position3DBatch([1, 2, 3]))
26+
rr.log_components("data", [position], static=True)
27+
28+
# The tags are indirectly checked by the Rust version (have a look over there for more info).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python3
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
import pyarrow as pa
8+
import rerun as rr # pip install rerun-sdk
9+
10+
11+
class CustomPosition3DBatch(rr.ComponentBatchLike):
12+
def __init__(self: Any, position: rr.components.Position3DBatch) -> None:
13+
self.position = position
14+
15+
def component_name(self) -> str:
16+
return "user.CustomPosition3D"
17+
18+
def as_arrow_array(self) -> pa.Array:
19+
return self.position.as_arrow_array()
20+
21+
22+
rr.init("rerun_example_descriptors_custom_component_vanilla")
23+
rr.spawn()
24+
25+
position = CustomPosition3DBatch(rr.components.Position3DBatch([1, 2, 3]))
26+
rr.log_components("data", [position], static=True)
27+
28+
# The tags are indirectly checked by the Rust version (have a look over there for more info).

examples/cpp/dna/main.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ static constexpr size_t NUM_POINTS = 100;
1212

1313
int main() {
1414
const auto rec = rerun::RecordingStream("rerun_example_dna_abacus");
15-
rec.spawn().exit_on_failure();
15+
// rec.spawn().exit_on_failure();
16+
rec.save("/tmp/dna.rrd").exit_on_failure();
1617

1718
std::vector<rerun::Position3D> points1, points2;
1819
std::vector<rerun::Color> colors1, colors2;

rerun_py/rerun_sdk/rerun/_baseclasses.py

+99-1
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,98 @@
1212
T = TypeVar("T")
1313

1414

15+
# TODO: docs (copy from rust, probably)
16+
# TODO: the fact that this thing is nor interned and is recomputed/realloc'd for each
17+
# log call is pretty shit? then again I guess this is just as true for the arrow datatypes we
18+
# keep passing around... and the same is true for rust... (i think cpp caches all of this?)
19+
class ComponentDescriptor:
20+
def __init__(
21+
self,
22+
component_name: str,
23+
*,
24+
archetype_name: str | None = None,
25+
archetype_field_name: str | None = None,
26+
) -> None:
27+
self.archetype_name = archetype_name
28+
self.archetype_field_name = archetype_field_name
29+
self.component_name = component_name
30+
31+
def __str__(self) -> str:
32+
archetype_name = self.archetype_name
33+
archetype_field_name = self.archetype_field_name
34+
component_name = self.component_name
35+
36+
if archetype_name is not None and archetype_field_name is None:
37+
return f"{archetype_name}:{component_name}"
38+
elif archetype_name is None and archetype_field_name is not None:
39+
return f"{component_name}#{archetype_field_name}"
40+
elif archetype_name is not None and archetype_field_name is not None:
41+
return f"{archetype_name}:{component_name}#{archetype_field_name}"
42+
43+
return component_name
44+
45+
46+
# TODO: Protocol are duck typed
1547
class ComponentBatchLike(Protocol):
1648
"""Describes interface for objects that can be converted to batch of rerun Components."""
1749

50+
# TODO: uh... this needs to be the complete opposite
51+
# def component_name(self) -> str:
52+
# """
53+
# The fully-qualified name of this component batch, e.g. `rerun.components.Position2D`.
54+
#
55+
# This is a trivial but useful helper for `self.descriptor().component_name`.
56+
#
57+
# The default implementation already does the right thing. Do not override unless you know
58+
# what you're doing.
59+
# `Self::name()` must exactly match the value returned by `self.descriptor().component_name`,
60+
# or undefined behavior ensues.
61+
# """
62+
# return self.component_descriptor().component_name
63+
#
64+
# def component_descriptor(self) -> ComponentDescriptor:
65+
# """Returns the complete descriptor of the component."""
66+
# ...
67+
68+
# TODO: in python we probably dont need component_name at all anymore though?
69+
# TODO: for the static type checker...
1870
def component_name(self) -> str:
1971
"""Returns the name of the component."""
2072
...
2173

74+
def component_descriptor(self) -> ComponentDescriptor:
75+
"""Returns the complete descriptor of the component."""
76+
return ComponentDescriptor(self.component_name())
77+
2278
def as_arrow_array(self) -> pa.Array:
2379
"""Returns a `pyarrow.Array` of the component data."""
2480
...
2581

2682

83+
class DescribedComponentBatch(ComponentBatchLike):
84+
"""A `ComponentBatchLike` object with its associated `ComponentDescriptor`."""
85+
86+
# TODO: don't do this noob
87+
# _descriptor: ComponentDescriptor
88+
# _batch: ComponentBatchLike
89+
90+
def __init__(self, batch: ComponentBatchLike, descriptor: ComponentDescriptor):
91+
self._batch = batch
92+
self._descriptor = descriptor
93+
94+
def component_name(self) -> str:
95+
"""Returns the name of the component."""
96+
return self._batch.component_name()
97+
98+
def component_descriptor(self) -> ComponentDescriptor:
99+
"""Returns the complete descriptor of the component."""
100+
return self._descriptor
101+
102+
def as_arrow_array(self) -> pa.Array:
103+
"""Returns a `pyarrow.Array` of the component data."""
104+
return self._batch.as_arrow_array()
105+
106+
27107
class AsComponents(Protocol):
28108
"""
29109
Describes interface for interpreting an object as a bundle of Components.
@@ -43,6 +123,8 @@ def as_component_batches(self) -> Iterable[ComponentBatchLike]:
43123
...
44124

45125

126+
# TODO: same as `Archetype(rr.AsComponents)`
127+
# TODO:
46128
@define
47129
class Archetype:
48130
"""Base class for all archetypes."""
@@ -107,11 +189,17 @@ def as_component_batches(self) -> Iterable[ComponentBatchLike]:
107189
# TODO(#3381): Depending on what we decide
108190
# to do with optional components, we may need to make this instead call `_empty_pa_array`
109191
if comp is not None:
110-
yield comp
192+
descr = ComponentDescriptor(
193+
comp.component_name(),
194+
archetype_name=self.archetype_name(),
195+
archetype_field_name=fld.name,
196+
)
197+
yield DescribedComponentBatch(comp, descr)
111198

112199
__repr__ = __str__
113200

114201

202+
# TODO: basically that's the equivalent of Rust's Loggable right now
115203
class BaseBatch(Generic[T]):
116204
_ARROW_DATATYPE: pa.DataType | None = None
117205
"""The pyarrow type of this batch."""
@@ -268,6 +356,14 @@ def component_name(self) -> str:
268356
"""
269357
return self.component_batch.component_name()
270358

359+
def component_descriptor(self) -> ComponentDescriptor:
360+
"""
361+
Returns the complete descriptor of the component.
362+
363+
Part of the `ComponentBatchLike` logging interface.
364+
"""
365+
return self.component_batch.component_descriptor()
366+
271367
def as_arrow_array(self) -> pa.Array:
272368
"""
273369
The component as an arrow batch.
@@ -279,6 +375,7 @@ def as_arrow_array(self) -> pa.Array:
279375
return pa.ListArray.from_arrays(offsets, array)
280376

281377

378+
# TODO: okay what about this? does this need a descriptor?
282379
class ComponentBatchMixin(ComponentBatchLike):
283380
def component_name(self) -> str:
284381
"""
@@ -308,6 +405,7 @@ def partition(self, lengths: npt.ArrayLike) -> ComponentColumn:
308405
return ComponentColumn(self, lengths)
309406

310407

408+
# TODO: okay what about this? does this need a descriptor????????????
311409
class ComponentMixin(ComponentBatchLike):
312410
"""
313411
Makes components adhere to the ComponentBatchLike interface.

0 commit comments

Comments
 (0)