From e8f5eb5ca5fce9d6e70f2bae288529a14d2c1191 Mon Sep 17 00:00:00 2001 From: Amos Law Date: Thu, 17 Sep 2020 12:43:36 -0700 Subject: [PATCH] Add type hints to OTLP exporter (#1121) --- .../opentelemetry-exporter-otlp/CHANGELOG.md | 2 ++ .../opentelemetry/exporter/otlp/exporter.py | 33 ++++++++++++----- .../otlp/metrics_exporter/__init__.py | 36 +++++++++++++------ .../exporter/otlp/trace_exporter/__init__.py | 33 ++++++++++------- 4 files changed, 72 insertions(+), 32 deletions(-) diff --git a/exporter/opentelemetry-exporter-otlp/CHANGELOG.md b/exporter/opentelemetry-exporter-otlp/CHANGELOG.md index 94ae999eca9..a3686e1a10d 100644 --- a/exporter/opentelemetry-exporter-otlp/CHANGELOG.md +++ b/exporter/opentelemetry-exporter-otlp/CHANGELOG.md @@ -10,6 +10,8 @@ Released 2020-09-17 ([#1095](https://github.com/open-telemetry/opentelemetry-python/pull/1095)) - Add metric OTLP exporter ([#835](https://github.com/open-telemetry/opentelemetry-python/pull/835)) +- Add type hints to OTLP exporter + ([#1121](https://github.com/open-telemetry/opentelemetry-python/pull/1121)) ## Version 0.12b0 diff --git a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/exporter.py b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/exporter.py index 7cd9f905e06..079557f831a 100644 --- a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/exporter.py +++ b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/exporter.py @@ -18,6 +18,9 @@ from abc import ABC, abstractmethod from collections.abc import Mapping, Sequence from time import sleep +from typing import Any, Callable, Dict, Generic, List, Optional +from typing import Sequence as TypingSequence +from typing import Text, Tuple, TypeVar from backoff import expo from google.rpc.error_details_pb2 import RetryInfo @@ -31,11 +34,17 @@ from opentelemetry.proto.common.v1.common_pb2 import AnyValue, KeyValue from opentelemetry.proto.resource.v1.resource_pb2 import Resource +from opentelemetry.sdk.resources import Resource as SDKResource logger = logging.getLogger(__name__) +SDKDataT = TypeVar("SDKDataT") +ResourceDataT = TypeVar("ResourceDataT") +TypingResourceT = TypeVar("TypingResourceT") +ExportServiceRequestT = TypeVar("ExportServiceRequestT") +ExportResultT = TypeVar("ExportResultT") -def _translate_key_values(key, value): +def _translate_key_values(key: Text, value: Any) -> KeyValue: if isinstance(value, bool): any_value = AnyValue(bool_value=value) @@ -64,8 +73,12 @@ def _translate_key_values(key, value): def _get_resource_data( - sdk_resource_instrumentation_library_data, resource_class, name -): + sdk_resource_instrumentation_library_data: Dict[ + SDKResource, ResourceDataT + ], + resource_class: Callable[..., TypingResourceT], + name: str, +) -> List[TypingResourceT]: resource_data = [] @@ -101,7 +114,9 @@ def _get_resource_data( # pylint: disable=no-member -class OTLPExporterMixin(ABC): +class OTLPExporterMixin( + ABC, Generic[SDKDataT, ExportServiceRequestT, ExportResultT] +): """OTLP span/metric exporter Args: @@ -114,7 +129,7 @@ def __init__( self, endpoint: str = "localhost:55680", credentials: ChannelCredentials = None, - metadata: tuple = None, + metadata: Optional[Tuple[Any]] = None, ): super().__init__() @@ -127,10 +142,12 @@ def __init__( self._client = self._stub(secure_channel(endpoint, credentials)) @abstractmethod - def _translate_data(self, data): + def _translate_data( + self, data: TypingSequence[SDKDataT] + ) -> ExportServiceRequestT: pass - def _export(self, data): + def _export(self, data: TypingSequence[SDKDataT]) -> ExportResultT: # expo returns a generator that yields delay values which grow # exponentially. Once delay is greater than max_value, the yielded # value will remain constant. @@ -190,5 +207,5 @@ def _export(self, data): return self._result.FAILURE - def shutdown(self): + def shutdown(self) -> None: pass diff --git a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/metrics_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/metrics_exporter/__init__.py index 944428e37d0..033de0d6ddc 100644 --- a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/metrics_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/metrics_exporter/__init__.py @@ -15,13 +15,14 @@ """OTLP Metrics Exporter""" import logging -from typing import Sequence +from typing import List, Sequence, Type, TypeVar, Union # pylint: disable=duplicate-code from opentelemetry.exporter.otlp.exporter import ( OTLPExporterMixin, _get_resource_data, ) +from opentelemetry.metrics import InstrumentT from opentelemetry.proto.collector.metrics.v1.metrics_service_pb2 import ( ExportMetricsServiceRequest, ) @@ -41,9 +42,8 @@ MetricDescriptor, ResourceMetrics, ) -from opentelemetry.sdk.metrics import Counter -from opentelemetry.sdk.metrics import Metric as SDKMetric from opentelemetry.sdk.metrics import ( + Counter, SumObserver, UpDownCounter, UpDownSumObserver, @@ -51,14 +51,18 @@ ValueRecorder, ) from opentelemetry.sdk.metrics.export import ( + MetricRecord, MetricsExporter, MetricsExportResult, ) logger = logging.getLogger(__name__) +DataPointT = TypeVar("DataPointT", Int64DataPoint, DoubleDataPoint) -def _get_data_points(sdk_metric, data_point_class): +def _get_data_points( + sdk_metric: MetricRecord, data_point_class: Type[DataPointT] +) -> List[DataPointT]: data_points = [] @@ -89,7 +93,9 @@ def _get_data_points(sdk_metric, data_point_class): return data_points -def _get_temporality(instrument): +def _get_temporality( + instrument: InstrumentT, +) -> "MetricDescriptor.TemporalityValue": # pylint: disable=no-member if isinstance(instrument, (Counter, UpDownCounter)): temporality = MetricDescriptor.Temporality.DELTA @@ -107,12 +113,12 @@ def _get_temporality(instrument): return temporality -def _get_type(value_type): +def _get_type(value_type: Union[int, float]) -> "MetricDescriptor.TypeValue": # pylint: disable=no-member - if value_type is int: + if value_type is int: # type: ignore[comparison-overlap] type_ = MetricDescriptor.Type.INT64 - elif value_type is float: + elif value_type is float: # type: ignore[comparison-overlap] type_ = MetricDescriptor.Type.DOUBLE # FIXME What are the types that correspond with @@ -126,7 +132,13 @@ def _get_type(value_type): return type_ -class OTLPMetricsExporter(MetricsExporter, OTLPExporterMixin): +class OTLPMetricsExporter( + MetricsExporter, + OTLPExporterMixin[ + MetricRecord, ExportMetricsServiceRequest, MetricsExportResult + ], +): + # pylint: disable=unsubscriptable-object """OTLP metrics exporter Args: @@ -138,7 +150,9 @@ class OTLPMetricsExporter(MetricsExporter, OTLPExporterMixin): _stub = MetricsServiceStub _result = MetricsExportResult - def _translate_data(self, data): + def _translate_data( + self, data: Sequence[MetricRecord] + ) -> ExportMetricsServiceRequest: # pylint: disable=too-many-locals,no-member # pylint: disable=attribute-defined-outside-init @@ -193,6 +207,6 @@ def _translate_data(self, data): ) ) - def export(self, metrics: Sequence[SDKMetric]) -> MetricsExportResult: + def export(self, metrics: Sequence[MetricRecord]) -> MetricsExportResult: # pylint: disable=arguments-differ return self._export(metrics) diff --git a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/trace_exporter/__init__.py b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/trace_exporter/__init__.py index fd1d8e235e4..c08d6049e1f 100644 --- a/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/trace_exporter/__init__.py +++ b/exporter/opentelemetry-exporter-otlp/src/opentelemetry/exporter/otlp/trace_exporter/__init__.py @@ -41,7 +41,11 @@ # pylint: disable=no-member -class OTLPSpanExporter(SpanExporter, OTLPExporterMixin): +class OTLPSpanExporter( + SpanExporter, + OTLPExporterMixin[SDKSpan, ExportTraceServiceRequest, SpanExportResult], +): + # pylint: disable=unsubscriptable-object """OTLP span exporter Args: @@ -53,34 +57,34 @@ class OTLPSpanExporter(SpanExporter, OTLPExporterMixin): _result = SpanExportResult _stub = TraceServiceStub - def _translate_name(self, sdk_span): + def _translate_name(self, sdk_span: SDKSpan) -> None: self._collector_span_kwargs["name"] = sdk_span.name - def _translate_start_time(self, sdk_span): + def _translate_start_time(self, sdk_span: SDKSpan) -> None: self._collector_span_kwargs[ "start_time_unix_nano" ] = sdk_span.start_time - def _translate_end_time(self, sdk_span): + def _translate_end_time(self, sdk_span: SDKSpan) -> None: self._collector_span_kwargs["end_time_unix_nano"] = sdk_span.end_time - def _translate_span_id(self, sdk_span): + def _translate_span_id(self, sdk_span: SDKSpan) -> None: self._collector_span_kwargs[ "span_id" ] = sdk_span.context.span_id.to_bytes(8, "big") - def _translate_trace_id(self, sdk_span): + def _translate_trace_id(self, sdk_span: SDKSpan) -> None: self._collector_span_kwargs[ "trace_id" ] = sdk_span.context.trace_id.to_bytes(16, "big") - def _translate_parent(self, sdk_span): + def _translate_parent(self, sdk_span: SDKSpan) -> None: if sdk_span.parent is not None: self._collector_span_kwargs[ "parent_span_id" ] = sdk_span.parent.span_id.to_bytes(8, "big") - def _translate_context_trace_state(self, sdk_span): + def _translate_context_trace_state(self, sdk_span: SDKSpan) -> None: if sdk_span.context.trace_state is not None: self._collector_span_kwargs["trace_state"] = ",".join( [ @@ -89,7 +93,7 @@ def _translate_context_trace_state(self, sdk_span): ] ) - def _translate_attributes(self, sdk_span): + def _translate_attributes(self, sdk_span: SDKSpan) -> None: if sdk_span.attributes: self._collector_span_kwargs["attributes"] = [] @@ -103,7 +107,7 @@ def _translate_attributes(self, sdk_span): except Exception as error: # pylint: disable=broad-except logger.exception(error) - def _translate_events(self, sdk_span): + def _translate_events(self, sdk_span: SDKSpan) -> None: if sdk_span.events: self._collector_span_kwargs["events"] = [] @@ -127,7 +131,7 @@ def _translate_events(self, sdk_span): collector_span_event ) - def _translate_links(self, sdk_span): + def _translate_links(self, sdk_span: SDKSpan) -> None: if sdk_span.links: self._collector_span_kwargs["links"] = [] @@ -153,14 +157,17 @@ def _translate_links(self, sdk_span): collector_span_link ) - def _translate_status(self, sdk_span): + def _translate_status(self, sdk_span: SDKSpan) -> None: if sdk_span.status is not None: self._collector_span_kwargs["status"] = Status( code=sdk_span.status.canonical_code.value, message=sdk_span.status.description, ) - def _translate_data(self, data) -> ExportTraceServiceRequest: + def _translate_data( + self, data: Sequence[SDKSpan] + ) -> ExportTraceServiceRequest: + # pylint: disable=attribute-defined-outside-init sdk_resource_instrumentation_library_spans = {}