Repository: census-instrumentation/opencensus-python
Pull request #200 (2018-06-04 23:56:14)

Fixing Non-Shared Measure to View Map


Patch:
diff --git a/opencensus/stats/execution_context.py b/opencensus/stats/execution_context.py
new file mode 100644
index 00000000..d5d3439
--- /dev/null
+++ b/opencensus/stats/execution_context.py
@@ -0,0 +1,29 @@
+# Copyright 2018, OpenCensus 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.
+
+import threading
+
+_thread_local = threading.local()
+
+
+def get_measure_to_view_map():
+    return getattr(_thread_local, 'measure_to_view_map', {})
+
+
+def set_measure_to_view_map(measure_to_view_map):
+    setattr(_thread_local, 'measure_to_view_map', measure_to_view_map)
+
+
+def clear():
+    _thread_local.__dict__.clear()
diff --git a/opencensus/stats/stats_recorder.py b/opencensus/stats/stats_recorder.py
index 4c11ebe..8ea04da 100644
--- a/opencensus/stats/stats_recorder.py
+++ b/opencensus/stats/stats_recorder.py
@@ -13,18 +13,19 @@
 # limitations under the License.
 
 from opencensus.stats.measurement_map import MeasurementMap
+from opencensus.stats.measure_to_view_map import MeasureToViewMap
+from opencensus.stats import execution_context
 
 
 class StatsRecorder(object):
     """Stats Recorder provides methods to record stats against tags
 
-    :type measure_to_view_map: :class: '~opencensus.stats.measure_to_view_map.
-                                        MeasureToViewMap'
-    :param measure_to_view_map: the measure to view map for the stats recorder
-
     """
-    def __init__(self, measure_to_view_map=None):
-        self.measure_to_view_map = measure_to_view_map or {}
+    def __init__(self):
+        if execution_context.get_measure_to_view_map() == {}:
+            execution_context.set_measure_to_view_map(MeasureToViewMap())
+
+        self.measure_to_view_map = execution_context.get_measure_to_view_map()
 
     def new_measurement_map(self):
         """Creates a new MeasurementMap in order to record stats
diff --git a/opencensus/stats/view_manager.py b/opencensus/stats/view_manager.py
index 7e104d7..19efba2 100644
--- a/opencensus/stats/view_manager.py
+++ b/opencensus/stats/view_manager.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 from opencensus.stats.measure_to_view_map import MeasureToViewMap
+from opencensus.stats import execution_context
 from datetime import datetime
 
 
@@ -21,7 +22,10 @@ class ViewManager(object):
     and receiving stats data as View Data"""
     def __init__(self):
         self.time = datetime.utcnow().isoformat() + 'Z'
-        self._measure_view_map = MeasureToViewMap()
+        if execution_context.get_measure_to_view_map() == {}:
+            execution_context.set_measure_to_view_map(MeasureToViewMap())
+
+        self._measure_view_map = execution_context.get_measure_to_view_map()
 
     @property
     def measure_to_view_map(self):


Test patch:
diff --git a/tests/unit/stats/test_stats_execution_context.py b/tests/unit/stats/test_stats_execution_context.py
new file mode 100644
index 00000000..44b1d0d
--- /dev/null
+++ b/tests/unit/stats/test_stats_execution_context.py
@@ -0,0 +1,35 @@
+# Copyright 2018, OpenCensus 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.
+
+import unittest
+
+from opencensus.stats import execution_context
+
+
+class TestExecutionContext(unittest.TestCase):
+
+    def tearDown(self):
+        execution_context.clear()
+
+    def test_get_and_set(self):
+        execution_context.clear()
+        execution_context.get_measure_to_view_map()
+        self.assertEqual({}, execution_context.get_measure_to_view_map())
+
+        measure_to_view_map = {'key1', 'val1'}
+        execution_context.set_measure_to_view_map(measure_to_view_map)
+
+        execution_context.get_measure_to_view_map()
+        self.assertEqual(measure_to_view_map,
+                         execution_context.get_measure_to_view_map())
diff --git a/tests/unit/stats/test_stats_recorder.py b/tests/unit/stats/test_stats_recorder.py
index c023c66..b5dbf55 100644
--- a/tests/unit/stats/test_stats_recorder.py
+++ b/tests/unit/stats/test_stats_recorder.py
@@ -16,25 +16,25 @@ import unittest
 import mock
 from opencensus.stats import stats_recorder as stats_recorder_module
 from opencensus.stats.measurement_map import MeasurementMap
