[#2914] Fix: Specifying file version for default config

This commit is contained in:
bendikro 2016-10-28 19:12:03 +02:00 committed by Calum Lind
parent 27c87d56bb
commit 4d3cf756e4
4 changed files with 84 additions and 77 deletions

View File

@ -70,14 +70,14 @@ def prop(func):
def find_json_objects(s): def find_json_objects(s):
""" """Find json objects in a string.
Find json objects in a string.
:param s: the string to find json objects in Args:
:type s: string s (str): the string to find json objects in
:returns: a list of tuples containing start and end locations of json objects in the string `s` Returns:
:rtype: [(start, end), ...] list ([(start, end), ...]): a list of tuples containing start and
end locations of json objects in the string `s`
""" """
objects = [] objects = []
@ -101,15 +101,18 @@ def find_json_objects(s):
class Config(object): class Config(object):
""" """This class is used to access/create/modify config files
This class is used to access/create/modify config files
:param filename: the name of the config file Args:
:param defaults: dictionary of default values filename (str): The config filename.
:param config_dir: the path to the config directory defaults (dict): The default config values to insert before loading the config file
config_dir (str): the path to the config directory
file_version (int): The file format for the default config values when creating
a fresh config. This value should be increased whenever a new migration function is
setup to convert old config files. Default value is 1.
""" """
def __init__(self, filename, defaults=None, config_dir=None): def __init__(self, filename, defaults=None, config_dir=None, file_version=1):
self.__config = {} self.__config = {}
self.__set_functions = {} self.__set_functions = {}
self.__change_callbacks = [] self.__change_callbacks = []
@ -117,7 +120,7 @@ class Config(object):
# These hold the version numbers and they will be set when loaded # These hold the version numbers and they will be set when loaded
self.__version = { self.__version = {
"format": 1, "format": 1,
"file": 1 "file": file_version
} }
# This will get set with a reactor.callLater whenever a config option # This will get set with a reactor.callLater whenever a config option
@ -140,24 +143,23 @@ class Config(object):
return item in self.__config return item in self.__config
def __setitem__(self, key, value): def __setitem__(self, key, value):
""" """See set_item"""
See
:meth:`set_item`
"""
return self.set_item(key, value) return self.set_item(key, value)
def set_item(self, key, value): def set_item(self, key, value):
""" """Sets item 'key' to 'value' in the config dictionary, but does not allow
Sets item 'key' to 'value' in the config dictionary, but does not allow
changing the item's type unless it is None. If the types do not match, changing the item's type unless it is None. If the types do not match,
it will attempt to convert it to the set type before raising a ValueError. it will attempt to convert it to the set type before raising a ValueError.
:param key: string, item to change to change Args:
:param value: the value to change item to, must be same type as what is currently in the config key (str): item to change to change
value (any): the value to change item to, must be same type as what is
currently in the config
:raises ValueError: raised when the type of value is not the same as\ Raises:
what is currently in the config and it could not convert the value ValueError: raised when the type of value is not the same as what is
currently in the config and it could not convert the value
**Usage** **Usage**
@ -218,20 +220,20 @@ what is currently in the config and it could not convert the value
self._save_timer = callLater(5, self.save) self._save_timer = callLater(5, self.save)
def __getitem__(self, key): def __getitem__(self, key):
""" """See get_item """
See
:meth:`get_item`
"""
return self.get_item(key) return self.get_item(key)
def get_item(self, key): def get_item(self, key):
""" """Gets the value of item 'key'
Gets the value of item 'key'
:param key: the item for which you want it's value Args:
:return: the value of item 'key' key (str): the item for which you want it's value
:raises KeyError: if 'key' is not in the config dictionary Returns:
the value of item 'key'
Raises:
ValueError: if 'key' is not in the config dictionary
**Usage** **Usage**
@ -249,14 +251,16 @@ what is currently in the config and it could not convert the value
return self.__config[key] return self.__config[key]
def get(self, key, default=None): def get(self, key, default=None):
""" """Gets the value of item 'key' if key is in the config, else default.
Gets the value of item 'key' if key is in the config, else default.
If default is not given, it defaults to None, so that this method If default is not given, it defaults to None, so that this method
never raises a KeyError. never raises a KeyError.
:param key: the item for which you want it's value Args:
:param default: the default value if key is missing key (str): the item for which you want it's value
:return: the value of item 'key' or default default (any): the default value if key is missing
Returns:
the value of item 'key' or default
**Usage** **Usage**
@ -280,11 +284,13 @@ what is currently in the config and it could not convert the value
self.del_item(key) self.del_item(key)
def del_item(self, key): def del_item(self, key):
""" """Deletes item with a specific key from the configuration.
Deletes item with a specific key from the configuration.
:param key: the item which you wish to delete. Args:
:raises KeyError: if 'key' is not in the config dictionary key (str): the item which you wish to delete.
Raises:
ValueError: if 'key' is not in the config dictionary
**Usage** **Usage**
>>> config = Config("test.conf", defaults={"test": 5}) >>> config = Config("test.conf", defaults={"test": 5})
@ -302,10 +308,10 @@ what is currently in the config and it could not convert the value
self._save_timer = callLater(5, self.save) self._save_timer = callLater(5, self.save)
def register_change_callback(self, callback): def register_change_callback(self, callback):
""" """Registers a callback function that will be called when a value is changed in the config dictionary
Registers a callback function that will be called when a value is changed in the config dictionary
:param callback: the function, callback(key, value) Args:
callback (func): the function, callback(key, value)
**Usage** **Usage**
@ -319,12 +325,12 @@ what is currently in the config and it could not convert the value
self.__change_callbacks.append(callback) self.__change_callbacks.append(callback)
def register_set_function(self, key, function, apply_now=True): def register_set_function(self, key, function, apply_now=True):
""" """Register a function to be called when a config value changes
Register a function to be called when a config value changes
:param key: the item to monitor for change Args:
:param function: the function to call when the value changes, f(key, value) key (str): the item to monitor for change
:keyword apply_now: if True, the function will be called after it's registered function (func): the function to call when the value changes, f(key, value)
apply_now (bool): if True, the function will be called after it's registered
**Usage** **Usage**
@ -348,8 +354,7 @@ what is currently in the config and it could not convert the value
return return
def apply_all(self): def apply_all(self):
""" """Calls all set functions
Calls all set functions
**Usage** **Usage**
@ -368,10 +373,10 @@ what is currently in the config and it could not convert the value
func(key, self.__config[key]) func(key, self.__config[key])
def apply_set_functions(self, key): def apply_set_functions(self, key):
""" """Calls set functions for `:param:key`.
Calls set functions for `:param:key`.
:param key: str, the config key Args:
key (str): the config key
""" """
log.debug("Calling set functions for key %s..", key) log.debug("Calling set functions for key %s..", key)
@ -380,11 +385,10 @@ what is currently in the config and it could not convert the value
func(key, self.__config[key]) func(key, self.__config[key])
def load(self, filename=None): def load(self, filename=None):
""" """Load a config file
Load a config file
:param filename: if None, uses filename set in object initialization
Args:
filename (str): if None, uses filename set in object initialization
""" """
if not filename: if not filename:
@ -427,12 +431,13 @@ what is currently in the config and it could not convert the value
self.__version["format"], self.__version["file"], self.__config) self.__version["format"], self.__version["file"], self.__config)
def save(self, filename=None): def save(self, filename=None):
""" """Save configuration to disk
Save configuration to disk
:param filename: if None, uses filename set in object initiliazation Args:
:rtype bool: filename (str): if None, uses filename set in object initialization
:return: whether or not the save succeeded.
Returns:
bool: whether or not the save succeeded.
""" """
if not filename: if not filename:
@ -489,17 +494,17 @@ what is currently in the config and it could not convert the value
self._save_timer.cancel() self._save_timer.cancel()
def run_converter(self, input_range, output_version, func): def run_converter(self, input_range, output_version, func):
""" """Runs a function that will convert file versions in the `:param:input_range`
Runs a function that will convert file versions in the `:param:input_range`
to the `:param:output_version`. to the `:param:output_version`.
:param input_range: tuple, (int, int) the range of input versions this Args:
function will accept input_range (tuple): (int, int) the range of input versions this function will accept
:param output_version: int, the version this function will return output_version (int): the version this function will return
:param func: func, the function that will do the conversion, it will take func (func): the function that will do the conversion, it will take the config
the config dict as an argument and return the augmented dict dict as an argument and return the augmented dict
:raises ValueError: if the output_version is less than the input_range Raises:
ValueError: if the output_version is less than the input_range
""" """
if output_version in input_range or output_version <= max(input_range): if output_version in input_range or output_version <= max(input_range):

