Repository: open-telemetry/opentelemetry-python

Issue:
AttributeError: 'NoneType' object has no attribute 'attributes'
Run a slightly modified version of the [docs example](https://github.com/open-telemetry/opentelemetry-python/tree/main/docs/examples/metrics):

``` python
from typing import Iterable
from time import sleep

from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
    OTLPMetricExporter,
)
from opentelemetry.metrics import (
    CallbackOptions,
    Observation,
    get_meter_provider,
    set_meter_provider,
)
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader

exporter = OTLPMetricExporter(insecure=True)
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=1000)
provider = MeterProvider(metric_readers=[reader])
set_meter_provider(provider)


def observable_counter_func(options: CallbackOptions) -> Iterable[Observation]:
    yield Observation(1, {})


def observable_up_down_counter_func(
    options: CallbackOptions,
) -> Iterable[Observation]:
    yield Observation(-10, {})


def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]:
    yield Observation(9, {})


meter = get_meter_provider().get_meter("getting-started", "0.1.2")

# Counter
counter = meter.create_counter("counter")
counter.add(1)

# Async Counter
observable_counter = meter.create_observable_counter(
    "observable_counter",
    [observable_counter_func],
)

# UpDownCounter
updown_counter = meter.create_up_down_counter("updown_counter")
updown_counter.add(1)
updown_counter.add(-5)

# Async UpDownCounter
observable_updown_counter = meter.create_observable_up_down_counter(
    "observable_updown_counter", [observable_up_down_counter_func]
)

# Histogram
histogram = meter.create_histogram("histogram")
histogram.record(99.9)

# Async Gauge
gauge = meter.create_observable_gauge("gauge", [observable_gauge_func])

sleep(6)
```
running this script (after activating the collector via Docker) produces these errors:

```
Exception while exporting metrics 'NoneType' object has no attribute 'attributes'
Traceback (most recent call last):
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py", line 412, in _receive_metrics
    self._exporter.export(metrics_data, timeout_millis=timeout_millis)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 203, in export
    return self._export(metrics_data)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py", line 293, in _export
    request=self._translate_data(data),
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 139, in _translate_data
    data_point.attributes
AttributeError: 'NoneType' object has no attribute 'attributes'
Exception while exporting metrics 'NoneType' object has no attribute 'attributes'
Traceback (most recent call last):
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py", line 412, in _receive_metrics
    self._exporter.export(metrics_data, timeout_millis=timeout_millis)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 203, in export
    return self._export(metrics_data)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py", line 293, in _export
    request=self._translate_data(data),
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 139, in _translate_data
    data_point.attributes
AttributeError: 'NoneType' object has no attribute 'attributes'
Exception while exporting metrics 'NoneType' object has no attribute 'attributes'
Traceback (most recent call last):
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py", line 412, in _receive_metrics
    self._exporter.export(metrics_data, timeout_millis=timeout_millis)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 203, in export
    return self._export(metrics_data)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py", line 293, in _export
    request=self._translate_data(data),
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 139, in _translate_data
    data_point.attributes
AttributeError: 'NoneType' object has no attribute 'attributes'
Exception while exporting metrics 'NoneType' object has no attribute 'attributes'
Traceback (most recent call last):
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py", line 412, in _receive_metrics
    self._exporter.export(metrics_data, timeout_millis=timeout_millis)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 203, in export
    return self._export(metrics_data)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py", line 293, in _export
    request=self._translate_data(data),
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 139, in _translate_data
    data_point.attributes
AttributeError: 'NoneType' object has no attribute 'attributes'
Exception while exporting metrics 'NoneType' object has no attribute 'attributes'
Traceback (most recent call last):
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/export/__init__.py", line 412, in _receive_metrics
    self._exporter.export(metrics_data, timeout_millis=timeout_millis)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 203, in export
    return self._export(metrics_data)
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/exporter.py", line 293, in _export
    request=self._translate_data(data),
  File "/home/ocelotl/github/ocelotl/opentelemetry-python/exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/metric_exporter/__init__.py", line 139, in _translate_data
    data_point.attributes
AttributeError: 'NoneType' object has no attribute 'attributes'
shutdown
```



Discussion:

Resolving patch:
diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/_view_instrument_match.py
@@ -130,6 +130,8 @@ def collect(
 
         with self._lock:
             for aggregation in self._attributes_aggregation.values():
-                yield aggregation.collect(
+                data_point = aggregation.collect(
                     aggregation_temporality, collection_start_nanos
                 )
+                if data_point is not None:
+                    yield data_point


Test patch:
diff --git a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
--- a/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
+++ b/opentelemetry-sdk/tests/metrics/test_view_instrument_match.py
@@ -212,6 +212,78 @@ def test_collect(self):
         self.assertEqual(number_data_point.attributes, {"c": "d"})
         self.assertEqual(number_data_point.value, 0)
 
+    def test_data_point_check(self):
+        instrument1 = Counter(
+            "instrument1",
+            Mock(),
+            Mock(),
+            description="description",
+            unit="unit",
+        )
+        instrument1.instrumentation_scope = self.mock_instrumentation_scope
+
+        view_instrument_match = _ViewInstrumentMatch(
+            view=View(
+                instrument_name="instrument1",
+                name="name",
+                aggregation=DefaultAggregation(),
+            ),
+            instrument=instrument1,
+            instrument_class_aggregation=MagicMock(
+                **{
+                    "__getitem__.return_value": Mock(
+                        **{
+                            "_create_aggregation.return_value": Mock(
+                                **{
+                                    "collect.side_effect": [
+                                        Mock(),
+                                        Mock(),
+                                        None,
+                                        Mock(),
+                                    ]
+                                }
+                            )
+                        }
+                    )
+                }
+            ),
+        )
+
+        view_instrument_match.consume_measurement(
+            Measurement(
+                value=0,
+                instrument=Mock(name="instrument1"),
+                attributes={"c": "d", "f": "g"},
+            )
+        )
+        view_instrument_match.consume_measurement(
+            Measurement(
+                value=0,
+                instrument=Mock(name="instrument1"),
+                attributes={"h": "i", "j": "k"},
+            )
+        )
+        view_instrument_match.consume_measurement(
+            Measurement(
+                value=0,
+                instrument=Mock(name="instrument1"),
+                attributes={"l": "m", "n": "o"},
+            )
+        )
+        view_instrument_match.consume_measurement(
+            Measurement(
+                value=0,
+                instrument=Mock(name="instrument1"),
+                attributes={"p": "q", "r": "s"},
+            )
+        )
+
+        result = view_instrument_match.collect(
+            AggregationTemporality.CUMULATIVE, 0
+        )
+
+        self.assertEqual(len(list(result)), 3)
+
     def test_setting_aggregation(self):
         instrument1 = Counter(
             name="instrument1",