From 45f1935490c7b599d143d4df6e2d4c3c435adc19 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 28 Mar 2024 19:20:56 +0530 Subject: [PATCH 1/5] Make create_gauge non-abstract method --- .../metrics/_internal/__init__.py | 6 +- opentelemetry-api/tests/metrics/test_meter.py | 1 - .../metrics/test_subclass_instantiation.py | 168 ++++++++++++++++++ 3 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 opentelemetry-api/tests/metrics/test_subclass_instantiation.py diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py index 2e6914f8e3..e714b7374c 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py @@ -41,6 +41,8 @@ """ +import warnings + from abc import ABC, abstractmethod from logging import getLogger from os import environ @@ -385,7 +387,6 @@ def create_histogram( description: A description for this instrument and what it measures. """ - @abstractmethod def create_gauge( self, name: str, @@ -400,6 +401,9 @@ def create_gauge( example, ``By`` for bytes. UCUM units are recommended. description: A description for this instrument and what it measures. """ + warnings.warn( + "create_gauge() is not implemented and will be a no-op" + ) @abstractmethod def create_observable_gauge( diff --git a/opentelemetry-api/tests/metrics/test_meter.py b/opentelemetry-api/tests/metrics/test_meter.py index 8b427a7372..2226965521 100644 --- a/opentelemetry-api/tests/metrics/test_meter.py +++ b/opentelemetry-api/tests/metrics/test_meter.py @@ -134,7 +134,6 @@ def test_create_gauge(self): """ self.assertTrue(hasattr(Meter, "create_gauge")) - self.assertTrue(Meter.create_gauge.__isabstractmethod__) def test_create_observable_gauge(self): """ diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py new file mode 100644 index 0000000000..8df2447e09 --- /dev/null +++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py @@ -0,0 +1,168 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# type: ignore + +# NOTE: The tests in this file are intended to test the semver compatibility of the public API. +# Any tests that fail here indicate that the public API has changed in a way that is not backwards compatible. +# Either bump the major version of the API, or make the necessary changes to the API to remain semver compatible. + +from opentelemetry.metrics import ( + Meter, + MeterProvider, + Synchronous, + Asynchronous, + Counter, + _Gauge, + Histogram, + Instrument, + ObservableCounter, + ObservableGauge, + ObservableUpDownCounter, + UpDownCounter, +) + +class MeterProviderImplTest(MeterProvider): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def get_meter(self, name: str, version: str | None = None, schema_url: str | None = None) -> Meter: + return super().get_meter(name, version, schema_url) + +def test_meter_provider_subclass_instantiation(): + meter_provider = MeterProviderImplTest() + assert isinstance(meter_provider, MeterProvider) + +class MeterImplTest(Meter): + def create_counter(self, name, description, **kwargs): + pass + + def create_up_down_counter(self, name, description, **kwargs): + pass + + def create_observable_counter(self, name, description, **kwargs): + pass + + def create_histogram(self, name, description, **kwargs): + pass + + def create_observable_gauge(self, name, description, **kwargs): + pass + + def create_observable_up_down_counter(self, name, description, **kwargs): + pass + +def test_meter_subclass_instantiation(): + meter = MeterImplTest("subclass_test") + assert isinstance(meter, Meter) + +class SynchronousImplTest(Synchronous): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_synchronous_subclass_instantiation(): + synchronous = SynchronousImplTest("subclass_test") + assert isinstance(synchronous, Synchronous) + +class AsynchronousImplTest(Asynchronous): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_asynchronous_subclass_instantiation(): + asynchronous = AsynchronousImplTest("subclass_test") + assert isinstance(asynchronous, Asynchronous) + +class CounterImplTest(Counter): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + + def add(self, amount: int, **kwargs): + pass + +def test_counter_subclass_instantiation(): + counter = CounterImplTest("subclass_test") + assert isinstance(counter, Counter) + +class UpDownCounterImplTest(UpDownCounter): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + + def add(self, amount: int, **kwargs): + pass + +def test_up_down_counter_subclass_instantiation(): + up_down_counter = UpDownCounterImplTest("subclass_test") + assert isinstance(up_down_counter, UpDownCounter) + +class ObservableCounterImplTest(ObservableCounter): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_observable_counter_subclass_instantiation(): + observable_counter = ObservableCounterImplTest("subclass_test") + assert isinstance(observable_counter, ObservableCounter) + +class HistogramImplTest(Histogram): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + + def record(self, amount: int, **kwargs): + pass + +def test_histogram_subclass_instantiation(): + histogram = HistogramImplTest("subclass_test") + assert isinstance(histogram, Histogram) + +class GaugeImplTest(_Gauge): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + + def set(self, amount: int, **kwargs): + pass + +def test_gauge_subclass_instantiation(): + gauge = GaugeImplTest("subclass_test") + assert isinstance(gauge, _Gauge) + +class InstrumentImplTest(Instrument): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_instrument_subclass_instantiation(): + instrument = InstrumentImplTest("subclass_test") + assert isinstance(instrument, Instrument) + +class ObservableGaugeImplTest(ObservableGauge): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_observable_gauge_subclass_instantiation(): + observable_gauge = ObservableGaugeImplTest("subclass_test") + assert isinstance(observable_gauge, ObservableGauge) + +class ObservableUpDownCounterImplTest(ObservableUpDownCounter): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_observable_up_down_counter_subclass_instantiation(): + observable_up_down_counter = ObservableUpDownCounterImplTest("subclass_test") + assert isinstance(observable_up_down_counter, ObservableUpDownCounter) + +class ObservableUpDownCounterImplTest(ObservableUpDownCounter): + def __init__(self, name: str, unit: str = "", description: str = "") -> None: + super().__init__(name, unit, description) + +def test_observable_up_down_counter_subclass_instantiation(): + observable_up_down_counter = ObservableUpDownCounterImplTest("subclass_test") + assert isinstance(observable_up_down_counter, ObservableUpDownCounter) + From 3771bd11701534fc88aba9621cb0b1b9034e1530 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 28 Mar 2024 20:39:42 +0530 Subject: [PATCH 2/5] Update test_subclass_instantiation.py --- opentelemetry-api/tests/metrics/test_subclass_instantiation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py index 8df2447e09..44763aa54f 100644 --- a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py +++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py @@ -33,9 +33,6 @@ ) class MeterProviderImplTest(MeterProvider): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def get_meter(self, name: str, version: str | None = None, schema_url: str | None = None) -> Meter: return super().get_meter(name, version, schema_url) From 1c0b28b1b1b8a719805f694a130ec8a932f979ab Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 28 Mar 2024 22:03:50 +0530 Subject: [PATCH 3/5] Fix lint --- CHANGELOG.md | 2 ++ .../metrics/test_subclass_instantiation.py | 36 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4a7add3c..79fb60e3b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Make create_gauge non-abstract method + ([#3817](https://github.com/open-telemetry/opentelemetry-python/pull/3817)) - Make `tracer.start_as_current_span()` decorator work with async functions ([#3633](https://github.com/open-telemetry/opentelemetry-python/pull/3633)) - Fix python 3.12 deprecation warning diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py index 44763aa54f..bbcef6afd2 100644 --- a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py +++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py @@ -17,6 +17,7 @@ # Any tests that fail here indicate that the public API has changed in a way that is not backwards compatible. # Either bump the major version of the API, or make the necessary changes to the API to remain semver compatible. +from typing import Optional from opentelemetry.metrics import ( Meter, MeterProvider, @@ -32,14 +33,19 @@ UpDownCounter, ) + class MeterProviderImplTest(MeterProvider): - def get_meter(self, name: str, version: str | None = None, schema_url: str | None = None) -> Meter: + def get_meter( + self, name: str, version: Optional[str] = None, schema_url: Optional[str] = None + ) -> Meter: return super().get_meter(name, version, schema_url) + def test_meter_provider_subclass_instantiation(): meter_provider = MeterProviderImplTest() assert isinstance(meter_provider, MeterProvider) + class MeterImplTest(Meter): def create_counter(self, name, description, **kwargs): pass @@ -59,26 +65,32 @@ def create_observable_gauge(self, name, description, **kwargs): def create_observable_up_down_counter(self, name, description, **kwargs): pass + def test_meter_subclass_instantiation(): meter = MeterImplTest("subclass_test") assert isinstance(meter, Meter) + class SynchronousImplTest(Synchronous): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_synchronous_subclass_instantiation(): synchronous = SynchronousImplTest("subclass_test") assert isinstance(synchronous, Synchronous) + class AsynchronousImplTest(Asynchronous): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_asynchronous_subclass_instantiation(): asynchronous = AsynchronousImplTest("subclass_test") assert isinstance(asynchronous, Asynchronous) + class CounterImplTest(Counter): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) @@ -86,10 +98,12 @@ def __init__(self, name: str, unit: str = "", description: str = "") -> None: def add(self, amount: int, **kwargs): pass + def test_counter_subclass_instantiation(): counter = CounterImplTest("subclass_test") assert isinstance(counter, Counter) + class UpDownCounterImplTest(UpDownCounter): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) @@ -97,18 +111,22 @@ def __init__(self, name: str, unit: str = "", description: str = "") -> None: def add(self, amount: int, **kwargs): pass + def test_up_down_counter_subclass_instantiation(): up_down_counter = UpDownCounterImplTest("subclass_test") assert isinstance(up_down_counter, UpDownCounter) + class ObservableCounterImplTest(ObservableCounter): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_observable_counter_subclass_instantiation(): observable_counter = ObservableCounterImplTest("subclass_test") assert isinstance(observable_counter, ObservableCounter) + class HistogramImplTest(Histogram): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) @@ -116,10 +134,12 @@ def __init__(self, name: str, unit: str = "", description: str = "") -> None: def record(self, amount: int, **kwargs): pass + def test_histogram_subclass_instantiation(): histogram = HistogramImplTest("subclass_test") assert isinstance(histogram, Histogram) + class GaugeImplTest(_Gauge): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) @@ -127,39 +147,37 @@ def __init__(self, name: str, unit: str = "", description: str = "") -> None: def set(self, amount: int, **kwargs): pass + def test_gauge_subclass_instantiation(): gauge = GaugeImplTest("subclass_test") assert isinstance(gauge, _Gauge) + class InstrumentImplTest(Instrument): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_instrument_subclass_instantiation(): instrument = InstrumentImplTest("subclass_test") assert isinstance(instrument, Instrument) + class ObservableGaugeImplTest(ObservableGauge): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_observable_gauge_subclass_instantiation(): observable_gauge = ObservableGaugeImplTest("subclass_test") assert isinstance(observable_gauge, ObservableGauge) -class ObservableUpDownCounterImplTest(ObservableUpDownCounter): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: - super().__init__(name, unit, description) - -def test_observable_up_down_counter_subclass_instantiation(): - observable_up_down_counter = ObservableUpDownCounterImplTest("subclass_test") - assert isinstance(observable_up_down_counter, ObservableUpDownCounter) class ObservableUpDownCounterImplTest(ObservableUpDownCounter): def __init__(self, name: str, unit: str = "", description: str = "") -> None: super().__init__(name, unit, description) + def test_observable_up_down_counter_subclass_instantiation(): observable_up_down_counter = ObservableUpDownCounterImplTest("subclass_test") assert isinstance(observable_up_down_counter, ObservableUpDownCounter) - From 4b46293012532d2e864dbc5b3ab20febbf69f6c9 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 28 Mar 2024 22:16:46 +0530 Subject: [PATCH 4/5] Actually fix lint --- .../metrics/_internal/__init__.py | 6 +-- .../metrics/test_subclass_instantiation.py | 49 ++++++++++++++----- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py index e714b7374c..625aca22c9 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py @@ -387,7 +387,7 @@ def create_histogram( description: A description for this instrument and what it measures. """ - def create_gauge( + def create_gauge( # type: ignore self, name: str, unit: str = "", @@ -401,9 +401,7 @@ def create_gauge( example, ``By`` for bytes. UCUM units are recommended. description: A description for this instrument and what it measures. """ - warnings.warn( - "create_gauge() is not implemented and will be a no-op" - ) + warnings.warn("create_gauge() is not implemented and will be a no-op") @abstractmethod def create_observable_gauge( diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py index bbcef6afd2..c928a0cc06 100644 --- a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py +++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py @@ -36,7 +36,10 @@ class MeterProviderImplTest(MeterProvider): def get_meter( - self, name: str, version: Optional[str] = None, schema_url: Optional[str] = None + self, + name: str, + version: Optional[str] = None, + schema_url: Optional[str] = None, ) -> Meter: return super().get_meter(name, version, schema_url) @@ -72,7 +75,9 @@ def test_meter_subclass_instantiation(): class SynchronousImplTest(Synchronous): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) @@ -82,7 +87,9 @@ def test_synchronous_subclass_instantiation(): class AsynchronousImplTest(Asynchronous): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) @@ -92,7 +99,9 @@ def test_asynchronous_subclass_instantiation(): class CounterImplTest(Counter): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) def add(self, amount: int, **kwargs): @@ -105,7 +114,9 @@ def test_counter_subclass_instantiation(): class UpDownCounterImplTest(UpDownCounter): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) def add(self, amount: int, **kwargs): @@ -118,7 +129,9 @@ def test_up_down_counter_subclass_instantiation(): class ObservableCounterImplTest(ObservableCounter): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) @@ -128,7 +141,9 @@ def test_observable_counter_subclass_instantiation(): class HistogramImplTest(Histogram): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) def record(self, amount: int, **kwargs): @@ -141,7 +156,9 @@ def test_histogram_subclass_instantiation(): class GaugeImplTest(_Gauge): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) def set(self, amount: int, **kwargs): @@ -154,7 +171,9 @@ def test_gauge_subclass_instantiation(): class InstrumentImplTest(Instrument): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) @@ -164,7 +183,9 @@ def test_instrument_subclass_instantiation(): class ObservableGaugeImplTest(ObservableGauge): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) @@ -174,10 +195,14 @@ def test_observable_gauge_subclass_instantiation(): class ObservableUpDownCounterImplTest(ObservableUpDownCounter): - def __init__(self, name: str, unit: str = "", description: str = "") -> None: + def __init__( + self, name: str, unit: str = "", description: str = "" + ) -> None: super().__init__(name, unit, description) def test_observable_up_down_counter_subclass_instantiation(): - observable_up_down_counter = ObservableUpDownCounterImplTest("subclass_test") + observable_up_down_counter = ObservableUpDownCounterImplTest( + "subclass_test" + ) assert isinstance(observable_up_down_counter, ObservableUpDownCounter) From 1a92c8d76d406bf327dc7d46d54254a1efae0845 Mon Sep 17 00:00:00 2001 From: Srikanth Chekuri Date: Thu, 28 Mar 2024 22:29:32 +0530 Subject: [PATCH 5/5] This time we actually fix lint --- .../src/opentelemetry/metrics/_internal/__init__.py | 3 +-- .../tests/metrics/test_subclass_instantiation.py | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py index 625aca22c9..1579a1a244 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/_internal/__init__.py @@ -42,7 +42,6 @@ import warnings - from abc import ABC, abstractmethod from logging import getLogger from os import environ @@ -387,7 +386,7 @@ def create_histogram( description: A description for this instrument and what it measures. """ - def create_gauge( # type: ignore + def create_gauge( # type: ignore # pylint: disable=no-self-use self, name: str, unit: str = "", diff --git a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py index c928a0cc06..a5b68d1c06 100644 --- a/opentelemetry-api/tests/metrics/test_subclass_instantiation.py +++ b/opentelemetry-api/tests/metrics/test_subclass_instantiation.py @@ -18,19 +18,20 @@ # Either bump the major version of the API, or make the necessary changes to the API to remain semver compatible. from typing import Optional + from opentelemetry.metrics import ( - Meter, - MeterProvider, - Synchronous, Asynchronous, Counter, - _Gauge, Histogram, Instrument, + Meter, + MeterProvider, ObservableCounter, ObservableGauge, ObservableUpDownCounter, + Synchronous, UpDownCounter, + _Gauge, )