View File

@ -84,12 +84,14 @@ class _ConfigManager(object):
# We need to return True to keep the timer active # We need to return True to keep the timer active
return True return True
def get_config(self, config_file, defaults=None): def get_config(self, config_file, defaults=None, file_version=1):
"""Get a reference to the Config object for this filename""" """Get a reference to the Config object for this filename"""
log.debug("Getting config '%s'", config_file) log.debug("Getting config '%s'", config_file)
# Create the config object if not already created # Create the config object if not already created
if config_file not in self.config_files.keys(): if config_file not in self.config_files.keys():
self.config_files[config_file] = Config(config_file, defaults, self.config_directory) self.config_files[config_file] = Config(config_file, defaults,
config_dir=self.config_directory,
file_version=file_version)
return self.config_files[config_file] return self.config_files[config_file]
@ -97,8 +99,8 @@ class _ConfigManager(object):
_configmanager = _ConfigManager() _configmanager = _ConfigManager()
def ConfigManager(config, defaults=None): # NOQA def ConfigManager(config, defaults=None, file_version=1): # NOQA
return _configmanager.get_config(config, defaults) return _configmanager.get_config(config, defaults=defaults, file_version=file_version)
def set_config_dir(directory): def set_config_dir(directory):

View File

@ -102,7 +102,7 @@ class ConnectionManager(component.Component):
localclient_password localclient_password
)] )]
} }
config = ConfigManager("hostlist.conf.1.2", default_config) config = ConfigManager("hostlist.conf.1.2", defaults=default_config, file_version=2)
config.run_converter((0, 1), 2, self.__migrate_config_1_to_2) config.run_converter((0, 1), 2, self.__migrate_config_1_to_2)
return config return config

View File

@ -554,7 +554,7 @@ class DelugeWeb(component.Component):
""" """
component.Component.__init__(self, "DelugeWeb", depend=["Web"]) component.Component.__init__(self, "DelugeWeb", depend=["Web"])
self.config = configmanager.ConfigManager("web.conf", CONFIG_DEFAULTS) self.config = configmanager.ConfigManager("web.conf", defaults=CONFIG_DEFAULTS, file_version=2)
self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2) self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2)
self.config.register_set_function("language", self._on_language_changed) self.config.register_set_function("language", self._on_language_changed)
self.socket = None self.socket = None