Source code for cozify.config

"""Module for handling consistent state storage.

Attributes:
    state_path(str): file path where state storage is kept. By default XDG conventions are used. (Most likely ~/.config/python-cozify/python-cozify.cfg)
    state(configparser.ConfigParser): State object used for in-memory state. By default initialized with _initState.
    latest_version(int): Current up to date version number. Anything encountered lower than this will go through autoconversion.
"""

import configparser
import os
import datetime
from absl import logging

# anything below 2 was not versioned
latest_version = 2


[docs]def version(new_version=None): """Return or manipulate current config version. Args: new_version(int): New version number to assume for the current config. You probably don't want to do this! """ global state if 'meta' not in state: state['meta'] = {} if 'version' not in state['meta']: state['meta']['version'] = '1' # version not set, assume it to be ancient commit() if new_version is not None and isinstance(new_version, int): state['meta']['version'] = str(new_version) commit() return state.getint('meta', 'version')
[docs]def commit(tmpstate=None): """Write current state to file storage. Args: tmpstate(configparser.ConfigParser): State object to store instead of default state. """ global state_path if tmpstate is None: global state tmpstate = state with open(state_path, 'w') as cf: tmpstate.write(cf)
[docs]def set_state_path(filepath=None, copy_current=False): """Set state storage path. Useful for example for testing without affecting your normal state. Call with no arguments to reset back to autoconfigured location. Args: filepath(str): file path to use as new storage location. Defaults to XDG defined path. copy_current(bool): Instead of initializing target file, dump previous state into it. """ if filepath is None: filepath = _initXDG() global state_path global state state_path = filepath if copy_current: commit() else: state = _initState(state_path)
[docs]def dump(): """Print out current state file to stdout. Long values are truncated since this is only for visualization. """ for section in state.sections(): print('[{!s:.10}]'.format(section)) for option in state.options(section): print(' {!s:<13.13} = {!s:>10.100}'.format(option, state[section][option]))
def _iso_now(): """Helper to return isoformat datetime stamp that's more compatible than the default. Once Python 3.6 is more widely popular this becomes redundant since isoformat() will support timespec. Returns: str: now() in isoformat truncated to full seconds. """ return datetime.datetime.now().isoformat().split(".")[0] def _initState(state_path): """Initialize state on cold start. Any stored state is read in or a new basic state is initialized. Args: state_path(str): State storage filepath to attempt to read from. Returns: configparser.ConfigParser: State object. """ # if we can read it, read it in, otherwise create empty file state = configparser.ConfigParser(allow_no_value=True) try: cf = open(state_path, 'r') except IOError: cf = open(state_path, 'w+') os.chmod(state_path, 0o600) # set to user readwrite only to protect tokens else: state.read_file(cf) # make sure config is in roughly a valid state for key in ['Cloud', 'Hubs']: if key not in state: state[key] = {} commit(state) return state def _initXDG(): """Initialize config path per XDG basedir-spec and resolve the final location of state file storage. Returns: str: file path to state file as per XDG spec and current env. """ # per the XDG basedir-spec we adhere to $XDG_CONFIG_HOME if it's set, otherwise assume $HOME/.config xdg_config_home = '' if 'XDG_CONFIG_HOME' in os.environ: xdg_config_home = os.environ['XDG_CONFIG_HOME'] logging.debug('XDG basedir overriden: {0}'.format(xdg_config_home)) else: xdg_config_home = "%s/.config" % os.path.expanduser('~') # XDG base-dir: "If, when attempting to write a file, the destination directory is non-existant an attempt should be made to create it with permission 0700. If the destination directory exists already the permissions should not be changed." if not os.path.isdir(xdg_config_home): logging.debug('XDG basedir does not exist, creating: {0}'.format(xdg_config_home)) os.mkdir(xdg_config_home, 0o0700) # finally create our own config dir config_dir = "%s/%s" % (xdg_config_home, 'python-cozify') if not os.path.isdir(config_dir): logging.debug('XDG local dir does not exist, creating: {0}'.format(config_dir)) os.mkdir(config_dir, 0o0700) state_path = "%s/python-cozify.cfg" % config_dir return state_path state_path = _initXDG() state = _initState(state_path)