We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from abc import ABC, abstractmethod
16 from bisect import bisect_left
17 from dataclasses import replace
18 from logging import getLogger
19 from math import inf
20 from threading import Lock
21 from typing import Generic, List, Optional, Sequence, TypeVar
22 
23 from opentelemetry.sdk._metrics.measurement import Measurement
24 from opentelemetry.sdk._metrics.point import (
25     AggregationTemporality,
26     Gauge,
27     Histogram,
28     PointT,
29     Sum,
30 )
31 from opentelemetry.util._time import _time_ns
32 
33 _PointVarT = TypeVar("_PointVarT", bound=PointT)
34 
35 _logger = getLogger(__name__)
36 
37 
38 class Aggregation(ABC, Generic[_PointVarT]):
39     def __init__(self):
40         self._lock = Lock()
41 
42     @abstractmethod
43     def aggregate(self, measurement: Measurement) -> None:
44         pass
45 
46     @abstractmethod
47     def collect(self) -> Optional[_PointVarT]:
48         pass
49 
50 
51 class SumAggregation(Aggregation[Sum]):
52     def __init__(
53         self,
54         instrument_is_monotonic: bool,
55         instrument_temporality: AggregationTemporality,
56     ):
57         super().__init__()
58 
59         self._start_time_unix_nano = _time_ns()
60         self._instrument_temporality = instrument_temporality
61         self._instrument_is_monotonic = instrument_is_monotonic
62 
63         if self._instrument_temporality is AggregationTemporality.DELTA:
64             self._value = 0
65         else:
66             self._value = None
67 
68     def aggregate(self, measurement: Measurement) -> None:
69         with self._lock:
70             if self._value is None:
71                 self._value = 0
72             self._value = self._value + measurement.value
73 
74     def collect(self) -> Optional[Sum]:
75         """
76         Atomically return a point for the current value of the metric and
77         reset the aggregation value.
78         """
79         now = _time_ns()
80 
81         if self._instrument_temporality is AggregationTemporality.DELTA:
82 
83             with self._lock:
84                 value = self._value
85                 start_time_unix_nano = self._start_time_unix_nano
86 
87                 self._value = 0
88                 self._start_time_unix_nano = now + 1
89 
90             return Sum(
91                 aggregation_temporality=AggregationTemporality.DELTA,
92                 is_monotonic=self._instrument_is_monotonic,
93                 start_time_unix_nano=start_time_unix_nano,
94                 time_unix_nano=now,
95                 value=value,
96             )
97 
98         if self._value is None:
99             return None
100 
101         return Sum(
102             aggregation_temporality=AggregationTemporality.CUMULATIVE,
103             is_monotonic=self._instrument_is_monotonic,
104             start_time_unix_nano=self._start_time_unix_nano,
105             time_unix_nano=now,
106             value=self._value,
107         )
108 
109 
110 class LastValueAggregation(Aggregation[Gauge]):
111     def __init__(self):
112         super().__init__()
113         self._value = None
114 
115     def aggregate(self, measurement: Measurement):
116         with self._lock:
117             self._value = measurement.value
118 
119     def collect(self) -> Optional[Gauge]:
120         """
121         Atomically return a point for the current value of the metric.
122         """
123         if self._value is None:
124             return None
125 
126         return Gauge(
127             time_unix_nano=_time_ns(),
128             value=self._value,
129         )
130 
131 
132 class ExplicitBucketHistogramAggregation(Aggregation[Histogram]):
133     def __init__(
134         self,
135         boundaries: Sequence[float] = (
136             0.0,
137             5.0,
138             10.0,
139             25.0,
140             50.0,
141             75.0,
142             100.0,
143             250.0,
144             500.0,
145             1000.0,
146         ),
147         record_min_max: bool = True,
148     ):
149         super().__init__()
150         self._boundaries = tuple(boundaries)
151         self._bucket_counts = self._get_empty_bucket_counts()
152         self._min = inf
153         self._max = -inf
154         self._sum = 0
155         self._record_min_max = record_min_max
156         self._start_time_unix_nano = _time_ns()
157 
158     def _get_empty_bucket_counts(self) -> List[int]:
159         return [0] * (len(self._boundaries) + 1)
160 
161     def aggregate(self, measurement: Measurement) -> None:
162 
163         value = measurement.value
164 
165         if self._record_min_max:
166             self._min = min(self._min, value)
167             self._max = max(self._max, value)
168 
169         self._sum += value
170 
171         self._bucket_counts[bisect_left(self._boundaries, value)] += 1
172 
173     def collect(self) -> Histogram:
174         """
175         Atomically return a point for the current value of the metric.
176         """
177         now = _time_ns()
178 
179         with self._lock:
180             value = self._bucket_counts
181             start_time_unix_nano = self._start_time_unix_nano
182 
183             self._bucket_counts = self._get_empty_bucket_counts()
184             self._start_time_unix_nano = now + 1
185 
186         return Histogram(
187             start_time_unix_nano=start_time_unix_nano,
188             time_unix_nano=now,
189             bucket_counts=tuple(value),
190             explicit_bounds=self._boundaries,
191             aggregation_temporality=AggregationTemporality.DELTA,
192             sum=self._sum,
193         )
194 
195 
196 def _convert_aggregation_temporality(
197     previous_point: Optional[_PointVarT],
198     current_point: _PointVarT,
199     aggregation_temporality: AggregationTemporality,
200 ) -> _PointVarT:
201     """Converts `current_point` to the requested `aggregation_temporality`
202     given the `previous_point`.
203 
204     `previous_point` must have `CUMULATIVE` temporality. `current_point` may
205     have `DELTA` or `CUMULATIVE` temporality.
206 
207     The output point will have temporality `aggregation_temporality`. Since
208     `GAUGE` points have no temporality, they are returned unchanged.
209     """
210 
211     current_point_type = type(current_point)
212 
213     if current_point_type is Gauge:
214         return current_point
215 
216     if previous_point is not None and type(previous_point) is not type(
217         current_point
218     ):
219         _logger.warning(
220             "convert_aggregation_temporality called with mismatched "
221             "point types: %s and %s",
222             type(previous_point),
223             current_point_type,
224         )
225 
226         return current_point
227 
228     if current_point_type is Sum:
229         if previous_point is None:
230             # Output CUMULATIVE for a synchronous instrument
231             # There is no previous value, return the delta point as a
232             # cumulative
233             return replace(
234                 current_point, aggregation_temporality=aggregation_temporality
235             )
236         if previous_point.aggregation_temporality is not (
237             AggregationTemporality.CUMULATIVE
238         ):
239             raise Exception(
240                 "previous_point aggregation temporality must be CUMULATIVE"
241             )
242 
243         if current_point.aggregation_temporality is aggregation_temporality:
244             # Output DELTA for a synchronous instrument
245             # Output CUMULATIVE for an asynchronous instrument
246             return current_point
247 
248         if aggregation_temporality is AggregationTemporality.DELTA:
249             # Output temporality DELTA for an asynchronous instrument
250             value = current_point.value - previous_point.value
251             output_start_time_unix_nano = previous_point.time_unix_nano
252 
253         else:
254             # Output CUMULATIVE for a synchronous instrument
255             value = current_point.value + previous_point.value
256             output_start_time_unix_nano = previous_point.start_time_unix_nano
257 
258         is_monotonic = (
259             previous_point.is_monotonic and current_point.is_monotonic
260         )
261 
262         return Sum(
263             start_time_unix_nano=output_start_time_unix_nano,
264             time_unix_nano=current_point.time_unix_nano,
265             value=value,
266             aggregation_temporality=aggregation_temporality,
267             is_monotonic=is_monotonic,
268         )
269 
270     return None
271
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
         )
 
 
