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

Python backport: add log_any() #2581

Merged
merged 15 commits into from
Jul 3, 2023
2 changes: 1 addition & 1 deletion crates/re_types/source_hash.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This is a sha256 hash for all direct and indirect dependencies of this crate's build script.
# It can be safely removed at anytime to force the build script to run again.
# Check out build.rs to see how it's computed.
128cd542f3f9e6cf3b2a44aeb022cd5f3ad819b00ce5371eeb311300f3e9a7f1
b7927e46605c38a4659f7c90a026f6ce5903e84428414549725107993a4af38a
66 changes: 52 additions & 14 deletions crates/re_types_builder/src/codegen/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl CodeGenerator for PythonCodeGenerator {
datatypes_path,
arrow_registry,
objs,
ObjectKind::Datatype,
&objs.ordered_objects(ObjectKind::Datatype.into()),
)
.0,
Expand All @@ -55,6 +56,7 @@ impl CodeGenerator for PythonCodeGenerator {
components_path,
arrow_registry,
objs,
ObjectKind::Component,
&objs.ordered_objects(ObjectKind::Component.into()),
)
.0,
Expand All @@ -68,6 +70,7 @@ impl CodeGenerator for PythonCodeGenerator {
archetypes_path,
arrow_registry,
objs,
ObjectKind::Archetype,
&objs.ordered_objects(ObjectKind::Archetype.into()),
);
filepaths.extend(paths);
Expand Down Expand Up @@ -117,6 +120,7 @@ fn quote_objects(
out_path: impl AsRef<Path>,
arrow_registry: &ArrowRegistry,
all_objects: &Objects,
kind: ObjectKind,
objs: &[&Object],
) -> (Vec<PathBuf>, Vec<String>) {
let out_path = out_path.as_ref();
Expand Down Expand Up @@ -176,6 +180,11 @@ fn quote_objects(
code.push_text(&format!("# {AUTOGEN_WARNING}"), 2, 0);

let manifest = quote_manifest(names);
let base_include = match kind {
ObjectKind::Archetype => "from ._base import Archetype",
ObjectKind::Component => "from ._base import Component",
ObjectKind::Datatype => "",
};
code.push_unindented_text(
format!(
"
Expand All @@ -185,9 +194,11 @@ fn quote_objects(
import numpy.typing as npt
import pyarrow as pa

from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Any, Dict, Iterable, Optional, Sequence, Set, Tuple, Union

{base_include}

__all__ = [{manifest}]

",
Expand Down Expand Up @@ -220,14 +231,21 @@ fn quote_objects(

let manifest = quote_manifest(mods.iter().flat_map(|(_, names)| names.iter()));

let (base_manifest, base_include) = match kind {
ObjectKind::Archetype => ("\"Archetype\", ", "from ._base import Archetype\n"),
ObjectKind::Component => ("\"Component\", ", "from ._base import Component\n"),
ObjectKind::Datatype => ("", ""),
};

code.push_text(&format!("# {AUTOGEN_WARNING}"), 2, 0);
code.push_unindented_text(
format!(
"
from __future__ import annotations

__all__ = [{manifest}]
__all__ = [{base_manifest}{manifest}]

{base_include}
",
),
0,
Expand Down Expand Up @@ -275,14 +293,18 @@ impl QuotedObject {

let mut code = String::new();

let superclass = match *kind {
ObjectKind::Archetype => "(Archetype)",
ObjectKind::Component | ObjectKind::Datatype => "",
};
code.push_unindented_text(
format!(
r#"

## --- {name} --- ##

@dataclass
class {name}:
class {name}{superclass}:
"#
),
0,
Expand Down Expand Up @@ -317,10 +339,18 @@ impl QuotedObject {
} else {
typ
};
let typ = if *is_nullable {
format!("{typ} | None = None")
} else {
let typ = if *kind == ObjectKind::Archetype {
if !*is_nullable {
format!("{typ} = field(metadata={{'component': 'primary'}})")
} else {
format!(
"{typ} | None = field(default=None, metadata={{'component': 'secondary'}})"
)
}
} else if !*is_nullable {
typ
} else {
format!("{typ} | None = None")
};

code.push_text(format!("{name}: {typ}"), 1, 4);
Expand Down Expand Up @@ -457,13 +487,13 @@ fn quote_str_repr_from_obj(obj: &Object) -> String {
s = f"rr.{type(self).__name__}(\n"

from dataclasses import fields
for field in fields(self):
data = getattr(self, field.name)
datatype = getattr(data, "type", None)
if datatype:
name = datatype.extension_name
typ = datatype.storage_type
s += f" {name}<{typ}>(\n {data.to_pylist()}\n )\n"
for fld in fields(self):
if "component" in fld.metadata:
comp: components.Component = getattr(self, fld.name)
if datatype := getattr(comp, "type"):
name = comp.extension_name
typ = datatype.storage_type
s += f" {name}<{typ}>(\n {comp.to_pylist()}\n )\n"

s += ")"

Expand Down Expand Up @@ -754,6 +784,12 @@ fn quote_arrow_support_from_obj(arrow_registry: &ArrowRegistry, obj: &Object) ->
.try_get_attr::<String>(ATTR_RERUN_LEGACY_FQNAME)
.unwrap_or_else(|| fqname.clone());

let superclass = if kind == &ObjectKind::Component {
"Component, "
} else {
""
};

unindent::unindent(&format!(
r#"

Expand Down Expand Up @@ -784,7 +820,9 @@ fn quote_arrow_support_from_obj(arrow_registry: &ArrowRegistry, obj: &Object) ->
# TODO(cmc): bring back registration to pyarrow once legacy types are gone
# pa.register_extension_type({arrow}())

class {many}(pa.ExtensionArray, {many}Ext): # type: ignore[misc]
class {many}({superclass}{many}Ext): # type: ignore[misc]
_extension_name = "{legacy_fqname}"

@staticmethod
def from_similar(data: {many_aliases} | None) -> pa.Array:
if data is None:
Expand Down
20 changes: 11 additions & 9 deletions examples/python/api_demo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,20 @@ def run_segmentation() -> None:
rr.log_segmentation_image("seg_demo/img", segmentation_img)

# Log a bunch of classified 2D points
rr.log_point("seg_demo/single_point", np.array([64, 64]), class_id=13)
rr.log_point("seg_demo/single_point_labeled", np.array([90, 50]), class_id=13, label="labeled point")
rr.log_points("seg_demo/several_points0", np.array([[20, 50], [100, 70], [60, 30]]), class_ids=42)
rr.log_points(
# Note: this uses the new, WIP object-oriented API
rr.log_any("seg_demo/single_point", rr.Points2D(np.array([64, 64]), class_ids=13))
rr.log_any("seg_demo/single_point_labeled", rr.Points2D(np.array([90, 50]), class_ids=13, labels="labeled point"))
rr.log_any("seg_demo/several_points0", rr.Points2D(np.array([[20, 50], [100, 70], [60, 30]]), class_ids=42))
rr.log_any(
"seg_demo/several_points1",
np.array([[40, 50], [120, 70], [80, 30]]),
class_ids=np.array([13, 42, 99], dtype=np.uint8),
rr.Points2D(np.array([[40, 50], [120, 70], [80, 30]]), class_ids=np.array([13, 42, 99], dtype=np.uint8)),
)
rr.log_points(
rr.log_any(
"seg_demo/many points",
np.array([[100 + (int(i / 5)) * 2, 100 + (i % 5) * 2] for i in range(25)]),
class_ids=np.array([42], dtype=np.uint8),
rr.Points2D(
np.array([[100 + (int(i / 5)) * 2, 100 + (i % 5) * 2] for i in range(25)]),
class_ids=np.array([42], dtype=np.uint8),
),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this means that this example only runs with _ENABLE_NEXT_GEN_API now right?

How about we do something like:

if _ENABLE_NEXT_GEN_API:
  # do new stuff
else:
  # do old stuff

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I'll make that variable public then (remove the leading _).

)

rr.log_text_entry("logs/seg_demo_log", "default colored rects, default colored points, a single point has a label")
Expand Down
1 change: 1 addition & 0 deletions rerun_py/rerun_sdk/rerun/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
_ENABLE_NEXT_GEN_API = True
if _ENABLE_NEXT_GEN_API:
from ._rerun2.archetypes import *
from ._rerun2.log_any import log_any


def _init_recording_stream() -> None:
Expand Down
3 changes: 2 additions & 1 deletion rerun_py/rerun_sdk/rerun/_rerun2/archetypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from __future__ import annotations

__all__ = ["AffixFuzzer1", "Points2D"]
__all__ = ["Archetype", "AffixFuzzer1", "Points2D"]

from ._base import Archetype
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of having the abstract super class (or whatever it's called) be defined in this package with the rest of the actual archetypes...
In particular I can very easily imagine a situation where someone wants an archetype called Archetype 😬

Can we put those abstract classes at the very top maybe? _rerun2.Archetype, _rerun2.Component, etc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving them all to _rerun2/_baseclasses.py.

Note that in the next PR:

  • there will be many more such base classes
  • most of them will have longer names...
  • ...except Archetype, which is user-facing (that's what log_any() accepts)

from .fuzzy import AffixFuzzer1
from .points2d import Points2D
8 changes: 8 additions & 0 deletions rerun_py/rerun_sdk/rerun/_rerun2/archetypes/_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

from dataclasses import dataclass


@dataclass
class Archetype:
pass
76 changes: 39 additions & 37 deletions rerun_py/rerun_sdk/rerun/_rerun2/archetypes/fuzzy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from __future__ import annotations

from dataclasses import dataclass
from dataclasses import dataclass, field

from ._base import Archetype

__all__ = ["AffixFuzzer1"]

Expand All @@ -12,48 +14,48 @@


@dataclass
class AffixFuzzer1:
fuzz1001: components.AffixFuzzer1Array
fuzz1002: components.AffixFuzzer2Array
fuzz1003: components.AffixFuzzer3Array
fuzz1004: components.AffixFuzzer4Array
fuzz1005: components.AffixFuzzer5Array
fuzz1006: components.AffixFuzzer6Array
fuzz1007: components.AffixFuzzer7Array
fuzz1101: components.AffixFuzzer1Array
fuzz1102: components.AffixFuzzer2Array
fuzz1103: components.AffixFuzzer3Array
fuzz1104: components.AffixFuzzer4Array
fuzz1105: components.AffixFuzzer5Array
fuzz1106: components.AffixFuzzer6Array
fuzz1107: components.AffixFuzzer7Array
fuzz2001: components.AffixFuzzer1Array | None = None
fuzz2002: components.AffixFuzzer2Array | None = None
fuzz2003: components.AffixFuzzer3Array | None = None
fuzz2004: components.AffixFuzzer4Array | None = None
fuzz2005: components.AffixFuzzer5Array | None = None
fuzz2006: components.AffixFuzzer6Array | None = None
fuzz2007: components.AffixFuzzer7Array | None = None
fuzz2101: components.AffixFuzzer1Array | None = None
fuzz2102: components.AffixFuzzer2Array | None = None
fuzz2103: components.AffixFuzzer3Array | None = None
fuzz2104: components.AffixFuzzer4Array | None = None
fuzz2105: components.AffixFuzzer5Array | None = None
fuzz2106: components.AffixFuzzer6Array | None = None
fuzz2107: components.AffixFuzzer7Array | None = None
class AffixFuzzer1(Archetype):
fuzz1001: components.AffixFuzzer1Array = field(metadata={"component": "primary"})
fuzz1002: components.AffixFuzzer2Array = field(metadata={"component": "primary"})
fuzz1003: components.AffixFuzzer3Array = field(metadata={"component": "primary"})
fuzz1004: components.AffixFuzzer4Array = field(metadata={"component": "primary"})
fuzz1005: components.AffixFuzzer5Array = field(metadata={"component": "primary"})
fuzz1006: components.AffixFuzzer6Array = field(metadata={"component": "primary"})
fuzz1007: components.AffixFuzzer7Array = field(metadata={"component": "primary"})
fuzz1101: components.AffixFuzzer1Array = field(metadata={"component": "primary"})
fuzz1102: components.AffixFuzzer2Array = field(metadata={"component": "primary"})
fuzz1103: components.AffixFuzzer3Array = field(metadata={"component": "primary"})
fuzz1104: components.AffixFuzzer4Array = field(metadata={"component": "primary"})
fuzz1105: components.AffixFuzzer5Array = field(metadata={"component": "primary"})
fuzz1106: components.AffixFuzzer6Array = field(metadata={"component": "primary"})
fuzz1107: components.AffixFuzzer7Array = field(metadata={"component": "primary"})
fuzz2001: components.AffixFuzzer1Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2002: components.AffixFuzzer2Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2003: components.AffixFuzzer3Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2004: components.AffixFuzzer4Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2005: components.AffixFuzzer5Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2006: components.AffixFuzzer6Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2007: components.AffixFuzzer7Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2101: components.AffixFuzzer1Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2102: components.AffixFuzzer2Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2103: components.AffixFuzzer3Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2104: components.AffixFuzzer4Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2105: components.AffixFuzzer5Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2106: components.AffixFuzzer6Array | None = field(default=None, metadata={"component": "secondary"})
fuzz2107: components.AffixFuzzer7Array | None = field(default=None, metadata={"component": "secondary"})

def __str__(self) -> str:
s = f"rr.{type(self).__name__}(\n"

from dataclasses import fields

for field in fields(self):
data = getattr(self, field.name)
datatype = getattr(data, "type", None)
if datatype:
name = datatype.extension_name
typ = datatype.storage_type
s += f" {name}<{typ}>(\n {data.to_pylist()}\n )\n"
for fld in fields(self):
if "component" in fld.metadata:
comp: components.Component = getattr(self, fld.name)
if datatype := getattr(comp, "type"):
name = comp.extension_name
typ = datatype.storage_type
s += f" {name}<{typ}>(\n {comp.to_pylist()}\n )\n"

s += ")"

Expand Down
Loading