Repository: open-telemetry/opentelemetry-python

Issue:
Add aggregation temporality conversion for all point types
Can you add a todo for the other point types?

_Originally posted by @aabmass in https://github.com/open-telemetry/opentelemetry-python/pull/2380#discussion_r787074866_


Discussion:

Resolving patch:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py
@@ -193,6 +193,7 @@ def collect(self) -> Histogram:
         )
 
 
+# pylint: disable=too-many-return-statements,too-many-branches
 def _convert_aggregation_temporality(
     previous_point: Optional[_PointVarT],
     current_point: _PointVarT,
@@ -267,4 +268,46 @@ def _convert_aggregation_temporality(
             is_monotonic=is_monotonic,
         )
 
+    if current_point_type is Histogram:
+        if previous_point is None:
+            return replace(
+                current_point, aggregation_temporality=aggregation_temporality
+            )
+        if previous_point.aggregation_temporality is not (
+            AggregationTemporality.CUMULATIVE
+        ):
+            raise Exception(
+                "previous_point aggregation temporality must be CUMULATIVE"
+            )
+
+        if current_point.aggregation_temporality is aggregation_temporality:
+            return current_point
+
+        if aggregation_temporality is AggregationTemporality.CUMULATIVE:
+            start_time_unix_nano = previous_point.start_time_unix_nano
+            sum_ = current_point.sum + previous_point.sum
+            bucket_counts = [
+                curr_count + prev_count
+                for curr_count, prev_count in zip(
+                    current_point.bucket_counts, previous_point.bucket_counts
+                )
+            ]
+        else:
+            start_time_unix_nano = previous_point.time_unix_nano
+            sum_ = current_point.sum - previous_point.sum
+            bucket_counts = [
+                curr_count - prev_count
+                for curr_count, prev_count in zip(
+                    current_point.bucket_counts, previous_point.bucket_counts
+                )
+            ]
+
+        return Histogram(
+            start_time_unix_nano=start_time_unix_nano,
+            time_unix_nano=current_point.time_unix_nano,
+            bucket_counts=bucket_counts,
+            explicit_bounds=current_point.explicit_bounds,
+            sum=sum_,
+            aggregation_temporality=aggregation_temporality,
+        )
     return None


Test patch:
diff --git a/opentelemetry-sdk/tests/metrics/test_aggregation.py b/opentelemetry-sdk/tests/metrics/test_aggregation.py
--- a/opentelemetry-sdk/tests/metrics/test_aggregation.py
+++ b/opentelemetry-sdk/tests/metrics/test_aggregation.py
@@ -26,7 +26,7 @@
     _convert_aggregation_temporality,
 )
 from opentelemetry.sdk._metrics.measurement import Measurement
-from opentelemetry.sdk._metrics.point import Gauge, Sum
+from opentelemetry.sdk._metrics.point import Gauge, Histogram, Sum
 
 
 class TestSynchronousSumAggregation(TestCase):
@@ -576,3 +576,167 @@ def test_current_point_gauge(self):
             ),
             current_point,
         )