+# pylint: disable=too-many-return-statements,too-many-branches
 def _convert_aggregation_temporality(
     previous_point: Optional[_PointVarT],
     current_point: _PointVarT,
@@ -267,4 +268,46 @@
             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

{"golden_diff": "diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py\n--- a/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py\n+++ b/opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py\n@@ -193,6 +193,7 @@\n         )\n \n \n+# pylint: disable=too-many-return-statements,too-many-branches\n def _convert_aggregation_temporality(\n     previous_point: Optional[_PointVarT],\n     current_point: _PointVarT,\n@@ -267,4 +268,46 @@\n             is_monotonic=is_monotonic,\n         )\n \n+    if current_point_type is Histogram:\n+        if previous_point is None:\n+            return replace(\n+                current_point, aggregation_temporality=aggregation_temporality\n+            )\n+        if previous_point.aggregation_temporality is not (\n+            AggregationTemporality.CUMULATIVE\n+        ):\n+            raise Exception(\n+                \"previous_point aggregation temporality must be CUMULATIVE\"\n+            )\n+\n+        if current_point.aggregation_temporality is aggregation_temporality:\n+            return current_point\n+\n+        if aggregation_temporality is AggregationTemporality.CUMULATIVE:\n+            start_time_unix_nano = previous_point.start_time_unix_nano\n+            sum_ = current_point.sum + previous_point.sum\n+            bucket_counts = [\n+                curr_count + prev_count\n+                for curr_count, prev_count in zip(\n+                    current_point.bucket_counts, previous_point.bucket_counts\n+                )\n+            ]\n+        else:\n+            start_time_unix_nano = previous_point.time_unix_nano\n+            sum_ = current_point.sum - previous_point.sum\n+            bucket_counts = [\n+                curr_count - prev_count\n+                for curr_count, prev_count in zip(\n+                    current_point.bucket_counts, previous_point.bucket_counts\n+                )\n+            ]\n+\n+        return Histogram(\n+            start_time_unix_nano=start_time_unix_nano,\n+            time_unix_nano=current_point.time_unix_nano,\n+            bucket_counts=bucket_counts,\n+            explicit_bounds=current_point.explicit_bounds,\n+            sum=sum_,\n+            aggregation_temporality=aggregation_temporality,\n+        )\n     return None\n", "issue": "Add aggregation temporality conversion for all point types\nCan you add a todo for the other point types?\r\n\r\n_Originally posted by @aabmass in https://github.com/open-telemetry/opentelemetry-python/pull/2380#discussion_r787074866_\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom abc import ABC, abstractmethod\nfrom bisect import bisect_left\nfrom dataclasses import replace\nfrom logging import getLogger\nfrom math import inf\nfrom threading import Lock\nfrom typing import Generic, List, Optional, Sequence, TypeVar\n\nfrom opentelemetry.sdk._metrics.measurement import Measurement\nfrom opentelemetry.sdk._metrics.point import (\n    AggregationTemporality,\n    Gauge,\n    Histogram,\n    PointT,\n    Sum,\n)\nfrom opentelemetry.util._time import _time_ns\n\n_PointVarT = TypeVar(\"_PointVarT\", bound=PointT)\n\n_logger = getLogger(__name__)\n\n\nclass Aggregation(ABC, Generic[_PointVarT]):\n    def __init__(self):\n        self._lock = Lock()\n\n    @abstractmethod\n    def aggregate(self, measurement: Measurement) -> None:\n        pass\n\n    @abstractmethod\n    def collect(self) -> Optional[_PointVarT]:\n        pass\n\n\nclass SumAggregation(Aggregation[Sum]):\n    def __init__(\n        self,\n        instrument_is_monotonic: bool,\n        instrument_temporality: AggregationTemporality,\n    ):\n        super().__init__()\n\n        self._start_time_unix_nano = _time_ns()\n        self._instrument_temporality = instrument_temporality\n        self._instrument_is_monotonic = instrument_is_monotonic\n\n        if self._instrument_temporality is AggregationTemporality.DELTA:\n            self._value = 0\n        else:\n            self._value = None\n\n    def aggregate(self, measurement: Measurement) -> None:\n        with self._lock:\n            if self._value is None:\n                self._value = 0\n            self._value = self._value + measurement.value\n\n    def collect(self) -> Optional[Sum]:\n        \"\"\"\n        Atomically return a point for the current value of the metric and\n        reset the aggregation value.\n        \"\"\"\n        now = _time_ns()\n\n        if self._instrument_temporality is AggregationTemporality.DELTA:\n\n            with self._lock:\n                value = self._value\n                start_time_unix_nano = self._start_time_unix_nano\n\n                self._value = 0\n                self._start_time_unix_nano = now + 1\n\n            return Sum(\n                aggregation_temporality=AggregationTemporality.DELTA,\n                is_monotonic=self._instrument_is_monotonic,\n                start_time_unix_nano=start_time_unix_nano,\n                time_unix_nano=now,\n                value=value,\n            )\n\n        if self._value is None:\n            return None\n\n        return Sum(\n            aggregation_temporality=AggregationTemporality.CUMULATIVE,\n            is_monotonic=self._instrument_is_monotonic,\n            start_time_unix_nano=self._start_time_unix_nano,\n            time_unix_nano=now,\n            value=self._value,\n        )\n\n\nclass LastValueAggregation(Aggregation[Gauge]):\n    def __init__(self):\n        super().__init__()\n        self._value = None\n\n    def aggregate(self, measurement: Measurement):\n        with self._lock:\n            self._value = measurement.value\n\n    def collect(self) -> Optional[Gauge]:\n        \"\"\"\n        Atomically return a point for the current value of the metric.\n        \"\"\"\n        if self._value is None:\n            return None\n\n        return Gauge(\n            time_unix_nano=_time_ns(),\n            value=self._value,\n        )\n\n\nclass ExplicitBucketHistogramAggregation(Aggregation[Histogram]):\n    def __init__(\n        self,\n        boundaries: Sequence[float] = (\n            0.0,\n            5.0,\n            10.0,\n            25.0,\n            50.0,\n            75.0,\n            100.0,\n            250.0,\n            500.0,\n            1000.0,\n        ),\n        record_min_max: bool = True,\n    ):\n        super().__init__()\n        self._boundaries = tuple(boundaries)\n        self._bucket_counts = self._get_empty_bucket_counts()\n        self._min = inf\n        self._max = -inf\n        self._sum = 0\n        self._record_min_max = record_min_max\n        self._start_time_unix_nano = _time_ns()\n\n    def _get_empty_bucket_counts(self) -> List[int]:\n        return [0] * (len(self._boundaries) + 1)\n\n    def aggregate(self, measurement: Measurement) -> None:\n\n        value = measurement.value\n\n        if self._record_min_max:\n            self._min = min(self._min, value)\n            self._max = max(self._max, value)\n\n        self._sum += value\n\n        self._bucket_counts[bisect_left(self._boundaries, value)] += 1\n\n    def collect(self) -> Histogram:\n        \"\"\"\n        Atomically return a point for the current value of the metric.\n        \"\"\"\n        now = _time_ns()\n\n        with self._lock:\n            value = self._bucket_counts\n            start_time_unix_nano = self._start_time_unix_nano\n\n            self._bucket_counts = self._get_empty_bucket_counts()\n            self._start_time_unix_nano = now + 1\n\n        return Histogram(\n            start_time_unix_nano=start_time_unix_nano,\n            time_unix_nano=now,\n            bucket_counts=tuple(value),\n            explicit_bounds=self._boundaries,\n            aggregation_temporality=AggregationTemporality.DELTA,\n            sum=self._sum,\n        )\n\n\ndef _convert_aggregation_temporality(\n    previous_point: Optional[_PointVarT],\n    current_point: _PointVarT,\n    aggregation_temporality: AggregationTemporality,\n) -> _PointVarT:\n    \"\"\"Converts `current_point` to the requested `aggregation_temporality`\n    given the `previous_point`.\n\n    `previous_point` must have `CUMULATIVE` temporality. `current_point` may\n    have `DELTA` or `CUMULATIVE` temporality.\n\n    The output point will have temporality `aggregation_temporality`. Since\n    `GAUGE` points have no temporality, they are returned unchanged.\n    \"\"\"\n\n    current_point_type = type(current_point)\n\n    if current_point_type is Gauge:\n        return current_point\n\n    if previous_point is not None and type(previous_point) is not type(\n        current_point\n    ):\n        _logger.warning(\n            \"convert_aggregation_temporality called with mismatched \"\n            \"point types: %s and %s\",\n            type(previous_point),\n            current_point_type,\n        )\n\n        return current_point\n\n    if current_point_type is Sum:\n        if previous_point is None:\n            # Output CUMULATIVE for a synchronous instrument\n            # There is no previous value, return the delta point as a\n            # cumulative\n            return replace(\n                current_point, aggregation_temporality=aggregation_temporality\n            )\n        if previous_point.aggregation_temporality is not (\n            AggregationTemporality.CUMULATIVE\n        ):\n            raise Exception(\n                \"previous_point aggregation temporality must be CUMULATIVE\"\n            )\n\n        if current_point.aggregation_temporality is aggregation_temporality:\n            # Output DELTA for a synchronous instrument\n            # Output CUMULATIVE for an asynchronous instrument\n            return current_point\n\n        if aggregation_temporality is AggregationTemporality.DELTA:\n            # Output temporality DELTA for an asynchronous instrument\n            value = current_point.value - previous_point.value\n            output_start_time_unix_nano = previous_point.time_unix_nano\n\n        else:\n            # Output CUMULATIVE for a synchronous instrument\n            value = current_point.value + previous_point.value\n            output_start_time_unix_nano = previous_point.start_time_unix_nano\n\n        is_monotonic = (\n            previous_point.is_monotonic and current_point.is_monotonic\n        )\n\n        return Sum(\n            start_time_unix_nano=output_start_time_unix_nano,\n            time_unix_nano=current_point.time_unix_nano,\n            value=value,\n            aggregation_temporality=aggregation_temporality,\n            is_monotonic=is_monotonic,\n        )\n\n    return None\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom abc import ABC, abstractmethod\nfrom bisect import bisect_left\nfrom dataclasses import replace\nfrom logging import getLogger\nfrom math import inf\nfrom threading import Lock\nfrom typing import Generic, List, Optional, Sequence, TypeVar\n\nfrom opentelemetry.sdk._metrics.measurement import Measurement\nfrom opentelemetry.sdk._metrics.point import (\n    AggregationTemporality,\n    Gauge,\n    Histogram,\n    PointT,\n    Sum,\n)\nfrom opentelemetry.util._time import _time_ns\n\n_PointVarT = TypeVar(\"_PointVarT\", bound=PointT)\n\n_logger = getLogger(__name__)\n\n\nclass Aggregation(ABC, Generic[_PointVarT]):\n    def __init__(self):\n        self._lock = Lock()\n\n    @abstractmethod\n    def aggregate(self, measurement: Measurement) -> None:\n        pass\n\n    @abstractmethod\n    def collect(self) -> Optional[_PointVarT]:\n        pass\n\n\nclass SumAggregation(Aggregation[Sum]):\n    def __init__(\n        self,\n        instrument_is_monotonic: bool,\n        instrument_temporality: AggregationTemporality,\n    ):\n        super().__init__()\n\n        self._start_time_unix_nano = _time_ns()\n        self._instrument_temporality = instrument_temporality\n        self._instrument_is_monotonic = instrument_is_monotonic\n\n        if self._instrument_temporality is AggregationTemporality.DELTA:\n            self._value = 0\n        else:\n            self._value = None\n\n    def aggregate(self, measurement: Measurement) -> None:\n        with self._lock:\n            if self._value is None:\n                self._value = 0\n            self._value = self._value + measurement.value\n\n    def collect(self) -> Optional[Sum]:\n        \"\"\"\n        Atomically return a point for the current value of the metric and\n        reset the aggregation value.\n        \"\"\"\n        now = _time_ns()\n\n        if self._instrument_temporality is AggregationTemporality.DELTA:\n\n            with self._lock:\n                value = self._value\n                start_time_unix_nano = self._start_time_unix_nano\n\n                self._value = 0\n                self._start_time_unix_nano = now + 1\n\n            return Sum(\n                aggregation_temporality=AggregationTemporality.DELTA,\n                is_monotonic=self._instrument_is_monotonic,\n                start_time_unix_nano=start_time_unix_nano,\n                time_unix_nano=now,\n                value=value,\n            )\n\n        if self._value is None:\n            return None\n\n        return Sum(\n            aggregation_temporality=AggregationTemporality.CUMULATIVE,\n            is_monotonic=self._instrument_is_monotonic,\n            start_time_unix_nano=self._start_time_unix_nano,\n            time_unix_nano=now,\n            value=self._value,\n        )\n\n\nclass LastValueAggregation(Aggregation[Gauge]):\n    def __init__(self):\n        super().__init__()\n        self._value = None\n\n    def aggregate(self, measurement: Measurement):\n        with self._lock:\n            self._value = measurement.value\n\n    def collect(self) -> Optional[Gauge]:\n        \"\"\"\n        Atomically return a point for the current value of the metric.\n        \"\"\"\n        if self._value is None:\n            return None\n\n        return Gauge(\n            time_unix_nano=_time_ns(),\n            value=self._value,\n        )\n\n\nclass ExplicitBucketHistogramAggregation(Aggregation[Histogram]):\n    def __init__(\n        self,\n        boundaries: Sequence[float] = (\n            0.0,\n            5.0,\n            10.0,\n            25.0,\n            50.0,\n            75.0,\n            100.0,\n            250.0,\n            500.0,\n            1000.0,\n        ),\n        record_min_max: bool = True,\n    ):\n        super().__init__()\n        self._boundaries = tuple(boundaries)\n        self._bucket_counts = self._get_empty_bucket_counts()\n        self._min = inf\n        self._max = -inf\n        self._sum = 0\n        self._record_min_max = record_min_max\n        self._start_time_unix_nano = _time_ns()\n\n    def _get_empty_bucket_counts(self) -> List[int]:\n        return [0] * (len(self._boundaries) + 1)\n\n    def aggregate(self, measurement: Measurement) -> None:\n\n        value = measurement.value\n\n        if self._record_min_max:\n            self._min = min(self._min, value)\n            self._max = max(self._max, value)\n\n        self._sum += value\n\n        self._bucket_counts[bisect_left(self._boundaries, value)] += 1\n\n    def collect(self) -> Histogram:\n        \"\"\"\n        Atomically return a point for the current value of the metric.\n        \"\"\"\n        now = _time_ns()\n\n        with self._lock:\n            value = self._bucket_counts\n            start_time_unix_nano = self._start_time_unix_nano\n\n            self._bucket_counts = self._get_empty_bucket_counts()\n            self._start_time_unix_nano = now + 1\n\n        return Histogram(\n            start_time_unix_nano=start_time_unix_nano,\n            time_unix_nano=now,\n            bucket_counts=tuple(value),\n            explicit_bounds=self._boundaries,\n            aggregation_temporality=AggregationTemporality.DELTA,\n            sum=self._sum,\n        )\n\n\n# pylint: disable=too-many-return-statements,too-many-branches\ndef _convert_aggregation_temporality(\n    previous_point: Optional[_PointVarT],\n    current_point: _PointVarT,\n    aggregation_temporality: AggregationTemporality,\n) -> _PointVarT:\n    \"\"\"Converts `current_point` to the requested `aggregation_temporality`\n    given the `previous_point`.\n\n    `previous_point` must have `CUMULATIVE` temporality. `current_point` may\n    have `DELTA` or `CUMULATIVE` temporality.\n\n    The output point will have temporality `aggregation_temporality`. Since\n    `GAUGE` points have no temporality, they are returned unchanged.\n    \"\"\"\n\n    current_point_type = type(current_point)\n\n    if current_point_type is Gauge:\n        return current_point\n\n    if previous_point is not None and type(previous_point) is not type(\n        current_point\n    ):\n        _logger.warning(\n            \"convert_aggregation_temporality called with mismatched \"\n            \"point types: %s and %s\",\n            type(previous_point),\n            current_point_type,\n        )\n\n        return current_point\n\n    if current_point_type is Sum:\n        if previous_point is None:\n            # Output CUMULATIVE for a synchronous instrument\n            # There is no previous value, return the delta point as a\n            # cumulative\n            return replace(\n                current_point, aggregation_temporality=aggregation_temporality\n            )\n        if previous_point.aggregation_temporality is not (\n            AggregationTemporality.CUMULATIVE\n        ):\n            raise Exception(\n                \"previous_point aggregation temporality must be CUMULATIVE\"\n            )\n\n        if current_point.aggregation_temporality is aggregation_temporality:\n            # Output DELTA for a synchronous instrument\n            # Output CUMULATIVE for an asynchronous instrument\n            return current_point\n\n        if aggregation_temporality is AggregationTemporality.DELTA:\n            # Output temporality DELTA for an asynchronous instrument\n            value = current_point.value - previous_point.value\n            output_start_time_unix_nano = previous_point.time_unix_nano\n\n        else:\n            # Output CUMULATIVE for a synchronous instrument\n            value = current_point.value + previous_point.value\n            output_start_time_unix_nano = previous_point.start_time_unix_nano\n\n        is_monotonic = (\n            previous_point.is_monotonic and current_point.is_monotonic\n        )\n\n        return Sum(\n            start_time_unix_nano=output_start_time_unix_nano,\n            time_unix_nano=current_point.time_unix_nano,\n            value=value,\n            aggregation_temporality=aggregation_temporality,\n            is_monotonic=is_monotonic,\n        )\n\n    if current_point_type is Histogram:\n        if previous_point is None:\n            return replace(\n                current_point, aggregation_temporality=aggregation_temporality\n            )\n        if previous_point.aggregation_temporality is not (\n            AggregationTemporality.CUMULATIVE\n        ):\n            raise Exception(\n                \"previous_point aggregation temporality must be CUMULATIVE\"\n            )\n\n        if current_point.aggregation_temporality is aggregation_temporality:\n            return current_point\n\n        if aggregation_temporality is AggregationTemporality.CUMULATIVE:\n            start_time_unix_nano = previous_point.start_time_unix_nano\n            sum_ = current_point.sum + previous_point.sum\n            bucket_counts = [\n                curr_count + prev_count\n                for curr_count, prev_count in zip(\n                    current_point.bucket_counts, previous_point.bucket_counts\n                )\n            ]\n        else:\n            start_time_unix_nano = previous_point.time_unix_nano\n            sum_ = current_point.sum - previous_point.sum\n            bucket_counts = [\n                curr_count - prev_count\n                for curr_count, prev_count in zip(\n                    current_point.bucket_counts, previous_point.bucket_counts\n                )\n            ]\n\n        return Histogram(\n            start_time_unix_nano=start_time_unix_nano,\n            time_unix_nano=current_point.time_unix_nano,\n            bucket_counts=bucket_counts,\n            explicit_bounds=current_point.explicit_bounds,\n            sum=sum_,\n            aggregation_temporality=aggregation_temporality,\n        )\n    return None\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/_metrics/aggregation.py"}]}