Source code for brainscopypaste.conf

"""Manage settings from the :mod:`.settings` module, allowing overriding of
some values.

Use the :data:`settings` class instance from this module to access settings
from any other module: ``from brainscopypaste.conf import settings``. Note that
only uppercase variables from the :mod:`.settings` module are taken into
account, the rest is ignored.

"""


import logging
import importlib
from contextlib import contextmanager
import os
from tempfile import mkstemp

from brainscopypaste.utils import mkdirp


logger = logging.getLogger(__name__)


[docs]class Settings: """Hold all settings for the analysis, managing and proxying access to the :mod:`.settings` module. Only uppercase variables from the :mod:`.settings` module are taken into account, the rest is ignored. This class also lets you override values with a context manager to make testing easier. See the :meth:`override` and :meth:`file_override` methods for more details. Use the :data:`settings` instance of this class to access a singleton version of the settings for the whole analysis. Overridden values then appear overridden to all other modules (i.e. for all accesses) until the context manager is closed. """ def __init__(self): """Import the :mod:`.settings` module and check for folders to create.""" self.mod = importlib.import_module('brainscopypaste.settings') self._setup() for path in self.mod.paths_to_create: logger.debug("Checking for path '%s' to create", path) mkdirp(path)
[docs] def _setup(self): """Set uppercase variables from the :mod:`.settings` module as attributes on this instance.""" for setting in dir(self.mod): if setting.isupper(): setattr(self, setting, getattr(self.mod, setting))
@contextmanager
[docs] def override(self, *names_values): """Context manager that overrides setting values for the duration of the context. Use this method to override one or several setting values for a block of code, then have those settings go back to their default value. Very useful when writing tests. Parameters ---------- names_values : list of tuples List of `(name, value)` tuples defining which settings to override with what value. Setting names must already exist (you can't use this to create a new entry). Raises ------ ValueError If any of the `name` values in `names_values` is not an uppercase string or is not a known setting name. See Also -------- file_override Examples -------- Override MemeTracker filter settings for the duration of a test: >>> from brainscopypaste.conf import settings >>> with settings.override(('MT_FILTER_MIN_TOKENS', 2), ... ('MT_FILTER_MAX_DAYS, 50)): ... # Here: some test code using the overridden settings. >>> # `settings` is back to default here. """ for name, value in names_values: self._override(name, value) try: yield finally: self._setup()
@contextmanager
[docs] def file_override(self, *names): """Context manager that overrides a file setting by pointing it to an empty temporary file for the duration of the context. Some values in the :mod:`.settings` module are file paths, and you might want to easily override the `contents` of that file for a block of code. This method lets you do just that: it will create a temporary file for a setting you wish to override, point that setting to the new empty file, and clean up once the context closes. This is a shortcut for :meth:`override` when working on files whose contents you want to override. Parameters ---------- names : list of str List of setting names you want to override with temporary files. Raises ------ ValueError If any member of `names` is not an uppercase string or is not a known setting name. See Also -------- override Examples -------- Override the Age-of-Acquisition source file to e.g. test code that imports it as a word feature: >>> from brainscopypaste.conf import settings >>> with settings.file_override('AOA'): ... with open(settings.AOA, 'w') as aoa: ... # Write test content to the temporary AOA file. ... # Test your code on the temporary AOA content. >>> # `settings.AOA` is back to default here. """ filepaths = [mkstemp()[1] for name in names] try: with self.override(*zip(names, filepaths)): yield finally: for filepath in filepaths: os.remove(filepath)
[docs] def _override(self, name, value): """Override `name` with `value`, after some checks. The method checks that `name` is an uppercase string, and that it exists in the known settings. Use this when writing a context manager that wraps the operation in try/finally blocks, then restores the default behaviour. Parameters ---------- name : str Uppercase string denoting a known setting to be overridden. value : object Value to replace the setting with. Raises ------ ValueError If `name` is not an uppercase string or is not a known setting name. """ if not name.isupper(): raise ValueError('Setting names must be uppercase') if name not in dir(self.mod): raise ValueError('Unknown setting name') setattr(self, name, value)
#: Instance of the :class:`Settings` class that should be used to access #: settings. See that class's documentation for more information. settings = Settings()