Skip to content

Instantly share code, notes, and snippets.

@glenfant
Last active August 1, 2020 22:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glenfant/90004df7b2734c20b5741983d93745c4 to your computer and use it in GitHub Desktop.
Save glenfant/90004df7b2734c20b5741983d93745c4 to your computer and use it in GitHub Desktop.
How to provide a default configuration which individual points may be overriden in a custom configuration file (to improve and explain)
"""
================
euscans.settings
================
Provides settings customization and public configuration object
[DEPRECATED] I released the ``pyflexconfig`` package on PyPI that rationalizes and improves this boilerplate.
https://pypi.org/project/pyflexconfig/
"""
from argparse import Namespace
import logging.config
import os
from euscans import LOG
from .utils import execfile, CommandBase
this_directory = os.path.dirname(os.path.abspath(__file__))
default_settings_file = os.path.join(this_directory, 'defaultsettings.py')
assert os.path.isfile(default_settings_file), "Broken package: File '{}' is missing".format(default_settings_file)
def get_config(*files):
"""Provides a :class:`argparse.Namespace` configuration object that merges
all uppercase globals in Python ``files`` that don't start with '_'
Note:
Values from a file override values of same name provided by a previous file.
Args:
files (Iterable(str)): Paths to all config files
Returns:
argparse.Namespace: Merged configuration data
Examples:
Preparation
>>> import os, tempfile
>>> CONFIG1 = '''DATA1 = 10
... ignored = 100
... _IGNORED = 1000'''
>>> CONFIG2 = '''DATA1 = 100
... EXTRA = 100'''
>>> config1, path_config1 = tempfile.mkstemp('.py')
>>> with os.fdopen(config1, 'w') as config1:
... _ = config1.write(CONFIG1)
>>> config2, path_config2 = tempfile.mkstemp('.py')
>>> with os.fdopen(config2, 'w') as config2:
... _ = config2.write(CONFIG2)
Make the conf.
>>> conf = get_config(path_config1, path_config2)
>>> conf.DATA1
100
>>> conf.EXTRA
100
>>> hasattr(conf, 'ignored')
False
>>> hasattr(conf, '_IGNORED')
False
Cleaning...
>>> os.unlink(path_config1)
>>> os.unlink(path_config2)
"""
conf_globals = {}
conf_locals = {}
for file_ in files:
execfile(file_, conf_globals, conf_locals)
conf_locals = {name: value for name, value in conf_locals.items()
if name.isupper() and not name.startswith('_')}
return Namespace(**conf_locals)
conf = None
def bootstrap():
"""Bootstraps the global ``conf`` object
"""
global conf
# One shot function in process
if conf is not None:
return
is_file = os.path.isfile
# Standard settings
settings_files = (default_settings_file,)
# Custom settings
custom_settings_file = os.environ.get('EUSCANS_CONFIG_FILE')
if custom_settings_file is not None:
assert is_file(custom_settings_file), "Custom settings file '{}' does not exist".format(custom_settings_file)
settings_files += (custom_settings_file,)
LOG.debug("Reading the settings from %s", settings_files)
conf = get_config(*settings_files)
logging.config.dictConfig(conf.LOGGING)
return
class DumpConfigCommand(CommandBase):
name = 'dumpsettings'
help = "Prints the default settings to stdout. Which may be edited to make your custom settings"
def run(self, args, extra_args):
with open(default_settings_file, 'r') as fh:
for line in fh:
stripped = line.strip()
if stripped.startswith('#') or len(stripped) == 0:
print(line, end='')
else:
print('#'+line, end='')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment