2007-07-05 19:35:59 +00:00
|
|
|
#
|
|
|
|
# config.py
|
|
|
|
#
|
2008-11-05 12:35:02 +00:00
|
|
|
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
|
|
|
#
|
2007-07-05 19:35:59 +00:00
|
|
|
# Deluge is free software.
|
2008-11-05 12:35:02 +00:00
|
|
|
#
|
2007-07-05 19:35:59 +00:00
|
|
|
# You may redistribute it and/or modify it under the terms of the
|
|
|
|
# GNU General Public License, as published by the Free Software
|
2008-08-08 05:59:07 +00:00
|
|
|
# Foundation; either version 3 of the License, or (at your option)
|
2007-07-05 19:35:59 +00:00
|
|
|
# any later version.
|
2008-11-05 12:35:02 +00:00
|
|
|
#
|
2007-07-05 19:35:59 +00:00
|
|
|
# deluge is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
# See the GNU General Public License for more details.
|
2008-11-05 12:35:02 +00:00
|
|
|
#
|
2007-07-05 19:35:59 +00:00
|
|
|
# You should have received a copy of the GNU General Public License
|
2007-07-13 01:34:18 +00:00
|
|
|
# along with deluge. If not, write to:
|
2007-07-05 19:35:59 +00:00
|
|
|
# The Free Software Foundation, Inc.,
|
|
|
|
# 51 Franklin Street, Fifth Floor
|
2007-07-13 01:34:18 +00:00
|
|
|
# Boston, MA 02110-1301, USA.
|
2007-07-05 19:35:59 +00:00
|
|
|
#
|
2008-11-23 04:58:01 +00:00
|
|
|
|
2007-07-05 19:35:59 +00:00
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
"""
|
|
|
|
Deluge Config Module
|
|
|
|
"""
|
2007-07-05 19:35:59 +00:00
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
import cPickle as pickle
|
|
|
|
import shutil
|
|
|
|
import os
|
2007-11-10 06:00:23 +00:00
|
|
|
import gobject
|
2007-07-05 19:35:59 +00:00
|
|
|
import deluge.common
|
2007-08-26 14:34:50 +00:00
|
|
|
from deluge.log import LOG as log
|
2007-07-05 19:35:59 +00:00
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
def prop(func):
|
|
|
|
"""Function decorator for defining property attributes
|
|
|
|
|
|
|
|
The decorated function is expected to return a dictionary
|
|
|
|
containing one or more of the following pairs:
|
|
|
|
fget - function for getting attribute value
|
|
|
|
fset - function for setting attribute value
|
|
|
|
fdel - function for deleting attribute
|
|
|
|
This can be conveniently constructed by the locals() builtin
|
|
|
|
function; see:
|
|
|
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
|
|
|
|
"""
|
|
|
|
return property(doc=func.__doc__, **func())
|
|
|
|
|
|
|
|
class Config(object):
|
|
|
|
"""
|
|
|
|
This class is used to access/create/modify config files
|
|
|
|
|
|
|
|
:param filename: the name of the config file
|
|
|
|
:param defaults: dictionary of default values
|
|
|
|
:param config_dir: the path to the config directory
|
|
|
|
|
|
|
|
"""
|
2008-04-05 22:40:35 +00:00
|
|
|
def __init__(self, filename, defaults=None, config_dir=None):
|
2008-11-05 12:35:02 +00:00
|
|
|
self.__config = {}
|
|
|
|
self.__previous_config = {}
|
|
|
|
self.__set_functions = {}
|
|
|
|
self.__change_callback = None
|
|
|
|
# This will get set with a gobject.timeout_add whenever a config option
|
|
|
|
# is set.
|
|
|
|
self.__save_timer = None
|
|
|
|
|
|
|
|
if defaults:
|
|
|
|
self.__config = defaults
|
2007-07-05 19:35:59 +00:00
|
|
|
|
2007-07-13 01:34:18 +00:00
|
|
|
# Load the config from file in the config_dir
|
2008-11-05 12:35:02 +00:00
|
|
|
if config_dir:
|
|
|
|
self.__config_file = os.path.join(config_dir, filename)
|
2008-04-05 22:40:35 +00:00
|
|
|
else:
|
2008-11-05 12:35:02 +00:00
|
|
|
self.__config_file = deluge.common.get_default_config_dir(filename)
|
|
|
|
|
|
|
|
self.load()
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
"""
|
|
|
|
See
|
|
|
|
:meth:`set_item`
|
|
|
|
"""
|
|
|
|
|
|
|
|
return self.set_item(key, value)
|
|
|
|
|
|
|
|
def set_item(self, key, value):
|
|
|
|
"""
|
|
|
|
Sets item 'key' to 'value' in the config dictionary, but does not allow
|
|
|
|
changing the item's type unless it is None
|
|
|
|
|
|
|
|
:param key: string, item to change to change
|
|
|
|
:param value: 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 what is currently in the config
|
|
|
|
|
|
|
|
**Usage**
|
|
|
|
|
|
|
|
>>> config = Config("test.conf")
|
|
|
|
>>> config["test"] = 5
|
|
|
|
>>> config["test"]
|
|
|
|
5
|
|
|
|
|
|
|
|
"""
|
2008-11-06 05:42:15 +00:00
|
|
|
if not self.__config.has_key(key):
|
|
|
|
self.__config[key] = value
|
|
|
|
log.debug("Setting '%s' to %s of %s", key, value, type(value))
|
|
|
|
return
|
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
if self.__config[key] == value:
|
2008-08-14 05:58:58 +00:00
|
|
|
return
|
2008-11-05 12:35:02 +00:00
|
|
|
|
|
|
|
# Do not allow the type to change unless it is None
|
|
|
|
oldtype, newtype = type(self.__config[key]), type(value)
|
2008-08-12 11:13:41 +00:00
|
|
|
|
2008-08-14 06:37:20 +00:00
|
|
|
if value is not None and oldtype != type(None) and oldtype != newtype:
|
2008-01-20 21:37:11 +00:00
|
|
|
try:
|
2008-08-14 05:58:58 +00:00
|
|
|
value = oldtype(value)
|
|
|
|
except ValueError:
|
|
|
|
log.warning("Type '%s' invalid for '%s'", newtype, key)
|
2008-11-05 12:35:02 +00:00
|
|
|
raise
|
|
|
|
|
|
|
|
log.debug("Setting '%s' to %s of %s", key, value, type(value))
|
|
|
|
|
2008-08-14 05:58:58 +00:00
|
|
|
# Make a copy of the current config prior to changing it
|
2008-11-05 12:35:02 +00:00
|
|
|
self.__previous_config.update(self.__config)
|
|
|
|
self.__config[key] = value
|
2008-08-14 05:58:58 +00:00
|
|
|
# Run the set_function for this key if any
|
|
|
|
try:
|
2008-11-05 12:35:02 +00:00
|
|
|
gobject.idle_add(self.__set_functions[key], key, value)
|
2008-08-14 05:58:58 +00:00
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
try:
|
2008-11-05 12:35:02 +00:00
|
|
|
gobject.idle_add(self.__change_callback, key, value)
|
2008-08-14 05:58:58 +00:00
|
|
|
except:
|
|
|
|
pass
|
2007-09-18 14:40:19 +00:00
|
|
|
|
2008-09-25 02:03:58 +00:00
|
|
|
# We set the save_timer for 5 seconds if not already set
|
2008-11-05 12:35:02 +00:00
|
|
|
if not self.__save_timer:
|
|
|
|
self.__save_timer = gobject.timeout_add(5000, self.save)
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
"""
|
|
|
|
See
|
|
|
|
:meth:`get_item`
|
|
|
|
"""
|
|
|
|
return self.get_item(key)
|
|
|
|
|
|
|
|
def get_item(self, key):
|
|
|
|
"""
|
|
|
|
Gets the value of item 'key'
|
|
|
|
|
|
|
|
:param key: the item for which you want it's value
|
|
|
|
:return: the value of item 'key'
|
|
|
|
|
|
|
|
:raises KeyError: if 'key' is not in the config dictionary
|
|
|
|
|
|
|
|
**Usage**
|
|
|
|
|
|
|
|
>>> config = Config("test.conf", defaults={"test": 5})
|
|
|
|
>>> config["test"]
|
|
|
|
5
|
|
|
|
|
|
|
|
"""
|
|
|
|
return self.__config[key]
|
|
|
|
|
2008-01-20 21:37:11 +00:00
|
|
|
def register_change_callback(self, callback):
|
2008-11-05 12:35:02 +00:00
|
|
|
"""
|
|
|
|
Registers a callback function that will be called when a value is changed in the config dictionary
|
|
|
|
|
|
|
|
:param callback: the function, callback(key, value)
|
|
|
|
|
|
|
|
**Usage**
|
|
|
|
|
2008-11-06 00:32:46 +00:00
|
|
|
>>> config = Config("test.conf", defaults={"test": 5})
|
2008-11-05 12:35:02 +00:00
|
|
|
>>> def cb(key, value):
|
2008-11-06 05:42:15 +00:00
|
|
|
... print key, value
|
|
|
|
...
|
2008-11-05 12:35:02 +00:00
|
|
|
>>> config.register_change_callback(cb)
|
|
|
|
|
|
|
|
"""
|
|
|
|
self.__change_callback = callback
|
|
|
|
|
2007-09-27 13:29:20 +00:00
|
|
|
def register_set_function(self, key, function, apply_now=True):
|
2008-11-05 12:35:02 +00:00
|
|
|
"""
|
|
|
|
Register a function to be called when a config value changes
|
|
|
|
|
|
|
|
:param key: the item to monitor for change
|
|
|
|
:param function: the function to call when the value changes, f(key, value)
|
|
|
|
:keyword apply_now: if True, the function will be called after it's registered
|
|
|
|
|
|
|
|
**Usage**
|
|
|
|
|
2008-11-06 00:32:46 +00:00
|
|
|
>>> config = Config("test.conf", defaults={"test": 5})
|
2008-11-05 12:35:02 +00:00
|
|
|
>>> def cb(key, value):
|
2008-11-06 05:42:15 +00:00
|
|
|
... print key, value
|
|
|
|
...
|
|
|
|
>>> config.register_set_function("test", cb, apply_now=True)
|
|
|
|
test 5
|
2008-11-05 12:35:02 +00:00
|
|
|
|
|
|
|
"""
|
2007-09-20 22:10:03 +00:00
|
|
|
log.debug("Registering function for %s key..", key)
|
2008-11-05 12:35:02 +00:00
|
|
|
self.__set_functions[key] = function
|
2007-09-27 13:29:20 +00:00
|
|
|
# Run the function now if apply_now is set
|
|
|
|
if apply_now:
|
2008-11-05 12:35:02 +00:00
|
|
|
self.__set_functions[key](key, self.__config[key])
|
2007-09-18 14:40:19 +00:00
|
|
|
return
|
2008-11-05 12:35:02 +00:00
|
|
|
|
2007-09-20 22:10:03 +00:00
|
|
|
def apply_all(self):
|
2008-11-05 12:35:02 +00:00
|
|
|
"""
|
|
|
|
Calls all set functions
|
2007-07-09 02:50:20 +00:00
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
**Usage**
|
|
|
|
|
2008-11-06 00:32:46 +00:00
|
|
|
>>> config = Config("test.conf", defaults={"test": 5})
|
2008-11-05 12:35:02 +00:00
|
|
|
>>> def cb(key, value):
|
2008-11-06 05:42:15 +00:00
|
|
|
... print key, value
|
|
|
|
...
|
|
|
|
>>> config.register_set_function("test", cb, apply_now=False)
|
2008-11-05 12:35:02 +00:00
|
|
|
>>> config.apply_all()
|
|
|
|
test 5
|
|
|
|
|
|
|
|
"""
|
|
|
|
log.debug("Calling all set functions..")
|
|
|
|
for key, value in self.__set_functions.iteritems():
|
|
|
|
value(key, self.__config[key])
|
|
|
|
|
|
|
|
def load(self, filename=None):
|
|
|
|
"""
|
|
|
|
Load a config file
|
|
|
|
|
|
|
|
:param filename: if None, uses filename set in object initialization
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
if not filename:
|
|
|
|
filename = self.__config_file
|
|
|
|
try:
|
|
|
|
self.__config.update(pickle.load(open(filename, "rb")))
|
|
|
|
except Exception, e:
|
|
|
|
log.warning("Unable to load config file: %s", filename)
|
|
|
|
|
|
|
|
log.debug("Config %s loaded: %s", filename, self.__config)
|
|
|
|
|
|
|
|
def save(self, filename=None):
|
|
|
|
"""
|
|
|
|
Save configuration to disk
|
|
|
|
|
|
|
|
:param filename: if None, uses filename set in object initiliazation
|
|
|
|
|
|
|
|
"""
|
|
|
|
if not filename:
|
|
|
|
filename = self.__config_file
|
|
|
|
# Check to see if the current config differs from the one on disk
|
|
|
|
# We will only write a new config file if there is a difference
|
|
|
|
try:
|
|
|
|
if self.__config == pickle.load(open(filename, "rb")):
|
|
|
|
# The config has not changed so lets just return
|
|
|
|
self.__save_timer = None
|
|
|
|
return
|
|
|
|
except Exception, e:
|
|
|
|
log.warning("Unable to open config file: %s", filename)
|
|
|
|
|
|
|
|
self.__save_timer = None
|
|
|
|
|
|
|
|
try:
|
|
|
|
log.debug("Saving new config file %s", filename + ".new")
|
|
|
|
pickle.dump(self.__config, open(filename + ".new", "wb"))
|
|
|
|
except Exception, e:
|
|
|
|
log.error("Error writing new config file: %s", e)
|
|
|
|
return
|
|
|
|
|
|
|
|
# The new config file has been written successfully, so let's move it over
|
|
|
|
# the existing one.
|
|
|
|
try:
|
|
|
|
log.debug("Moving new config file %s to %s..", filename + ".new", filename)
|
|
|
|
shutil.move(filename + ".new", filename)
|
|
|
|
except Exception, e:
|
|
|
|
log.error("Error moving new config file: %s", e)
|
|
|
|
return
|
2007-09-17 11:07:58 +00:00
|
|
|
|
2008-11-05 12:35:02 +00:00
|
|
|
@prop
|
|
|
|
def config():
|
|
|
|
"""The config dictionary"""
|
|
|
|
def fget(self):
|
|
|
|
return self.__config
|
|
|
|
def fdel(self):
|
|
|
|
return self.save()
|
|
|
|
return locals()
|