From 15511fa688c47db749338c6d86950137555ac532 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 14 May 2024 14:17:13 +0200 Subject: [PATCH] Better handle redundant time ranges (#6306) ### What * Fixes #6276 * Better doc visibility * Warn if a timeline is specified twice Collateral: * generate `__hash__` methods for trivial python classes * fix issue with `pixi run py-test` ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6306?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/6306?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! - [PR Build Summary](https://build.rerun.io/pr/6306) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. --- .../archetypes/visible_time_ranges.fbs | 2 +- .../rerun/blueprint/views/spatial2d.fbs | 4 +- .../rerun/blueprint/views/spatial3d.fbs | 3 ++ .../rerun/blueprint/views/time_series.fbs | 3 ++ .../archetypes/visible_time_ranges.rs | 2 +- .../src/blueprint/views/spatial2d_view.rs | 3 ++ .../src/blueprint/views/spatial3d_view.rs | 3 ++ .../src/blueprint/views/time_series_view.rs | 3 ++ .../src/codegen/python/mod.rs | 5 ++- pixi.toml | 7 +++- .../archetypes/visible_time_ranges.hpp | 2 +- .../archetypes/visible_time_ranges.py | 28 ++----------- .../archetypes/visible_time_ranges_ext.py | 40 +++++++++++++++++++ .../blueprint/components/column_share.py | 3 ++ .../blueprint/components/grid_columns.py | 3 ++ .../rerun/blueprint/components/row_share.py | 3 ++ .../rerun/blueprint/views/spatial2d_view.py | 3 ++ .../rerun/blueprint/views/spatial3d_view.py | 3 ++ .../rerun/blueprint/views/time_series_view.py | 3 ++ .../rerun_sdk/rerun/components/depth_meter.py | 3 ++ .../rerun_sdk/rerun/components/draw_order.py | 3 ++ .../rerun_sdk/rerun/components/marker_size.py | 3 ++ rerun_py/rerun_sdk/rerun/components/radius.py | 3 ++ rerun_py/rerun_sdk/rerun/components/scalar.py | 3 ++ .../rerun/components/stroke_width.py | 3 ++ .../rerun_sdk/rerun/datatypes/class_id.py | 3 ++ .../rerun_sdk/rerun/datatypes/entity_path.py | 3 ++ rerun_py/rerun_sdk/rerun/datatypes/float32.py | 3 ++ .../rerun_sdk/rerun/datatypes/keypoint_id.py | 3 ++ rerun_py/rerun_sdk/rerun/datatypes/rgba32.py | 3 ++ .../rerun_sdk/rerun/datatypes/time_int.py | 3 ++ rerun_py/rerun_sdk/rerun/datatypes/uint32.py | 3 ++ rerun_py/rerun_sdk/rerun/datatypes/uint64.py | 3 ++ rerun_py/rerun_sdk/rerun/datatypes/utf8.py | 3 ++ .../test_types/components/affix_fuzzer9.py | 3 ++ .../test_types/datatypes/flattened_scalar.py | 3 ++ .../datatypes/primitive_component.py | 3 ++ .../test_types/datatypes/string_component.py | 3 ++ .../tests/unit/test_visible_time_ranges.py | 25 ++++++++++++ 39 files changed, 174 insertions(+), 31 deletions(-) create mode 100644 rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges_ext.py create mode 100644 rerun_py/tests/unit/test_visible_time_ranges.py diff --git a/crates/re_types/definitions/rerun/blueprint/archetypes/visible_time_ranges.fbs b/crates/re_types/definitions/rerun/blueprint/archetypes/visible_time_ranges.fbs index 3e2ccda7e362..8f8b44cdd6d6 100644 --- a/crates/re_types/definitions/rerun/blueprint/archetypes/visible_time_ranges.fbs +++ b/crates/re_types/definitions/rerun/blueprint/archetypes/visible_time_ranges.fbs @@ -25,6 +25,6 @@ table VisibleTimeRanges ( ) { /// The time ranges to show for each timeline unless specified otherwise on a per-entity basis. /// - /// If a timeline is listed twice, the first entry will be used. + /// If a timeline is specified more than once, the first entry will be used. ranges: [rerun.blueprint.components.VisibleTimeRange] ("attr.rerun.component_required", order: 1000); } diff --git a/crates/re_types/definitions/rerun/blueprint/views/spatial2d.fbs b/crates/re_types/definitions/rerun/blueprint/views/spatial2d.fbs index 9f0aaf7da479..f97c10adf362 100644 --- a/crates/re_types/definitions/rerun/blueprint/views/spatial2d.fbs +++ b/crates/re_types/definitions/rerun/blueprint/views/spatial2d.fbs @@ -16,6 +16,8 @@ table Spatial2DView ( visual_bounds: rerun.blueprint.archetypes.VisualBounds (order: 2000); /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the latest state of each component. + /// If a timeline is specified more than once, the first entry will be used. time_ranges: rerun.blueprint.archetypes.VisibleTimeRanges (order: 10000); - } diff --git a/crates/re_types/definitions/rerun/blueprint/views/spatial3d.fbs b/crates/re_types/definitions/rerun/blueprint/views/spatial3d.fbs index fa970c842861..cee0f9a7002c 100644 --- a/crates/re_types/definitions/rerun/blueprint/views/spatial3d.fbs +++ b/crates/re_types/definitions/rerun/blueprint/views/spatial3d.fbs @@ -12,5 +12,8 @@ table Spatial3DView ( background: rerun.blueprint.archetypes.Background (order: 1000); /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the latest state of each component. + /// If a timeline is specified more than once, the first entry will be used. time_ranges: rerun.blueprint.archetypes.VisibleTimeRanges (order: 10000); } diff --git a/crates/re_types/definitions/rerun/blueprint/views/time_series.fbs b/crates/re_types/definitions/rerun/blueprint/views/time_series.fbs index 203673adc449..aa9d1764ad37 100644 --- a/crates/re_types/definitions/rerun/blueprint/views/time_series.fbs +++ b/crates/re_types/definitions/rerun/blueprint/views/time_series.fbs @@ -15,5 +15,8 @@ table TimeSeriesView ( plot_legend: rerun.blueprint.archetypes.PlotLegend (order: 2000); /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the entire timeline. + /// If a timeline is specified more than once, the first entry will be used. time_ranges: rerun.blueprint.archetypes.VisibleTimeRanges (order: 10000); } diff --git a/crates/re_types/src/blueprint/archetypes/visible_time_ranges.rs b/crates/re_types/src/blueprint/archetypes/visible_time_ranges.rs index 187837c439cf..014d2a0c694a 100644 --- a/crates/re_types/src/blueprint/archetypes/visible_time_ranges.rs +++ b/crates/re_types/src/blueprint/archetypes/visible_time_ranges.rs @@ -35,7 +35,7 @@ use ::re_types_core::{DeserializationError, DeserializationResult}; pub struct VisibleTimeRanges { /// The time ranges to show for each timeline unless specified otherwise on a per-entity basis. /// - /// If a timeline is listed twice, the first entry will be used. + /// If a timeline is specified more than once, the first entry will be used. pub ranges: Vec, } diff --git a/crates/re_types/src/blueprint/views/spatial2d_view.rs b/crates/re_types/src/blueprint/views/spatial2d_view.rs index 2f93f350af83..3d7be20b0cc2 100644 --- a/crates/re_types/src/blueprint/views/spatial2d_view.rs +++ b/crates/re_types/src/blueprint/views/spatial2d_view.rs @@ -35,6 +35,9 @@ pub struct Spatial2DView { pub visual_bounds: crate::blueprint::archetypes::VisualBounds, /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the latest state of each component. + /// If a timeline is specified more than once, the first entry will be used. pub time_ranges: crate::blueprint::archetypes::VisibleTimeRanges, } diff --git a/crates/re_types/src/blueprint/views/spatial3d_view.rs b/crates/re_types/src/blueprint/views/spatial3d_view.rs index 61cf8d48d837..cbc039e986b6 100644 --- a/crates/re_types/src/blueprint/views/spatial3d_view.rs +++ b/crates/re_types/src/blueprint/views/spatial3d_view.rs @@ -29,6 +29,9 @@ pub struct Spatial3DView { pub background: crate::blueprint::archetypes::Background, /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the latest state of each component. + /// If a timeline is specified more than once, the first entry will be used. pub time_ranges: crate::blueprint::archetypes::VisibleTimeRanges, } diff --git a/crates/re_types/src/blueprint/views/time_series_view.rs b/crates/re_types/src/blueprint/views/time_series_view.rs index 7336a160217c..f78fae734835 100644 --- a/crates/re_types/src/blueprint/views/time_series_view.rs +++ b/crates/re_types/src/blueprint/views/time_series_view.rs @@ -32,6 +32,9 @@ pub struct TimeSeriesView { pub plot_legend: crate::blueprint::archetypes::PlotLegend, /// Configures which range on each timeline is shown by this view (unless specified differently per entity). + /// + /// If not specified, the default is to show the entire timeline. + /// If a timeline is specified more than once, the first entry will be used. pub time_ranges: crate::blueprint::archetypes::VisibleTimeRanges, } diff --git a/crates/re_types_builder/src/codegen/python/mod.rs b/crates/re_types_builder/src/codegen/python/mod.rs index 82b674e6e563..190ece9aec48 100644 --- a/crates/re_types_builder/src/codegen/python/mod.rs +++ b/crates/re_types_builder/src/codegen/python/mod.rs @@ -1283,7 +1283,7 @@ fn quote_array_method_from_obj( )) } -/// Automatically implement `__str__`, `__int__`, or `__float__` method if the object has a single +/// Automatically implement `__str__`, `__int__`, or `__float__` as well as `__hash__` methods if the object has a single /// field of the corresponding type that is not optional. /// /// Only applies to datatypes and components. @@ -1309,6 +1309,9 @@ fn quote_native_types_method_from_obj(objects: &Objects, obj: &Object) -> String " def __{typ}__(self) -> {typ}: return {typ}(self.{field_name}) + + def __hash__(self) -> int: + return hash(self.{field_name}) ", )) } diff --git a/pixi.toml b/pixi.toml index f3985a05701d..75f1a2199746 100644 --- a/pixi.toml +++ b/pixi.toml @@ -265,13 +265,16 @@ pip = ">=23" # around that and executes `rerun` from the system shell within the context of the pixi environment. rerun-from-path = "rerun" +# Duplicate of `py-build` but within the `wheel-test` environment. +py-build-wheel-test = { cmd = "pixi run -e wheel-test py-build" } + # Run the Python tests. # Don't call this on CI - use `nox` to run tests on all supported Python versions instead. py-test = { cmd = "python -m pytest -vv rerun_py/tests/unit", depends_on = [ - "py-build", + "py-build-wheel-test", ] } py-bench = { cmd = "python -m pytest -c rerun_py/pyproject.toml --benchmark-only", depends_on = [ - "py-build", + "py-build-wheel-test", ] } [feature.base.dependencies] diff --git a/rerun_cpp/src/rerun/blueprint/archetypes/visible_time_ranges.hpp b/rerun_cpp/src/rerun/blueprint/archetypes/visible_time_ranges.hpp index 749019bc82c8..77dd554ad9f5 100644 --- a/rerun_cpp/src/rerun/blueprint/archetypes/visible_time_ranges.hpp +++ b/rerun_cpp/src/rerun/blueprint/archetypes/visible_time_ranges.hpp @@ -26,7 +26,7 @@ namespace rerun::blueprint::archetypes { struct VisibleTimeRanges { /// The time ranges to show for each timeline unless specified otherwise on a per-entity basis. /// - /// If a timeline is listed twice, the first entry will be used. + /// If a timeline is specified more than once, the first entry will be used. Collection ranges; public: diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges.py index 1c874cf167e3..8e9192bdee8f 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges.py @@ -5,20 +5,17 @@ from __future__ import annotations -from typing import Any - from attrs import define, field -from ... import datatypes from ..._baseclasses import Archetype from ...blueprint import components as blueprint_components -from ...error_utils import catch_and_log_exceptions +from .visible_time_ranges_ext import VisibleTimeRangesExt __all__ = ["VisibleTimeRanges"] @define(str=False, repr=False, init=False) -class VisibleTimeRanges(Archetype): +class VisibleTimeRanges(VisibleTimeRangesExt, Archetype): """ **Archetype**: Configures what range of each timeline is shown on a view. @@ -31,24 +28,7 @@ class VisibleTimeRanges(Archetype): - For any other view, the default is to apply latest-at semantics. """ - def __init__(self: Any, ranges: datatypes.VisibleTimeRangeArrayLike): - """ - Create a new instance of the VisibleTimeRanges archetype. - - Parameters - ---------- - ranges: - The time ranges to show for each timeline unless specified otherwise on a per-entity basis. - - If a timeline is listed twice, the first entry will be used. - - """ - - # You can define your own __init__ function as a member of VisibleTimeRangesExt in visible_time_ranges_ext.py - with catch_and_log_exceptions(context=self.__class__.__name__): - self.__attrs_init__(ranges=ranges) - return - self.__attrs_clear__() + # __init__ can be found in visible_time_ranges_ext.py def __attrs_clear__(self) -> None: """Convenience method for calling `__attrs_init__` with all `None`s.""" @@ -69,7 +49,7 @@ def _clear(cls) -> VisibleTimeRanges: ) # The time ranges to show for each timeline unless specified otherwise on a per-entity basis. # - # If a timeline is listed twice, the first entry will be used. + # If a timeline is specified more than once, the first entry will be used. # # (Docstring intentionally commented out to hide this field from the docs) diff --git a/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges_ext.py b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges_ext.py new file mode 100644 index 000000000000..8c6ec1d9eef9 --- /dev/null +++ b/rerun_py/rerun_sdk/rerun/blueprint/archetypes/visible_time_ranges_ext.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import Any + +from ... import datatypes +from ...error_utils import _send_warning_or_raise, catch_and_log_exceptions + + +class VisibleTimeRangesExt: + """Extension for [VisibleTimeRanges][rerun.blueprint.archetypes.VisibleTimeRanges].""" + + def __init__(self: Any, ranges: datatypes.VisibleTimeRangeArrayLike): + """ + Create a new instance of the VisibleTimeRanges archetype. + + Parameters + ---------- + ranges: + The time ranges to show for each timeline unless specified otherwise on a per-entity basis. + + If a timeline is listed twice, a warning will be issued and the first entry will be used. + + """ + + if isinstance(ranges, datatypes.VisibleTimeRange): + ranges = [ranges] + + timelines = set() + for range in ranges: + if range.timeline in timelines: + _send_warning_or_raise( + f"Warning: Timeline {range.timeline} is listed twice in the list of visible time ranges. Only the first entry will be used.", + 1, + ) + timelines.add(range.timeline) + + with catch_and_log_exceptions(context=self.__class__.__name__): + self.__attrs_init__(ranges=ranges) + return + self.__attrs_clear__() diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/column_share.py b/rerun_py/rerun_sdk/rerun/blueprint/components/column_share.py index abd28beb0695..18a92c8f146f 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/column_share.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/column_share.py @@ -47,6 +47,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.share) + def __hash__(self) -> int: + return hash(self.share) + if TYPE_CHECKING: ColumnShareLike = Union[ColumnShare, float] diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/grid_columns.py b/rerun_py/rerun_sdk/rerun/blueprint/components/grid_columns.py index 31827791bf29..b5f625d155d6 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/grid_columns.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/grid_columns.py @@ -47,6 +47,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.columns) + def __hash__(self) -> int: + return hash(self.columns) + if TYPE_CHECKING: GridColumnsLike = Union[GridColumns, int] diff --git a/rerun_py/rerun_sdk/rerun/blueprint/components/row_share.py b/rerun_py/rerun_sdk/rerun/blueprint/components/row_share.py index 387cdcab7af2..8d5d3ba48156 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/components/row_share.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/components/row_share.py @@ -47,6 +47,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.share) + def __hash__(self) -> int: + return hash(self.share) + if TYPE_CHECKING: RowShareLike = Union[RowShare, float] diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/spatial2d_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/spatial2d_view.py index 89f855b6e2d0..6dd12b72f13a 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/views/spatial2d_view.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/views/spatial2d_view.py @@ -64,6 +64,9 @@ def __init__( time_ranges: Configures which range on each timeline is shown by this view (unless specified differently per entity). + If not specified, the default is to show the latest state of each component. + If a timeline is specified more than once, the first entry will be used. + """ properties: dict[str, AsComponents] = {} diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/spatial3d_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/spatial3d_view.py index 9aaa517bd7e8..c0072c86e391 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/views/spatial3d_view.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/views/spatial3d_view.py @@ -90,6 +90,9 @@ def __init__( time_ranges: Configures which range on each timeline is shown by this view (unless specified differently per entity). + If not specified, the default is to show the latest state of each component. + If a timeline is specified more than once, the first entry will be used. + """ properties: dict[str, AsComponents] = {} diff --git a/rerun_py/rerun_sdk/rerun/blueprint/views/time_series_view.py b/rerun_py/rerun_sdk/rerun/blueprint/views/time_series_view.py index 241a4d07d66d..2d011007a484 100644 --- a/rerun_py/rerun_sdk/rerun/blueprint/views/time_series_view.py +++ b/rerun_py/rerun_sdk/rerun/blueprint/views/time_series_view.py @@ -112,6 +112,9 @@ def __init__( time_ranges: Configures which range on each timeline is shown by this view (unless specified differently per entity). + If not specified, the default is to show the entire timeline. + If a timeline is specified more than once, the first entry will be used. + """ properties: dict[str, AsComponents] = {} diff --git a/rerun_py/rerun_sdk/rerun/components/depth_meter.py b/rerun_py/rerun_sdk/rerun/components/depth_meter.py index 34bd50f3f9ee..2e6fc9c0e976 100644 --- a/rerun_py/rerun_sdk/rerun/components/depth_meter.py +++ b/rerun_py/rerun_sdk/rerun/components/depth_meter.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: DepthMeterLike = Union[DepthMeter, float] diff --git a/rerun_py/rerun_sdk/rerun/components/draw_order.py b/rerun_py/rerun_sdk/rerun/components/draw_order.py index a4e6ae18afe9..9f822386af0f 100644 --- a/rerun_py/rerun_sdk/rerun/components/draw_order.py +++ b/rerun_py/rerun_sdk/rerun/components/draw_order.py @@ -44,6 +44,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: DrawOrderLike = Union[DrawOrder, float] diff --git a/rerun_py/rerun_sdk/rerun/components/marker_size.py b/rerun_py/rerun_sdk/rerun/components/marker_size.py index b11bd77bed18..acddeed68fd1 100644 --- a/rerun_py/rerun_sdk/rerun/components/marker_size.py +++ b/rerun_py/rerun_sdk/rerun/components/marker_size.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: MarkerSizeLike = Union[MarkerSize, float] diff --git a/rerun_py/rerun_sdk/rerun/components/radius.py b/rerun_py/rerun_sdk/rerun/components/radius.py index 69f66771094c..b66643d0eb56 100644 --- a/rerun_py/rerun_sdk/rerun/components/radius.py +++ b/rerun_py/rerun_sdk/rerun/components/radius.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: RadiusLike = Union[Radius, float] diff --git a/rerun_py/rerun_sdk/rerun/components/scalar.py b/rerun_py/rerun_sdk/rerun/components/scalar.py index 397fed9b30c6..fd5c83d1ecdd 100644 --- a/rerun_py/rerun_sdk/rerun/components/scalar.py +++ b/rerun_py/rerun_sdk/rerun/components/scalar.py @@ -40,6 +40,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: ScalarLike = Union[Scalar, float] diff --git a/rerun_py/rerun_sdk/rerun/components/stroke_width.py b/rerun_py/rerun_sdk/rerun/components/stroke_width.py index cf112f59fc19..ade3c93cc6c9 100644 --- a/rerun_py/rerun_sdk/rerun/components/stroke_width.py +++ b/rerun_py/rerun_sdk/rerun/components/stroke_width.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.width) + def __hash__(self) -> int: + return hash(self.width) + if TYPE_CHECKING: StrokeWidthLike = Union[StrokeWidth, float] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/class_id.py b/rerun_py/rerun_sdk/rerun/datatypes/class_id.py index a6fd233bde10..ee1500192c4d 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/class_id.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/class_id.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.id) + def __hash__(self) -> int: + return hash(self.id) + if TYPE_CHECKING: ClassIdLike = Union[ClassId, int] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py b/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py index 2e816c1f0764..f30dba61d2cf 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/entity_path.py @@ -30,6 +30,9 @@ def __init__(self: Any, path: EntityPathLike): def __str__(self) -> str: return str(self.path) + def __hash__(self) -> int: + return hash(self.path) + if TYPE_CHECKING: EntityPathLike = Union[EntityPath, str] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/float32.py b/rerun_py/rerun_sdk/rerun/datatypes/float32.py index f705bbcdb2b3..c42ecd4c1c4c 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/float32.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/float32.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: Float32Like = Union[Float32, float] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/keypoint_id.py b/rerun_py/rerun_sdk/rerun/datatypes/keypoint_id.py index 8a3ac5dc5bd3..85cba8c20feb 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/keypoint_id.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/keypoint_id.py @@ -43,6 +43,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.id) + def __hash__(self) -> int: + return hash(self.id) + if TYPE_CHECKING: KeypointIdLike = Union[KeypointId, int] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/rgba32.py b/rerun_py/rerun_sdk/rerun/datatypes/rgba32.py index 65cb6a739d65..f12c583aafbb 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/rgba32.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/rgba32.py @@ -48,6 +48,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.rgba) + def __hash__(self) -> int: + return hash(self.rgba) + if TYPE_CHECKING: Rgba32Like = Union[Rgba32, int, Sequence[Union[int, float]], npt.NDArray[Union[np.uint8, np.float32, np.float64]]] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/time_int.py b/rerun_py/rerun_sdk/rerun/datatypes/time_int.py index 15d4bbdf7863..ac2ed851d824 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/time_int.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/time_int.py @@ -33,6 +33,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: TimeIntLike = Union[TimeInt, int] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint32.py b/rerun_py/rerun_sdk/rerun/datatypes/uint32.py index 8accc3696ad5..fe71bf7dc5f1 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/uint32.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint32.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: UInt32Like = Union[UInt32, int] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/uint64.py b/rerun_py/rerun_sdk/rerun/datatypes/uint64.py index 2dc9ad1f70aa..21107e68514e 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/uint64.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/uint64.py @@ -36,6 +36,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: UInt64Like = Union[UInt64, int] diff --git a/rerun_py/rerun_sdk/rerun/datatypes/utf8.py b/rerun_py/rerun_sdk/rerun/datatypes/utf8.py index e401e4ca5ecf..6dbb18b4098b 100644 --- a/rerun_py/rerun_sdk/rerun/datatypes/utf8.py +++ b/rerun_py/rerun_sdk/rerun/datatypes/utf8.py @@ -30,6 +30,9 @@ def __init__(self: Any, value: Utf8Like): def __str__(self) -> str: return str(self.value) + def __hash__(self) -> int: + return hash(self.value) + if TYPE_CHECKING: Utf8Like = Union[Utf8, str] diff --git a/rerun_py/tests/test_types/components/affix_fuzzer9.py b/rerun_py/tests/test_types/components/affix_fuzzer9.py index 97796d775508..840e58841417 100644 --- a/rerun_py/tests/test_types/components/affix_fuzzer9.py +++ b/rerun_py/tests/test_types/components/affix_fuzzer9.py @@ -27,6 +27,9 @@ def __init__(self: Any, single_string_required: AffixFuzzer9Like): def __str__(self) -> str: return str(self.single_string_required) + def __hash__(self) -> int: + return hash(self.single_string_required) + AffixFuzzer9Like = AffixFuzzer9 AffixFuzzer9ArrayLike = Union[ diff --git a/rerun_py/tests/test_types/datatypes/flattened_scalar.py b/rerun_py/tests/test_types/datatypes/flattened_scalar.py index d58eaee92468..d36a3506c39b 100644 --- a/rerun_py/tests/test_types/datatypes/flattened_scalar.py +++ b/rerun_py/tests/test_types/datatypes/flattened_scalar.py @@ -39,6 +39,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __float__(self) -> float: return float(self.value) + def __hash__(self) -> int: + return hash(self.value) + FlattenedScalarLike = FlattenedScalar FlattenedScalarArrayLike = Union[ diff --git a/rerun_py/tests/test_types/datatypes/primitive_component.py b/rerun_py/tests/test_types/datatypes/primitive_component.py index 375d73b31fc7..456cee1634ba 100644 --- a/rerun_py/tests/test_types/datatypes/primitive_component.py +++ b/rerun_py/tests/test_types/datatypes/primitive_component.py @@ -39,6 +39,9 @@ def __array__(self, dtype: npt.DTypeLike = None) -> npt.NDArray[Any]: def __int__(self) -> int: return int(self.value) + def __hash__(self) -> int: + return hash(self.value) + PrimitiveComponentLike = PrimitiveComponent PrimitiveComponentArrayLike = Union[ diff --git a/rerun_py/tests/test_types/datatypes/string_component.py b/rerun_py/tests/test_types/datatypes/string_component.py index a7fe617962ac..711d34f56fe6 100644 --- a/rerun_py/tests/test_types/datatypes/string_component.py +++ b/rerun_py/tests/test_types/datatypes/string_component.py @@ -33,6 +33,9 @@ def __init__(self: Any, value: StringComponentLike): def __str__(self) -> str: return str(self.value) + def __hash__(self) -> int: + return hash(self.value) + StringComponentLike = StringComponent StringComponentArrayLike = Union[ diff --git a/rerun_py/tests/unit/test_visible_time_ranges.py b/rerun_py/tests/unit/test_visible_time_ranges.py new file mode 100644 index 000000000000..45cf45b70744 --- /dev/null +++ b/rerun_py/tests/unit/test_visible_time_ranges.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +import pytest +import rerun as rr + + +def test_visible_time_ranges_warns_on_duplicate_entry() -> None: + rr.set_strict_mode(True) + + with pytest.raises(ValueError): + rr.blueprint.archetypes.VisibleTimeRanges([ + rr.VisibleTimeRange("timeline", start=rr.TimeRangeBoundary.infinite(), end=rr.TimeRangeBoundary.infinite()), + rr.VisibleTimeRange( + "timeline", start=rr.TimeRangeBoundary.absolute(seconds=1.0), end=rr.TimeRangeBoundary.cursor_relative() + ), + ]) + + +def test_visible_time_ranges_from_single() -> None: + time_range = rr.VisibleTimeRange( + "timeline", start=rr.TimeRangeBoundary.cursor_relative(), end=rr.TimeRangeBoundary.absolute(seconds=1.0) + ) + assert rr.blueprint.archetypes.VisibleTimeRanges(time_range) == rr.blueprint.archetypes.VisibleTimeRanges([ + time_range + ])