+from opencensus.stats import execution_context
 
 
 class TestStatsRecorder(unittest.TestCase):
 
     def test_constructor_defaults(self):
-        stats_recorder = stats_recorder_module.StatsRecorder()
-
-        self.assertEqual({}, stats_recorder.measure_to_view_map)
+        execution_context.clear()
+        self.assertEqual({}, execution_context.get_measure_to_view_map())
 
-    def test_constructor_explicit(self):
-        measure_to_view_map = mock.Mock()
-        stats_recorder = stats_recorder_module.StatsRecorder(measure_to_view_map=measure_to_view_map)
+        stats_recorder = stats_recorder_module.StatsRecorder()
 
-        self.assertEqual(measure_to_view_map, stats_recorder.measure_to_view_map)
+        self.assertIsNotNone(execution_context.get_measure_to_view_map())
+        self.assertIs(stats_recorder.measure_to_view_map,
+                      execution_context.get_measure_to_view_map())
+        self.assertIsNotNone(stats_recorder.measure_to_view_map)
 
     def test_new_measurement_map(self):
-        measure_to_view_map = mock.Mock()
-        stats_recorder = stats_recorder_module.StatsRecorder(measure_to_view_map=measure_to_view_map)
+        measure_to_view_map = mock.Mock
+        stats_recorder = stats_recorder_module.StatsRecorder()
         measurement_map = stats_recorder.new_measurement_map()
 
-        self.assertEqual(measurement_map.measure_to_view_map, MeasurementMap(measure_to_view_map=measure_to_view_map).measure_to_view_map)
         self.assertEqual(measurement_map.measurement_map, MeasurementMap(measure_to_view_map=measure_to_view_map).measurement_map)
diff --git a/tests/unit/stats/test_view_manager.py b/tests/unit/stats/test_view_manager.py
index d57844f..9b7a41c 100644
--- a/tests/unit/stats/test_view_manager.py
+++ b/tests/unit/stats/test_view_manager.py
@@ -15,17 +15,31 @@
 import unittest
 import mock
 from opencensus.stats import view_manager as view_manager_module
+from opencensus.stats import execution_context
+from opencensus.stats.measure_to_view_map import MeasureToViewMap
 
 
 class TestViewManager(unittest.TestCase):
     def test_constructor(self):
+        execution_context.clear()
+        self.assertEqual({}, execution_context.get_measure_to_view_map())
+
         view_manager = view_manager_module.ViewManager()
 
         self.assertIsNotNone(view_manager.time)
-        self.assertIsNotNone(view_manager.measure_to_view_map)
+        self.assertEqual(view_manager.measure_to_view_map,
+                         execution_context.get_measure_to_view_map())
+
+        execution_context.clear()
+        measure_to_view_map = {'key1': 'val1'}
+        execution_context.set_measure_to_view_map(measure_to_view_map)
+        self.assertEqual(measure_to_view_map,
+                         execution_context.get_measure_to_view_map())
 
     def test_register_view(self):
         view = mock.Mock()
+        execution_context.clear()
+        execution_context.set_measure_to_view_map(MeasureToViewMap())
         view_manager = view_manager_module.ViewManager()
         view_manager.register_view(view=view)
 
@@ -37,6 +51,8 @@ class TestViewManager(unittest.TestCase):
 
     def test_get_view(self):
         view_name = mock.Mock()
+        execution_context.clear()
+        execution_context.set_measure_to_view_map(MeasureToViewMap())
         view_manager = view_manager_module.ViewManager()
         view_manager.get_view(view_name=view_name)
 
@@ -47,6 +63,8 @@ class TestViewManager(unittest.TestCase):
         self.assertTrue(view_manager_mock.get_view.called)
 
     def test_get_all_exported_views(self):
+        execution_context.clear()
+        execution_context.set_measure_to_view_map(MeasureToViewMap())
         view_manager = view_manager_module.ViewManager()
         exported_views = view_manager.get_all_exported_views()
         self.assertIsNotNone(exported_views)