+
+
+class TestHistogramConvertAggregationTemporality(TestCase):
+    def test_previous_point_none(self):
+
+        current_point = Histogram(
+            start_time_unix_nano=0,
+            time_unix_nano=1,
+            bucket_counts=[0, 2, 1, 2, 0],
+            explicit_bounds=[0, 5, 10, 25],
+            sum=70,
+            aggregation_temporality=AggregationTemporality.DELTA,
+        )
+
+        self.assertEqual(
+            _convert_aggregation_temporality(
+                None, current_point, AggregationTemporality.CUMULATIVE
+            ),
+            replace(
+                current_point,
+                aggregation_temporality=AggregationTemporality.CUMULATIVE,
+            ),
+        )
+
+    def test_previous_point_non_cumulative(self):
+
+        with self.assertRaises(Exception):
+
+            _convert_aggregation_temporality(
+                Histogram(
+                    start_time_unix_nano=0,
+                    time_unix_nano=1,
+                    bucket_counts=[0, 2, 1, 2, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=70,
+                    aggregation_temporality=AggregationTemporality.DELTA,
+                ),
+                Histogram(
+                    start_time_unix_nano=1,
+                    time_unix_nano=2,
+                    bucket_counts=[0, 1, 3, 0, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=35,
+                    aggregation_temporality=AggregationTemporality.DELTA,
+                ),
+                AggregationTemporality.DELTA,
+            ),
+
+    def test_same_aggregation_temporality_cumulative(self):
+        current_point = Histogram(
+            start_time_unix_nano=0,
+            time_unix_nano=2,
+            bucket_counts=[0, 3, 4, 2, 0],
+            explicit_bounds=[0, 5, 10, 25],
+            sum=105,
+            aggregation_temporality=AggregationTemporality.CUMULATIVE,
+        )
+        self.assertEqual(
+            _convert_aggregation_temporality(
+                Histogram(
+                    start_time_unix_nano=0,
+                    time_unix_nano=1,
+                    bucket_counts=[0, 2, 1, 2, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=70,
+                    aggregation_temporality=AggregationTemporality.CUMULATIVE,
+                ),
+                current_point,
+                AggregationTemporality.CUMULATIVE,
+            ),
+            current_point,
+        )
+
+    def test_same_aggregation_temporality_delta(self):
+        current_point = Histogram(
+            start_time_unix_nano=1,
+            time_unix_nano=2,
+            bucket_counts=[0, 1, 3, 0, 0],
+            explicit_bounds=[0, 5, 10, 25],
+            sum=35,
+            aggregation_temporality=AggregationTemporality.DELTA,
+        )
+
+        self.assertEqual(
+            _convert_aggregation_temporality(
+                Histogram(
+                    start_time_unix_nano=0,
+                    time_unix_nano=2,
+                    bucket_counts=[0, 3, 4, 2, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=105,
+                    aggregation_temporality=AggregationTemporality.CUMULATIVE,
+                ),
+                current_point,
+                AggregationTemporality.DELTA,
+            ),
+            current_point,
+        )
+
+    def test_aggregation_temporality_to_cumulative(self):
+        current_point = Histogram(
+            start_time_unix_nano=1,
+            time_unix_nano=2,
+            bucket_counts=[0, 1, 3, 0, 0],
+            explicit_bounds=[0, 5, 10, 25],
+            sum=35,
+            aggregation_temporality=AggregationTemporality.DELTA,
+        )
+
+        self.assertEqual(
+            _convert_aggregation_temporality(
+                Histogram(
+                    start_time_unix_nano=0,
+                    time_unix_nano=1,
+                    bucket_counts=[0, 2, 1, 2, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=70,
+                    aggregation_temporality=AggregationTemporality.CUMULATIVE,
+                ),
+                current_point,
+                AggregationTemporality.CUMULATIVE,
+            ),
+            Histogram(
+                start_time_unix_nano=0,
+                time_unix_nano=2,
+                bucket_counts=[0, 3, 4, 2, 0],
+                explicit_bounds=[0, 5, 10, 25],
+                sum=105,
+                aggregation_temporality=AggregationTemporality.CUMULATIVE,
+            ),
+        )
+
+    def test_aggregation_temporality_to_delta(self):
+        current_point = Histogram(
+            start_time_unix_nano=0,
+            time_unix_nano=2,
+            bucket_counts=[0, 3, 4, 2, 0],
+            explicit_bounds=[0, 5, 10, 25],
+            sum=105,
+            aggregation_temporality=AggregationTemporality.CUMULATIVE,
+        )
+
+        self.assertEqual(
+            _convert_aggregation_temporality(
+                Histogram(
+                    start_time_unix_nano=0,
+                    time_unix_nano=1,
+                    bucket_counts=[0, 2, 1, 2, 0],
+                    explicit_bounds=[0, 5, 10, 25],
+                    sum=70,
+                    aggregation_temporality=AggregationTemporality.CUMULATIVE,
+                ),
+                current_point,
+                AggregationTemporality.DELTA,
+            ),
+            Histogram(
+                start_time_unix_nano=1,
+                time_unix_nano=2,
+                bucket_counts=[0, 1, 3, 0, 0],
+                explicit_bounds=[0, 5, 10, 25],
+                sum=35,
+                aggregation_temporality=AggregationTemporality.DELTA,
+            ),
+        )