From 7db6a2604d72901408f6e47018d4839fe0959258 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Wed, 28 Mar 2018 17:26:26 -0400 Subject: [PATCH] use environment variable to control file location probing If OSLO_CONFIG_SHOW_CODE_LOCATIONS is set to any non-empty string it will turn on the ability to see which file has the definition of an Opt or where set_default() was invoked. Change-Id: Ie705014dcf331e3c6b3367d2fefbfb9acc091799 Signed-off-by: Doug Hellmann --- doc/source/reference/helpers.rst | 17 +++++++++++ oslo_config/cfg.py | 39 ++++++++++++++------------ oslo_config/tests/test_get_location.py | 18 ++++++------ 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/doc/source/reference/helpers.rst b/doc/source/reference/helpers.rst index e2d5cb40..4a36dc89 100644 --- a/doc/source/reference/helpers.rst +++ b/doc/source/reference/helpers.rst @@ -6,3 +6,20 @@ Helper Functions .. autofunction:: find_config_files .. autofunction:: set_defaults + +Showing detailed locations for configuration settings +----------------------------------------------------- + +``oslo.config`` can track the location in application and library code +where an option is defined, defaults are set, or values are +overridden. This feature is disabled by default because it is +expensive and incurs a significant performance penalty, but it can be +useful for developers tracing down issues with configuration option +definitions. + +To turn on detailed location tracking, set the environment variable +``OSLO_CONFIG_SHOW_CODE_LOCATIONS`` to any non-empty value (for +example, ``"1"`` or ``"yes, please"``) before starting the +application, test suite, or script. Then use +:func:`ConfigOpts.get_location` to access the location data for the +option. diff --git a/oslo_config/cfg.py b/oslo_config/cfg.py index b11e8f6c..4cedb693 100644 --- a/oslo_config/cfg.py +++ b/oslo_config/cfg.py @@ -489,7 +489,7 @@ import copy import errno import functools import glob -# import inspect +import inspect import itertools import logging import os @@ -804,6 +804,10 @@ def _is_opt_registered(opts, opt): return False +_show_caller_details = bool(os.environ.get( + 'OSLO_CONFIG_SHOW_CODE_LOCATIONS')) + + def _get_caller_detail(n=2): """Return a string describing where this is being called from. @@ -811,23 +815,22 @@ def _get_caller_detail(n=2): :type n: int :returns: str """ - return None - # FIXME(dhellmann): We need to look at the performance issues with - # doing this for every Opt instance. - # s = inspect.stack()[:n + 1] - # try: - # frame = s[n] - # try: - # return frame[1] - # # WARNING(dhellmann): Using frame.lineno to include the - # # line number in the return value causes some sort of - # # memory or stack corruption that manifests in values not - # # being cleaned up in the cfgfilter tests. - # # return '%s:%s' % (frame[1], frame[2]) - # finally: - # del frame - # finally: - # del s + if not _show_caller_details: + return None + s = inspect.stack()[:n + 1] + try: + frame = s[n] + try: + return frame[1] + # WARNING(dhellmann): Using frame.lineno to include the + # line number in the return value causes some sort of + # memory or stack corruption that manifests in values not + # being cleaned up in the cfgfilter tests. + # return '%s:%s' % (frame[1], frame[2]) + finally: + del frame + finally: + del s def set_defaults(opts, **kwargs): diff --git a/oslo_config/tests/test_get_location.py b/oslo_config/tests/test_get_location.py index 0c4e0ec5..9c7c2af0 100644 --- a/oslo_config/tests/test_get_location.py +++ b/oslo_config/tests/test_get_location.py @@ -51,6 +51,12 @@ class GetLocationTestCase(base.BaseTestCase): def setUp(self): super(GetLocationTestCase, self).setUp() + + def _clear(): + cfg._show_caller_details = False + self.addCleanup(_clear) + cfg._show_caller_details = True + self.conf = TestConfigOpts() self.normal_opt = cfg.StrOpt( 'normal_opt', @@ -70,8 +76,7 @@ class GetLocationTestCase(base.BaseTestCase): cfg.Locations.opt_default, loc.location, ) - self.assertIsNone(loc.detail) - # self.assertIn('test_get_location.py', loc.detail) + self.assertIn('test_get_location.py', loc.detail) def test_set_default_on_config_opt(self): self.conf.set_default('normal_opt', self.id()) @@ -81,8 +86,7 @@ class GetLocationTestCase(base.BaseTestCase): cfg.Locations.set_default, loc.location, ) - self.assertIsNone(loc.detail) - # self.assertIn('test_get_location.py', loc.detail) + self.assertIn('test_get_location.py', loc.detail) def test_set_defaults_func(self): cfg.set_defaults([self.normal_opt], normal_opt=self.id()) @@ -92,8 +96,7 @@ class GetLocationTestCase(base.BaseTestCase): cfg.Locations.set_default, loc.location, ) - self.assertIsNone(loc.detail) - # self.assertIn('test_get_location.py', loc.detail) + self.assertIn('test_get_location.py', loc.detail) def test_set_override(self): self.conf.set_override('normal_opt', self.id()) @@ -103,8 +106,7 @@ class GetLocationTestCase(base.BaseTestCase): cfg.Locations.set_override, loc.location, ) - self.assertIsNone(loc.detail) - # self.assertIn('test_get_location.py', loc.detail) + self.assertIn('test_get_location.py', loc.detail) def test_user_cli(self): filename = self._write_opt_to_tmp_file(