Source code for clay.settings

"""
Application settings manager.
"""
from threading import Lock
import os
import copy
import errno
import yaml
import appdirs
import pkg_resources


[docs]class _SettingsEditor(dict): """ Thread-safe settings editor context manager. For example see :py:meth:`~._Settings.edit`. """ _lock = Lock()
[docs] def __init__(self, original_config, commit_callback): super(_SettingsEditor, self).__init__() _SettingsEditor._lock.acquire() self._commit_callback = commit_callback self.update(copy.deepcopy(original_config))
def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_tb): _SettingsEditor._lock.release() if exc_tb is None: self._commit_callback(self) else: # TODO: Handle this pass
[docs]class _Settings(object): """ Settings management class. """
[docs] def __init__(self): self._config = {} self._default_config = {} self._cached_files = set() self._config_dir = None self._config_file_path = None self._cache_dir = None self._ensure_directories() self._load_config() self._load_cache()
[docs] def _ensure_directories(self): """ Create config dir, config file & cache dir if they do not exist yet. """ self._config_dir = appdirs.user_config_dir('clay', 'Clay') self._config_file_path = os.path.join(self._config_dir, 'config.yaml') self._colours_file_path = os.path.join(self._config_dir, 'colours.yaml') try: os.makedirs(self._config_dir) except OSError as error: if error.errno != errno.EEXIST: raise self._cache_dir = appdirs.user_cache_dir('clay', 'Clay') try: os.makedirs(self._cache_dir) except OSError as error: if error.errno != errno.EEXIST: raise if not os.path.exists(self._config_file_path): with open(self._config_file_path, 'w') as settings_file: settings_file.write('{}')
[docs] def _load_config(self): """ Read config from file. """ with open(self._config_file_path, 'r') as settings_file: self._config = yaml.load(settings_file.read()) # Load the configuration from Setuptools' ResourceManager API self._default_config = yaml.load(pkg_resources.resource_string(__name__, "config.yaml")) # We only either the user colour or the default colours to ease parsing logic. if os.path.exists(self._colours_file_path): with open(self._colours_file_path, 'r') as colours_file: self.colours_config = yaml.load(colours_file.read()) else: self.colours_config = yaml.load(pkg_resources.resource_string(__name__, "colours.yaml"))
[docs] def _load_cache(self): """ Load cached files. """ self._cached_files = set(os.listdir(self._cache_dir))
[docs] def _commit_edits(self, config): """ Write config to file. This method is supposed to be called only from :py:meth:`~._SettingsEditor.__exit__`. """ self._config.update(config) with open(self._config_file_path, 'w') as settings_file: settings_file.write(yaml.dump(self._config, default_flow_style=False))
[docs] def get(self, key, *sections): """ Return their configuration key in a specified section By default it looks in play_settings. """ section = self.get_section(*sections) try: return section.get(key) except (KeyError, TypeError): section = self.get_default_config_section(*sections) return section.get(key)
def _get_section(self, config, *sections): config = config.copy() for section in sections: config = config[section] return config
[docs] def get_section(self, *sections): """ Get a section from the user configuration file if it can find it, else load it from the system config """ try: return self._get_section(self._config, *sections) except (KeyError, TypeError): return self._get_section(self._default_config, *sections)
[docs] def get_default_config_section(self, *sections): """ Always get a section from the default/system configuration. You would use this whenever you need to loop through all the values in a section. In the user config they might be incomplete. """ return self._get_section(self._default_config, *sections)
[docs] def edit(self): """ Return :py:class:`._SettingsEditor` context manager to edit config. Settings are saved to file once the returned context manager exists. Example usage: .. code-block:: python from clay.settings import settings with settings.edit() as config: config['foo']['bar'] = 'baz' """ return _SettingsEditor(self._config, self._commit_edits)
[docs] def get_cached_file_path(self, filename): """ Get full path to cached file. """ path = os.path.join(self._cache_dir, filename) if os.path.exists(path): return path return None
[docs] def get_is_file_cached(self, filename): """ Return ``True`` if *filename* is present in cache. """ return filename in self._cached_files
[docs] def save_file_to_cache(self, filename, content): """ Save content into file in cache. """ path = os.path.join(self._cache_dir, filename) with open(path, 'wb') as cachefile: cachefile.write(content) self._cached_files.add(filename) return path
settings = _Settings() # pylint: disable=invalid-name