Repository: open-telemetry/opentelemetry-python

Issue:
Using exclude paths for all instances of a Flask app
This is less a feature request than a question about how to use `OPENTELEMETRY_PYTHON_FLASK_EXCLUDED_HOSTS` to exclude routes from being traced for all instances of a flask application (i.e. regardless of host being deployed to). I initially thought something like below would work -

```python
from os import environ
environ["OPENTELEMETRY_PYTHON_FLASK_EXCLUDED_HOSTS"] = "/route1,/route2"
```

But it appears that this would be executed after the `Configuration` singleton gets initialized, and won't get picked up. Calling `configuration.Configuration._reset()` after setting `environ` seems like a hack to make this work but the docs state it's not for production code and it feels hacky as well.

Of course setting the environment on the deployed to host would solve this. The issue is that in some instances we may have a flask application that has e.g. a ping route that should never be traced, and we may not have control over the environment variables of the host being deployed to (so the app has to somehow set the exclude paths). So I suppose my question is, can the application programmatically set it's own exclude paths? Thanks in advance for the help.



Discussion:
Calling `Configuration()` explicitly for the first time would initialize the `Configuration` singleton, so you should do this after the `environ[...]` line.
EDIT: Sorry I didn't read your question carefully. It seems like we should be implementing a `set` method for the Configuration API.
Yes a `set` method would work great for this case, thanks!

Resolving patch:
diff --git a/opentelemetry-api/src/opentelemetry/configuration/__init__.py b/opentelemetry-api/src/opentelemetry/configuration/__init__.py
--- a/opentelemetry-api/src/opentelemetry/configuration/__init__.py
+++ b/opentelemetry-api/src/opentelemetry/configuration/__init__.py
@@ -142,19 +142,18 @@ def __new__(cls) -> "Configuration":
     def __getattr__(self, name: str) -> Optional[ConfigValue]:
         return self._config_map.get(name)
 
-    def __setattr__(self, key: str, val: ConfigValue) -> None:
-        if key == "_config_map":
-            super().__setattr__(key, val)
+    def __setattr__(self, name: str, value: ConfigValue) -> None:
+        if name not in self._config_map.keys():
+            self._config_map[name] = value
         else:
-            raise AttributeError(key)
+            raise AttributeError(name)
 
     def get(self, name: str, default: _T) -> _T:
         """Use this typed method for dynamic access instead of `getattr`
 
         :rtype: str or bool or int or float or None
         """
-        val = self._config_map.get(name, default)
-        return val
+        return self._config_map.get(name, default)
 
     @classmethod
     def _reset(cls) -> None:


Test patch:
diff --git a/opentelemetry-api/tests/configuration/test_configuration.py b/opentelemetry-api/tests/configuration/test_configuration.py
--- a/opentelemetry-api/tests/configuration/test_configuration.py
+++ b/opentelemetry-api/tests/configuration/test_configuration.py
@@ -39,7 +39,7 @@ def test_singleton(self) -> None:
             "OPENTELEMETRY_PTHON_TRACEX_PROVIDER": "tracex_provider",
         },
     )
-    def test_environment_variables(self):
+    def test_environment_variables(self) -> None:
         self.assertEqual(
             Configuration().METER_PROVIDER, "meter_provider"
         )  # pylint: disable=no-member
@@ -58,13 +58,16 @@ def test_environment_variables(self):
         "os.environ",  # type: ignore
         {"OPENTELEMETRY_PYTHON_TRACER_PROVIDER": "tracer_provider"},
     )
-    def test_property(self):
+    def test_property(self) -> None:
         with self.assertRaises(AttributeError):
             Configuration().TRACER_PROVIDER = "new_tracer_provider"
 
-    def test_slots(self) -> None:
+    def test_set_once(self) -> None:
+
+        Configuration().XYZ = "xyz"
+
         with self.assertRaises(AttributeError):
-            Configuration().XYZ = "xyz"  # pylint: disable=assigning-non-slot
+            Configuration().XYZ = "abc"  # pylint: disable=assigning-non-slot
 
     def test_getattr(self) -> None:
         # literal access