From 9b7cf01bea1e63e4f6ace9f9cfdb51b3c3267e6e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Mon, 6 Apr 2020 21:05:48 -0600 Subject: [PATCH 1/9] Do not load entry points in configuration WIP --- .../opentelemetry/configuration/__init__.py | 67 ++++++------------- .../src/opentelemetry/trace/__init__.py | 7 +- .../src/opentelemetry/util/__init__.py | 25 +++++++ 3 files changed, 48 insertions(+), 51 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py index 850f0d4fe0f..e351ed67493 100644 --- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py +++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py @@ -61,71 +61,46 @@ """ -from logging import getLogger from os import environ - -from pkg_resources import iter_entry_points - -logger = getLogger(__name__) +from re import fullmatch class Configuration: _instance = None - __slots__ = ("tracer_provider", "meter_provider") + __slots__ = [] def __new__(cls) -> "Configuration": if Configuration._instance is None: - configuration = { - key: "default_{}".format(key) for key in cls.__slots__ - } - - for key, value in configuration.items(): - configuration[key] = environ.get( - "OPENTELEMETRY_PYTHON_{}".format(key.upper()), value - ) - - for key, value in configuration.items(): - underscored_key = "_{}".format(key) - - setattr(Configuration, underscored_key, None) + for key in { + key: value + for key, value in environ.items() + if fullmatch("OPENTELEMETRY_PYTHON_[A-Z][A-Z_]*", key) + is not None + }.keys(): + setattr(Configuration, "_{}".format(key), None) setattr( Configuration, key, property( - fget=lambda cls, local_key=key, local_value=value: cls._load( - key=local_key, value=local_value + fget=lambda cls, key=key: getattr( + cls, "_{}".format(key) ) ), ) Configuration._instance = object.__new__(cls) - return cls._instance + Configuration.__slots__.extend( + [ + key + for key in Configuration.__dict__.keys() if not key.startswith("_") # pylint: disable=consider-iterating-dictionary + ] + ) + Configuration.__slots__ = tuple(Configuration.__slots__) - @classmethod - def _load(cls, key=None, value=None): - underscored_key = "_{}".format(key) + from ipdb import set_trace + set_trace() - if getattr(cls, underscored_key) is None: - try: - setattr( - cls, - underscored_key, - next( - iter_entry_points( - "opentelemetry_{}".format(key), name=value, - ) - ).load()(), - ) - except Exception: # pylint: disable=broad-except - # FIXME Decide on how to handle this. Should an exception be - # raised here, or only a message should be logged and should - # we fall back to the default meter provider? - logger.error( - "Failed to load configured provider %s", value, - ) - raise - - return getattr(cls, underscored_key) + return cls._instance diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index 856745e0771..7dd678ca4eb 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -77,9 +77,8 @@ from contextlib import contextmanager from logging import getLogger -from opentelemetry.configuration import Configuration # type: ignore from opentelemetry.trace.status import Status -from opentelemetry.util import types +from opentelemetry.util import _load_provider, types logger = getLogger(__name__) @@ -701,8 +700,6 @@ def get_tracer_provider() -> TracerProvider: global _TRACER_PROVIDER # pylint: disable=global-statement if _TRACER_PROVIDER is None: - _TRACER_PROVIDER = ( - Configuration().tracer_provider # type: ignore # pylint: disable=no-member - ) + _TRACER_PROVIDER = _load_provider("tracer_provider") return _TRACER_PROVIDER # type: ignore diff --git a/opentelemetry-api/src/opentelemetry/util/__init__.py b/opentelemetry-api/src/opentelemetry/util/__init__.py index 9f68ef2d393..112bf4678c1 100644 --- a/opentelemetry-api/src/opentelemetry/util/__init__.py +++ b/opentelemetry-api/src/opentelemetry/util/__init__.py @@ -12,6 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. import time +from logging import getLogger + +from pkg_resources import iter_entry_points + +from opentelemetry.configuration import Configuration + +logger = getLogger(__name__) # Since we want API users to be able to provide timestamps, # this needs to be in the API. @@ -23,3 +30,21 @@ def time_ns() -> int: return int(time.time() * 1e9) + + +def _load_provider(provider): + + try: + return next( + iter_entry_points( + "opentelemetry_{}".format(provider), + name=getattr( + Configuration(), provider, "default_{}".format(provider), + ), + ) + ).load()() + except Exception: # pylint: disable=broad-except + logger.error( + "Failed to load configured provider %s", provider, + ) + raise From a0ff1757b228904298606f00c88cd2c39b70b7dc Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 7 Apr 2020 13:56:26 -0600 Subject: [PATCH 2/9] Fix test cases --- .../opentelemetry/configuration/__init__.py | 45 +++++------ .../tests/configuration/test_configuration.py | 80 +++---------------- 2 files changed, 30 insertions(+), 95 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py index e351ed67493..2513e855622 100644 --- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py +++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py @@ -73,34 +73,29 @@ class Configuration: def __new__(cls) -> "Configuration": if Configuration._instance is None: - for key in { - key: value - for key, value in environ.items() - if fullmatch("OPENTELEMETRY_PYTHON_[A-Z][A-Z_]*", key) - is not None - }.keys(): - setattr(Configuration, "_{}".format(key), None) - setattr( - Configuration, - key, - property( - fget=lambda cls, key=key: getattr( - cls, "_{}".format(key) - ) - ), - ) + for key, value in environ.items(): - Configuration._instance = object.__new__(cls) + match = fullmatch("OPENTELEMETRY_PYTHON_([A-Z][A-Z_]*)", key) + + if match is not None: + + key = match.group(1).lower() + + setattr(Configuration, "_{}".format(key), value) + setattr( + Configuration, + key, + property( + fget=lambda cls, key=key: getattr( + cls, "_{}".format(key) + ) + ), + ) + + Configuration.__slots__.append(key) - Configuration.__slots__.extend( - [ - key - for key in Configuration.__dict__.keys() if not key.startswith("_") # pylint: disable=consider-iterating-dictionary - ] - ) Configuration.__slots__ = tuple(Configuration.__slots__) - from ipdb import set_trace - set_trace() + Configuration._instance = object.__new__(cls) return cls._instance diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py index c69c09b52b9..d63d87182ce 100644 --- a/opentelemetry-api/tests/configuration/test_configuration.py +++ b/opentelemetry-api/tests/configuration/test_configuration.py @@ -12,97 +12,37 @@ # See the License for the specific language governing permissions and # limitations under the License. -from json import dumps from unittest import TestCase from unittest.mock import patch -from pytest import fixture # type: ignore # pylint: disable=import-error - from opentelemetry.configuration import Configuration # type: ignore class TestConfiguration(TestCase): - class IterEntryPointsMock: - def __init__( - self, argument, name=None - ): # pylint: disable=unused-argument - self._name = name - - def __next__(self): - return self - - def __call__(self): - return self._name - - def load(self): - return self - - @fixture(autouse=True) - def configdir(self, tmpdir): # type: ignore # pylint: disable=no-self-use - tmpdir.chdir() - tmpdir.mkdir(".config").join("opentelemetry_python.json").write( - dumps({"tracer_provider": "overridden_tracer_provider"}) - ) def setUp(self): - Configuration._instance = None # pylint: disable=protected-access + from opentelemetry.configuration import Configuration def tearDown(self): - Configuration._instance = None # pylint: disable=protected-access + from opentelemetry.configuration import Configuration def test_singleton(self): + self.assertIsInstance(Configuration(), Configuration) self.assertIs(Configuration(), Configuration()) - @patch( - "opentelemetry.configuration.iter_entry_points", - **{"side_effect": IterEntryPointsMock} # type: ignore - ) - def test_lazy( # type: ignore - self, mock_iter_entry_points, # pylint: disable=unused-argument - ): - configuration = Configuration() - - self.assertIsNone( - configuration._tracer_provider # pylint: disable=no-member,protected-access - ) - - configuration.tracer_provider # pylint: disable=pointless-statement - - self.assertEqual( - configuration._tracer_provider, # pylint: disable=no-member,protected-access - "default_tracer_provider", - ) - - @patch( - "opentelemetry.configuration.iter_entry_points", - **{"side_effect": IterEntryPointsMock} # type: ignore - ) - def test_default_values( # type: ignore - self, mock_iter_entry_points # pylint: disable=unused-argument - ): - self.assertEqual( - Configuration().tracer_provider, "default_tracer_provider" - ) # pylint: disable=no-member - self.assertEqual( - Configuration().meter_provider, "default_meter_provider" - ) # pylint: disable=no-member - - @patch( - "opentelemetry.configuration.iter_entry_points", - **{"side_effect": IterEntryPointsMock} # type: ignore - ) @patch.dict( "os.environ", - {"OPENTELEMETRY_PYTHON_METER_PROVIDER": "overridden_meter_provider"}, + { + "OPENTELEMETRY_PYTHON_METER_PROVIDER": "meter_provider", + "OPENTELEMETRY_PYTHON_TRACER_PROVIDER": "tracer_provider", + }, ) - def test_environment_variables( # type: ignore - self, mock_iter_entry_points # pylint: disable=unused-argument - ): # type: ignore + def test_environment_variables(self): # type: ignore self.assertEqual( - Configuration().tracer_provider, "default_tracer_provider" + Configuration().meter_provider, "meter_provider" ) # pylint: disable=no-member self.assertEqual( - Configuration().meter_provider, "overridden_meter_provider" + Configuration().tracer_provider, "tracer_provider" ) # pylint: disable=no-member def test_property(self): From d21666ee172403473f61dc963d1dfbacd453df95 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 7 Apr 2020 14:21:15 -0600 Subject: [PATCH 3/9] Try to fix lint WIP --- .../tests/configuration/test_configuration.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py index d63d87182ce..25da1a1a866 100644 --- a/opentelemetry-api/tests/configuration/test_configuration.py +++ b/opentelemetry-api/tests/configuration/test_configuration.py @@ -19,12 +19,18 @@ class TestConfiguration(TestCase): - + # pylint: disable def setUp(self): - from opentelemetry.configuration import Configuration + from opentelemetry.configuration import ( # pylint: disable + Configuration, + ) + + # =redefined-outer-name,reimported,import-outside-toplevel,unused-import def tearDown(self): - from opentelemetry.configuration import Configuration + from opentelemetry.configuration import ( + Configuration, # pylint: disable + ) def test_singleton(self): self.assertIsInstance(Configuration(), Configuration) From 1db9a71344ba39576e0db5ca99f8a13920c33a4e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 7 Apr 2020 15:09:33 -0600 Subject: [PATCH 4/9] More lint fixing --- .../src/opentelemetry/metrics/__init__.py | 6 ++---- .../src/opentelemetry/trace/__init__.py | 2 +- .../src/opentelemetry/util/__init__.py | 16 +++++++++------- .../tests/configuration/test_configuration.py | 14 ++++---------- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index c0ab525b7d5..2a28a396ede 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -34,7 +34,7 @@ from logging import getLogger from typing import Callable, Dict, Sequence, Tuple, Type, TypeVar -from opentelemetry.configuration import Configuration # type: ignore +from opentelemetry.util import _load_provider logger = getLogger(__name__) ValueT = TypeVar("ValueT", int, float) @@ -410,8 +410,6 @@ def get_meter_provider() -> MeterProvider: global _METER_PROVIDER # pylint: disable=global-statement if _METER_PROVIDER is None: - _METER_PROVIDER = ( - Configuration().meter_provider # type: ignore # pylint: disable=no-member - ) + _METER_PROVIDER = _load_provider("meter_provider") # type: ignore return _METER_PROVIDER # type: ignore diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index 7dd678ca4eb..0afaac98e92 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -700,6 +700,6 @@ def get_tracer_provider() -> TracerProvider: global _TRACER_PROVIDER # pylint: disable=global-statement if _TRACER_PROVIDER is None: - _TRACER_PROVIDER = _load_provider("tracer_provider") + _TRACER_PROVIDER = _load_provider("tracer_provider") # type: ignore return _TRACER_PROVIDER # type: ignore diff --git a/opentelemetry-api/src/opentelemetry/util/__init__.py b/opentelemetry-api/src/opentelemetry/util/__init__.py index 112bf4678c1..90d79492d2d 100644 --- a/opentelemetry-api/src/opentelemetry/util/__init__.py +++ b/opentelemetry-api/src/opentelemetry/util/__init__.py @@ -13,10 +13,13 @@ # limitations under the License. import time from logging import getLogger +from typing import Union from pkg_resources import iter_entry_points -from opentelemetry.configuration import Configuration +from opentelemetry.configuration import Configuration # type: ignore +from opentelemetry.metrics import MeterProvider +from opentelemetry.trace import TracerProvider logger = getLogger(__name__) @@ -24,7 +27,7 @@ # this needs to be in the API. try: - time_ns = time.time_ns + time_ns = time.time_ns # type: ignore # Python versions < 3.7 except AttributeError: @@ -32,14 +35,13 @@ def time_ns() -> int: return int(time.time() * 1e9) -def _load_provider(provider): - +def _load_provider(provider: str) -> Union[TracerProvider, MeterProvider]: try: - return next( + return next( # type: ignore iter_entry_points( "opentelemetry_{}".format(provider), - name=getattr( - Configuration(), provider, "default_{}".format(provider), + name=getattr( # type: ignore + Configuration(), provider, "default_{}".format(provider), # type: ignore ), ) ).load()() diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py index 25da1a1a866..701269e849c 100644 --- a/opentelemetry-api/tests/configuration/test_configuration.py +++ b/opentelemetry-api/tests/configuration/test_configuration.py @@ -11,6 +11,7 @@ # 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. +# pylint: disable-all from unittest import TestCase from unittest.mock import patch @@ -19,25 +20,18 @@ class TestConfiguration(TestCase): - # pylint: disable def setUp(self): - from opentelemetry.configuration import ( # pylint: disable - Configuration, - ) - - # =redefined-outer-name,reimported,import-outside-toplevel,unused-import + from opentelemetry.configuration import Configuration # type: ignore def tearDown(self): - from opentelemetry.configuration import ( - Configuration, # pylint: disable - ) + from opentelemetry.configuration import Configuration # type: ignore def test_singleton(self): self.assertIsInstance(Configuration(), Configuration) self.assertIs(Configuration(), Configuration()) @patch.dict( - "os.environ", + "os.environ", # type: ignore { "OPENTELEMETRY_PYTHON_METER_PROVIDER": "meter_provider", "OPENTELEMETRY_PYTHON_TRACER_PROVIDER": "tracer_provider", From 2eb48ad7da34f8e581468f02df1e608b9166ec43 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 7 Apr 2020 15:33:33 -0600 Subject: [PATCH 5/9] Fixing documentation --- .../opentelemetry/configuration/__init__.py | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py index 2513e855622..ca5b01a7a41 100644 --- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py +++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py @@ -18,12 +18,38 @@ """ Simple configuration manager -This is a configuration manager for the Tracer and Meter providers. It reads -configuration from environment variables prefixed with -``OPENTELEMETRY_PYTHON_``: +This is a configuration manager for OpenTelemetry. It reads configuration +values from environment variables prefixed with +``OPENTELEMETRY_PYTHON_`` whose characters are only all caps and underscores. +The first character after ``OPENTELEMETRY_PYTHON_`` must be an uppercase +character. -1. ``OPENTELEMETRY_PYTHON_TRACER_PROVIDER`` -2. ``OPENTELEMETRY_PYTHON_METER_PROVIDER`` +For example, these environment variables will be read: + +1. ``OPENTELEMETRY_PYTHON_SOMETHING`` +2. ``OPENTELEMETRY_PYTHON_SOMETHING_ELSE_`` +3. ``OPENTELEMETRY_PYTHON_SOMETHING_ELSE_AND__ELSE`` + +These won't: + +1. ``OPENTELEMETRY_PYTH_SOMETHING`` +2. ``OPENTELEMETRY_PYTHON_something`` +3. ``OPENTELEMETRY_PYTHON_SOMETHING_2_AND__ELSE`` +4. ``OPENTELEMETRY_PYTHON_SOMETHING_%_ELSE`` + +The values stored in the environment variables can be found in an instance of +``opentelemetry.configuration.Configuration``. This class can be instantiated +freely because instantiating it returns a singleton. + +For example, if the environment variable +``OPENTELEMETRY_PYTHON_METER_PROVIDER`` value is ``my_meter_provider``, then +``Configuration().meter_provider == "my_meter_provider"`` would be ``True``. + +Environment variables used by OpenTelemetry +------------------------------------------- + +1. OPENTELEMETRY_PYTHON_METER_PROVIDER +2. OPENTELEMETRY_PYTHON_TRACER_PROVIDER The value of these environment variables should be the name of the entry point that points to the class that implements either provider. This OpenTelemetry @@ -47,18 +73,6 @@ "default_meter_provider" (this is not actually necessary since the OpenTelemetry API provided providers are the default ones used if no configuration is found in the environment variables). - -Once this is done, the configuration manager can be used by simply importing -it from opentelemetry.configuration.Configuration. This is a class that can -be instantiated as many times as needed without concern because it will -always produce the same instance. Its attributes are lazy loaded and they -hold an instance of their corresponding provider. So, for example, to get -the configured meter provider:: - - from opentelemetry.configuration import Configuration - - tracer_provider = Configuration().tracer_provider - """ from os import environ From 099fa642bae6037cb9d068598c5872197bf582fc Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Tue, 7 Apr 2020 19:29:28 -0600 Subject: [PATCH 6/9] More lint fixing --- opentelemetry-api/src/opentelemetry/metrics/__init__.py | 4 ++-- opentelemetry-api/src/opentelemetry/trace/__init__.py | 4 ++-- opentelemetry-api/src/opentelemetry/util/__init__.py | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/opentelemetry-api/src/opentelemetry/metrics/__init__.py b/opentelemetry-api/src/opentelemetry/metrics/__init__.py index 2a28a396ede..b3fe69ab59f 100644 --- a/opentelemetry-api/src/opentelemetry/metrics/__init__.py +++ b/opentelemetry-api/src/opentelemetry/metrics/__init__.py @@ -410,6 +410,6 @@ def get_meter_provider() -> MeterProvider: global _METER_PROVIDER # pylint: disable=global-statement if _METER_PROVIDER is None: - _METER_PROVIDER = _load_provider("meter_provider") # type: ignore + _METER_PROVIDER = _load_provider("meter_provider") - return _METER_PROVIDER # type: ignore + return _METER_PROVIDER diff --git a/opentelemetry-api/src/opentelemetry/trace/__init__.py b/opentelemetry-api/src/opentelemetry/trace/__init__.py index 0afaac98e92..773a3908ce1 100644 --- a/opentelemetry-api/src/opentelemetry/trace/__init__.py +++ b/opentelemetry-api/src/opentelemetry/trace/__init__.py @@ -700,6 +700,6 @@ def get_tracer_provider() -> TracerProvider: global _TRACER_PROVIDER # pylint: disable=global-statement if _TRACER_PROVIDER is None: - _TRACER_PROVIDER = _load_provider("tracer_provider") # type: ignore + _TRACER_PROVIDER = _load_provider("tracer_provider") - return _TRACER_PROVIDER # type: ignore + return _TRACER_PROVIDER diff --git a/opentelemetry-api/src/opentelemetry/util/__init__.py b/opentelemetry-api/src/opentelemetry/util/__init__.py index 90d79492d2d..e6c36601596 100644 --- a/opentelemetry-api/src/opentelemetry/util/__init__.py +++ b/opentelemetry-api/src/opentelemetry/util/__init__.py @@ -18,8 +18,6 @@ from pkg_resources import iter_entry_points from opentelemetry.configuration import Configuration # type: ignore -from opentelemetry.metrics import MeterProvider -from opentelemetry.trace import TracerProvider logger = getLogger(__name__) @@ -35,7 +33,7 @@ def time_ns() -> int: return int(time.time() * 1e9) -def _load_provider(provider: str) -> Union[TracerProvider, MeterProvider]: +def _load_provider(provider: str) -> Union["TracerProvider", "MeterProvider"]: # type: ignore try: return next( # type: ignore iter_entry_points( From e33dcb3d173295caa0af6f41bb42be436c846d9e Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Wed, 8 Apr 2020 11:14:33 -0600 Subject: [PATCH 7/9] Fix mypy --- opentelemetry-api/src/opentelemetry/util/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opentelemetry-api/src/opentelemetry/util/__init__.py b/opentelemetry-api/src/opentelemetry/util/__init__.py index e6c36601596..8701d9ffbac 100644 --- a/opentelemetry-api/src/opentelemetry/util/__init__.py +++ b/opentelemetry-api/src/opentelemetry/util/__init__.py @@ -25,7 +25,7 @@ # this needs to be in the API. try: - time_ns = time.time_ns # type: ignore + time_ns = time.time_ns # Python versions < 3.7 except AttributeError: From 5ff920057af0a2f8b6c63b5c2fb88f736c4eb2e7 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 9 Apr 2020 13:20:32 -0600 Subject: [PATCH 8/9] Add support for non-defined attributes --- .../src/opentelemetry/configuration/__init__.py | 3 +++ .../tests/configuration/test_configuration.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py index ca5b01a7a41..e502bb4736d 100644 --- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py +++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py @@ -113,3 +113,6 @@ def __new__(cls) -> "Configuration": Configuration._instance = object.__new__(cls) return cls._instance + + def __getattr__(self, name): + return None diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py index 701269e849c..d5a63630912 100644 --- a/opentelemetry-api/tests/configuration/test_configuration.py +++ b/opentelemetry-api/tests/configuration/test_configuration.py @@ -45,6 +45,10 @@ def test_environment_variables(self): # type: ignore Configuration().tracer_provider, "tracer_provider" ) # pylint: disable=no-member + @patch.dict( + "os.environ", # type: ignore + {"OPENTELEMETRY_PYTHON_TRACER_PROVIDER": "tracer_provider"}, + ) def test_property(self): with self.assertRaises(AttributeError): Configuration().tracer_provider = "new_tracer_provider" @@ -52,3 +56,6 @@ def test_property(self): def test_slots(self): with self.assertRaises(AttributeError): Configuration().xyz = "xyz" # pylint: disable=assigning-non-slot + + def test_getattr(self): + Configuration().xyz is None From ff662b95dd9a24307ba0c1505091f188c22c23f1 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 9 Apr 2020 15:15:10 -0600 Subject: [PATCH 9/9] Adding more documentation --- opentelemetry-api/src/opentelemetry/configuration/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py index e502bb4736d..9b23c52bee6 100644 --- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py +++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py @@ -45,6 +45,10 @@ ``OPENTELEMETRY_PYTHON_METER_PROVIDER`` value is ``my_meter_provider``, then ``Configuration().meter_provider == "my_meter_provider"`` would be ``True``. +Non defined attributes will always return ``None``. This is intended to make it +easier to use the ``Configuration`` object in actual code, because it won't be +necessary to check for the attribute to be defined first. + Environment variables used by OpenTelemetry -------------------------------------------