From 7bf988fb1db2c926b7fd1dc7e1e6163fa4a9b7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Thu, 16 Aug 2018 15:48:22 +0200 Subject: [PATCH 1/6] settings: Change the way in-tree is evaluated MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently in-tree is evaluated based on presence of config files, but in reallity the "settings.intree" used to get tests path. Let's use it to check for in-tree detection, which should better correspond to how this value is being used. Signed-off-by: Lukáš Doktor --- avocado/core/settings.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/avocado/core/settings.py b/avocado/core/settings.py index 13c58289ea..8b09adf05f 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -140,9 +140,15 @@ def __init__(self, config_path=None): :param config_path: Path to a config file. Useful for unittesting. """ self.config = ConfigParser.ConfigParser() - self.intree = False self.config_paths = [] self.config_paths_failed = [] + _source_tree_root = os.path.dirname(os.path.dirname(os.path.dirname( + sys.modules[__name__].__file__))) + # In case "examples" file exists in root, we are running from tree + if os.path.exists(os.path.join(_source_tree_root, 'examples')): + self.intree = True + else: + self.intree = False if config_path is None: if 'VIRTUAL_ENV' in os.environ: cfg_dir = os.path.join(os.environ['VIRTUAL_ENV'], 'etc') @@ -154,7 +160,6 @@ def __init__(self, config_path=None): _config_dir_system = os.path.join(cfg_dir, 'avocado') _config_dir_system_extra = os.path.join(cfg_dir, 'avocado', 'conf.d') _config_dir_local = os.path.join(user_dir, '.config', 'avocado') - _source_tree_root = os.path.join(sys.modules[__name__].__file__, "..", "..", "..") _config_path_intree = os.path.join(os.path.abspath(_source_tree_root), 'avocado', 'etc', 'avocado') _config_path_intree_extra = os.path.join(_config_path_intree, 'conf.d') @@ -184,7 +189,6 @@ def __init__(self, config_path=None): if config_intree_extra: for extra_file in glob.glob(os.path.join(_config_path_intree_extra, '*.conf')): self.process_config_path(extra_file) - self.intree = True # Override with system config if config_system: self.process_config_path(config_path_system) From d4a028aa332c773d39d58702385d3a8ab8e9507b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Thu, 16 Aug 2018 15:55:45 +0200 Subject: [PATCH 2/6] settings: Use pkg_resources to get pkg/intree configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the intree config exists, it's the same resource as pkg config. Let's only process the pkg config and use pkg_resources properly to list the directory with extra config files. Signed-off-by: Lukáš Doktor --- avocado/core/settings.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/avocado/core/settings.py b/avocado/core/settings.py index 8b09adf05f..27dd7a761c 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -25,7 +25,10 @@ except ImportError: import configparser as ConfigParser -from pkg_resources import resource_exists, resource_filename +from pkg_resources import resource_exists +from pkg_resources import resource_filename +from pkg_resources import resource_isdir +from pkg_resources import resource_listdir from six import string_types from ..utils import path @@ -160,34 +163,36 @@ def __init__(self, config_path=None): _config_dir_system = os.path.join(cfg_dir, 'avocado') _config_dir_system_extra = os.path.join(cfg_dir, 'avocado', 'conf.d') _config_dir_local = os.path.join(user_dir, '.config', 'avocado') - _config_path_intree = os.path.join(os.path.abspath(_source_tree_root), - 'avocado', 'etc', 'avocado') - _config_path_intree_extra = os.path.join(_config_path_intree, 'conf.d') config_filename = 'avocado.conf' config_path_system = os.path.join(_config_dir_system, config_filename) config_path_local = os.path.join(_config_dir_local, config_filename) - config_path_intree = os.path.join(_config_path_intree, config_filename) config_system = os.path.exists(config_path_system) config_system_extra = os.path.exists(_config_dir_system_extra) config_local = os.path.exists(config_path_local) - config_intree = os.path.exists(config_path_intree) - config_intree_extra = os.path.exists(_config_path_intree_extra) - config_pkg_base = os.path.join('etc', config_filename) + config_pkg_base = os.path.join('etc', 'avocado', config_filename) config_pkg = resource_exists('avocado', config_pkg_base) config_path_pkg = resource_filename('avocado', config_pkg_base) + _config_pkg_extra = os.path.join('etc', 'avocado', 'conf.d') + if resource_isdir('avocado', _config_pkg_extra): + config_pkg_extra = resource_listdir('avocado', + _config_pkg_extra) + _config_pkg_extra = resource_filename('avocado', _config_pkg_extra) + else: + config_pkg_extra = None if not (config_system or config_local or - config_intree or config_pkg): + config_pkg): raise ConfigFileNotFound([config_path_system, config_path_local, - config_path_intree, config_path_pkg]) - # First try in-tree config - if config_intree: - self.process_config_path(config_path_intree) - if config_intree_extra: - for extra_file in glob.glob(os.path.join(_config_path_intree_extra, '*.conf')): + # First try pkg/in-tree config + if config_pkg: + self.process_config_path(config_path_pkg) + if config_pkg_extra: + for extra_file in (os.path.join(_config_pkg_extra, _) + for _ in config_pkg_extra + if _.endswith('.conf')): self.process_config_path(extra_file) # Override with system config if config_system: From c184d3b004a3e760066678b7ffc29e4d6dddf17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Thu, 16 Aug 2018 16:01:07 +0200 Subject: [PATCH 3/6] settings: Improve docstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This docstring is a bit cryptic, let's makes it 100% clear it's only used by unittests and the "--config" argument uses different approach. Signed-off-by: Lukáš Doktor --- avocado/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avocado/core/settings.py b/avocado/core/settings.py index 27dd7a761c..c0d30f6d0d 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -215,7 +215,6 @@ def __init__(self, config_path=None): else: self.process_config_path(config_path_local) else: - # Unittests self.process_config_path(config_path) def process_config_path(self, pth): @@ -224,6 +223,7 @@ def process_config_path(self, pth): self.config_paths += read_configs else: self.config_paths_failed.append(pth) + # Only used by unittests (the --config parses the file later) def _handle_no_value(self, section, key, default): """ From ae437e424f05d6acec82eb9389736fc9dc718447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Thu, 16 Aug 2018 16:33:33 +0200 Subject: [PATCH 4/6] settings: Change the way config files are processed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally we only processed files that existed and when they were read we updated list of config locations and in case of no return list of failed-to-read paths. Anyway the "config.read" function accepts list of config files and returns list of successfully parsed files. Missing files are automatically skipped and broken files raise exception, so let's just simply create list of to-be-read files and feed it into the "config.read" function. As a result the list of not-read files actually works, still in this version it's only displayed on "avocado config", where users can get notion of all possible locations and in "avocado run" only parsed files are displayed as it doesn't seem important enough to log them there to me. Signed-off-by: Lukáš Doktor --- avocado/core/job.py | 4 --- avocado/core/settings.py | 57 ++++++++++++++--------------------- avocado/plugins/config.py | 14 ++++----- docs/source/Configuration.rst | 49 ++++++++++++++++-------------- 4 files changed, 56 insertions(+), 68 deletions(-) diff --git a/avocado/core/job.py b/avocado/core/job.py index 29c7ba99dc..25c7bb1acf 100644 --- a/avocado/core/job.py +++ b/avocado/core/job.py @@ -383,10 +383,6 @@ def _log_avocado_config(): LOG_JOB.info('Config files read (in order):') for cfg_path in settings.config_paths: LOG_JOB.info(cfg_path) - if settings.config_paths_failed: - LOG_JOB.info('Config files failed to read (in order):') - for cfg_path in settings.config_paths_failed: - LOG_JOB.info(cfg_path) LOG_JOB.info('') LOG_JOB.info('Avocado config:') diff --git a/avocado/core/settings.py b/avocado/core/settings.py index c0d30f6d0d..8f485af08c 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -25,7 +25,6 @@ except ImportError: import configparser as ConfigParser -from pkg_resources import resource_exists from pkg_resources import resource_filename from pkg_resources import resource_isdir from pkg_resources import resource_listdir @@ -144,7 +143,7 @@ def __init__(self, config_path=None): """ self.config = ConfigParser.ConfigParser() self.config_paths = [] - self.config_paths_failed = [] + self.all_config_paths = [] _source_tree_root = os.path.dirname(os.path.dirname(os.path.dirname( sys.modules[__name__].__file__))) # In case "examples" file exists in root, we are running from tree @@ -168,11 +167,7 @@ def __init__(self, config_path=None): config_path_system = os.path.join(_config_dir_system, config_filename) config_path_local = os.path.join(_config_dir_local, config_filename) - config_system = os.path.exists(config_path_system) - config_system_extra = os.path.exists(_config_dir_system_extra) - config_local = os.path.exists(config_path_local) config_pkg_base = os.path.join('etc', 'avocado', config_filename) - config_pkg = resource_exists('avocado', config_pkg_base) config_path_pkg = resource_filename('avocado', config_pkg_base) _config_pkg_extra = os.path.join('etc', 'avocado', 'conf.d') if resource_isdir('avocado', _config_pkg_extra): @@ -180,28 +175,20 @@ def __init__(self, config_path=None): _config_pkg_extra) _config_pkg_extra = resource_filename('avocado', _config_pkg_extra) else: - config_pkg_extra = None - if not (config_system or config_local or - config_pkg): - raise ConfigFileNotFound([config_path_system, - config_path_local, - config_path_pkg]) + config_pkg_extra = [] # First try pkg/in-tree config - if config_pkg: - self.process_config_path(config_path_pkg) - if config_pkg_extra: - for extra_file in (os.path.join(_config_pkg_extra, _) - for _ in config_pkg_extra - if _.endswith('.conf')): - self.process_config_path(extra_file) + self.all_config_paths.append(config_path_pkg) + for extra_file in (os.path.join(_config_pkg_extra, _) + for _ in config_pkg_extra + if _.endswith('.conf')): + self.all_config_paths.append(extra_file) # Override with system config - if config_system: - self.process_config_path(config_path_system) - if config_system_extra: - for extra_file in glob.glob(os.path.join(_config_dir_system_extra, '*.conf')): - self.process_config_path(extra_file) + self.all_config_paths.append(config_path_system) + for extra_file in glob.glob(os.path.join(_config_dir_system_extra, + '*.conf')): + self.all_config_paths.append(extra_file) # And the local config - if not config_local: + if not os.path.exists(config_path_local): try: path.init_dir(_config_dir_local) with open(config_path_local, 'w') as config_local_fileobj: @@ -212,18 +199,20 @@ def __init__(self, config_path=None): config_local_fileobj.write(content) except IOError: # Some users can't write it (docker) pass - else: - self.process_config_path(config_path_local) + self.all_config_paths.append(config_path_local) else: - self.process_config_path(config_path) + # Only used by unittests (the --config parses the file later) + self.all_config_paths.append(config_path) + self.config_paths = self.config.read(self.all_config_paths) + if not self.config_paths: + raise ConfigFileNotFound(self.all_config_paths) def process_config_path(self, pth): - read_configs = self.config.read(pth) - if read_configs: - self.config_paths += read_configs - else: - self.config_paths_failed.append(pth) - # Only used by unittests (the --config parses the file later) + """ + Update list of config paths and process the given pth + """ + self.all_config_paths.append(pth) + self.config_paths.extend(self.config.read(pth)) def _handle_no_value(self, section, key, default): """ diff --git a/avocado/plugins/config.py b/avocado/plugins/config.py index 32e693f3f7..68590217e9 100644 --- a/avocado/plugins/config.py +++ b/avocado/plugins/config.py @@ -37,13 +37,13 @@ def configure(self, parser): 'Current: %(default)s') def run(self, args): - LOG_UI.info('Config files read (in order):') - for cfg_path in settings.config_paths: - LOG_UI.debug(' %s', cfg_path) - if settings.config_paths_failed: - LOG_UI.error('\nConfig files that failed to read:') - for cfg_path in settings.config_paths_failed: - LOG_UI.error(' %s', cfg_path) + LOG_UI.info("Config files read (in order, '*' means the file exists " + "and had been read):") + for cfg_path in settings.all_config_paths: + if cfg_path in settings.config_paths: + LOG_UI.debug(' * %s', cfg_path) + else: + LOG_UI.debug(' %s', cfg_path) LOG_UI.debug("") if not args.datadir: blength = 0 diff --git a/docs/source/Configuration.rst b/docs/source/Configuration.rst index 4a8c181231..154badd613 100644 --- a/docs/source/Configuration.rst +++ b/docs/source/Configuration.rst @@ -72,7 +72,26 @@ So the file parsing order is: * ``/etc/avocado/conf.d/*.conf`` * ``~/.config/avocado/avocado.conf`` -In this order, meaning that what you set on your local config file may override what's defined in the system wide files. +You can see the actual set of files/location by using ``avocado config`` +which uses ``*`` to mark existing and used files:: + + $ avocado config + Config files read (in order, '*' means the file exists and had been read): + * /etc/avocado/avocado.conf + * /etc/avocado/conf.d/resultsdb.conf + * /etc/avocado/conf.d/result_upload.conf + * /etc/avocado/conf.d/jobscripts.conf + * /etc/avocado/conf.d/gdb.conf + /home/medic/.config/avocado/avocado.conf + + Section.Key Value + datadir.paths.base_dir /var/lib/avocado + datadir.paths.test_dir /usr/share/doc/avocado/tests + ... + + +Where the lower config files override values of the upper files and +the ``/home/medic/.config/avocado/avocado.conf`` file missing. .. note:: Please note that if avocado is running from git repos, those files will be ignored in favor of in tree configuration files. This is something that would normally only affect people developing avocado, and if you are in doubt, ``avocado config`` will tell you exactly which files are being used in any given situation. .. note:: When avocado runs inside virtualenv than path for global config files is also changed. For example, `avocado.conf` comes from the virual-env path `venv/etc/avocado/avocado.conf`. @@ -93,26 +112,6 @@ example), we established the following order of precedence for variables (from l So the least important value comes from the library or test code default, going all the way up to the test parameters system. -Config plugin -============= - -A configuration plugin is provided for users that wish to quickly see what's defined in all sections of their Avocado -configuration, after all the files are parsed in their correct resolution order. Example:: - - $ avocado config - Config files read (in order): - /etc/avocado/avocado.conf - $HOME/.config/avocado/avocado.conf - - Section.Key Value - runner.base_dir /var/lib/avocado - runner.test_dir /usr/share/doc/avocado/tests - runner.data_dir /var/lib/avocado/data - runner.logs_dir ~/avocado/job-results - -The command also shows the order in which your config files were parsed, giving you a better understanding of -what's going on. The Section.Key nomenclature was inspired in ``git config --list`` output. - Avocado Data Directories ======================== @@ -132,8 +131,12 @@ it will give you an output similar to the one seen below:: $ avocado config --datadir Config files read (in order): - /etc/avocado/avocado.conf - $HOME/.config/avocado/avocado.conf + * /etc/avocado/avocado.conf + * /etc/avocado/conf.d/resultsdb.conf + * /etc/avocado/conf.d/result_upload.conf + * /etc/avocado/conf.d/jobscripts.conf + * /etc/avocado/conf.d/gdb.conf + $HOME/.config/avocado/avocado.conf Avocado replaces config dirs that can't be accessed with sensible defaults. Please edit your local config From b4b3116b49366bdf515edff7bd2a4d3180473c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Fri, 17 Aug 2018 17:49:26 +0200 Subject: [PATCH 5/6] Add support for SettingsPlugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this implementations plugins will be able to modify the path list for "avocado.core.settings". Mainly it should be used to register extra configuration, but users should also be able to reorder/remove/modify the path to default config files. Signed-off-by: Lukáš Doktor --- avocado/core/dispatcher.py | 6 ++++ avocado/core/plugin_interfaces.py | 16 +++++++++ avocado/core/settings.py | 24 ++++++++++++++ docs/source/Configuration.rst | 55 ++++++++++++++++++++++++++++--- 4 files changed, 97 insertions(+), 4 deletions(-) diff --git a/avocado/core/dispatcher.py b/avocado/core/dispatcher.py index fdb21e6bec..06f51a6cc3 100644 --- a/avocado/core/dispatcher.py +++ b/avocado/core/dispatcher.py @@ -21,10 +21,16 @@ from .settings import settings from .settings import SettingsError +from .settings import SettingsDispatcher from .output import LOG_UI from ..utils import stacktrace +#: Settings dispatcher is simplified and has to be available before the +#: settings object. Let's include it here for consistency +SettingsDispatcher = SettingsDispatcher + + class Dispatcher(EnabledExtensionManager): """ diff --git a/avocado/core/plugin_interfaces.py b/avocado/core/plugin_interfaces.py index e6950baa40..160383bb15 100644 --- a/avocado/core/plugin_interfaces.py +++ b/avocado/core/plugin_interfaces.py @@ -25,6 +25,22 @@ class Plugin(object): """ +class Settings(Plugin): + + """ + Base plugin to allow modifying settings. + + Currently it only supports to extend/modify the default list of + paths to config files. + """ + + @abc.abstractmethod + def adjust_settings_paths(self, paths): + """ + Entry point where plugin can modify the list of configuration paths + """ + + class CLI(Plugin): """ diff --git a/avocado/core/settings.py b/avocado/core/settings.py index 8f485af08c..c35a336279 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -29,10 +29,28 @@ from pkg_resources import resource_isdir from pkg_resources import resource_listdir from six import string_types +from stevedore import ExtensionManager from ..utils import path +class SettingsDispatcher(ExtensionManager): + + """ + Dispatchers that allows plugins to modify settings + + It's not the standard "avocado.core.dispatcher" because that one depends + on settings. This dispatcher is the bare-stevedore dispatcher which is + executed before settings is parsed. + """ + + def __init__(self): + super(SettingsDispatcher, self).__init__('avocado.plugins.settings', + invoke_on_load=True, + invoke_kwds={}, + propagate_map_exceptions=True) + + class SettingsError(Exception): """ Base settings error. @@ -199,6 +217,12 @@ def __init__(self, config_path=None): config_local_fileobj.write(content) except IOError: # Some users can't write it (docker) pass + # Allow plugins to modify/extend the list of configs + dispatcher = SettingsDispatcher() + if dispatcher.extensions: + dispatcher.map_method('adjust_settings_paths', + self.all_config_paths) + # Register user config as last to always take precedence self.all_config_paths.append(config_path_local) else: # Only used by unittests (the --config parses the file later) diff --git a/docs/source/Configuration.rst b/docs/source/Configuration.rst index 154badd613..ae3ad8dbc0 100644 --- a/docs/source/Configuration.rst +++ b/docs/source/Configuration.rst @@ -49,10 +49,54 @@ will read the config files present in the git repos, and will ignore the system Plugin config files =================== -Plugins can also be configured by config files. In order to not disturb the main Avocado config file, those plugins, -if they wish so, may install additional config files to ``/etc/avocado/conf.d/[pluginname].conf``, that will be parsed -after the system wide config file. Users can override those values as well at the local config file level. -Considering the config for the hypothethical plugin ``salad``: +There are two ways to extend settings of extra plugin configuration. Plugins +can extend the list of files parsed by ``Settings`` object by using +``avocado.plugins.settings`` entry-point (Python-way) or they can +simply drop the individual config files into ``/etc/avocado/conf.d`` +(linux/posix-way). + +`avocado.plugins.settings` +-------------------------- + +This entry-point uses ``avocado.core.plugin_interfaces.Settings``-like object +to extend the list of parsed files. It only accepts individual files, but +you can use something like ``glob.glob("*.conf")`` to add all config files +inside a directory. + +You need to create the plugin (eg. ``my_plugin/settings.py``):: + + from avocado.core.plugin_interfaces import Settings + + class MyPluginSettings(Settings): + def adjust_settings_paths(self, paths): + paths.extend(glob.glob("/etc/my_plugin/conf.d/*.conf")) + + +And register it in your ``setup.py`` entry-points:: + + from setuptools import setup + ... + setup(name="my-plugin", + entry_points={ + 'avocado.plugins.settings': [ + "my-plugin-settings = my_plugin.settings.MyPluginSettings", + ], + ... + +Which extends the list of files to be parsed by settings object. Note this +has to be executed early in the code so try to keep the required deps +minimal (for example the `avocado.core.settings.settings` is not yet +available). + +`/etc/avocado/conf.d` +--------------------- + +In order to not disturb the main Avocado config file, those plugins, +if they wish so, may install additional config files to +``/etc/avocado/conf.d/[pluginname].conf``, that will be parsed +after the system wide config file. Users can override those values +as well at the local config file level. Considering the config for +the hypothethical plugin ``salad``: .. code-block:: ini @@ -70,6 +114,7 @@ So the file parsing order is: * ``/etc/avocado/avocado.conf`` * ``/etc/avocado/conf.d/*.conf`` +* ``avocado.plugins.settings`` plugins (but they can insert to any location) * ``~/.config/avocado/avocado.conf`` You can see the actual set of files/location by using ``avocado config`` @@ -82,6 +127,8 @@ which uses ``*`` to mark existing and used files:: * /etc/avocado/conf.d/result_upload.conf * /etc/avocado/conf.d/jobscripts.conf * /etc/avocado/conf.d/gdb.conf + * /etc/avocado_vt/conf.d/vt.conf + * /etc/avocado_vt/conf.d/vt_joblock.conf /home/medic/.config/avocado/avocado.conf Section.Key Value From 49439bc427c638b85e9dfe69b5f65ffb6e542fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Doktor?= Date: Mon, 11 Feb 2019 19:26:44 +0100 Subject: [PATCH 6/6] core.settings: Pylint/style fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some pylint fixes and removal of not necessary import. Signed-off-by: Lukáš Doktor --- avocado/core/settings.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/avocado/core/settings.py b/avocado/core/settings.py index c35a336279..e1b3ed8daf 100644 --- a/avocado/core/settings.py +++ b/avocado/core/settings.py @@ -17,7 +17,6 @@ """ import ast import os -import sys import glob try: @@ -116,27 +115,25 @@ def convert_value_type(value, value_type): value_type = str # if length of string is zero then return None - if len(sval) == 0: + if not sval: if value_type == str: return "" - elif value_type == os.path.expanduser: + if value_type == os.path.expanduser: return "" - elif value_type == bool: + if value_type == bool: return False - elif value_type == int: + if value_type == int: return 0 - elif value_type == float: + if value_type == float: return 0.0 - elif value_type == list: + if value_type == list: return [] - else: - return None + return None if value_type == bool: if sval.lower() == "false": return False - else: - return True + return True if value_type == list: return ast.literal_eval(sval) @@ -163,12 +160,10 @@ def __init__(self, config_path=None): self.config_paths = [] self.all_config_paths = [] _source_tree_root = os.path.dirname(os.path.dirname(os.path.dirname( - sys.modules[__name__].__file__))) + __file__))) # In case "examples" file exists in root, we are running from tree - if os.path.exists(os.path.join(_source_tree_root, 'examples')): - self.intree = True - else: - self.intree = False + self.intree = bool(os.path.exists(os.path.join(_source_tree_root, + 'examples'))) if config_path is None: if 'VIRTUAL_ENV' in os.environ: cfg_dir = os.path.join(os.environ['VIRTUAL_ENV'], 'etc')