Re-write the Config class
This commit is contained in:
parent
89ae872394
commit
cc366e9369
368
deluge/config.py
368
deluge/config.py
|
@ -1,20 +1,20 @@
|
|||
#
|
||||
# config.py
|
||||
#
|
||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||
#
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
|
@ -31,171 +31,263 @@
|
|||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
|
||||
"""Configuration class used to access/create/modify configuration files."""
|
||||
|
||||
import cPickle
|
||||
import os.path
|
||||
"""
|
||||
Deluge Config Module
|
||||
"""
|
||||
|
||||
import cPickle as pickle
|
||||
import shutil
|
||||
import os
|
||||
import gobject
|
||||
import deluge.common
|
||||
from deluge.log import LOG as log
|
||||
|
||||
class Config:
|
||||
"""This class is used to access configuration files."""
|
||||
|
||||
def __init__(self, filename, defaults=None, config_dir=None):
|
||||
log.debug("Config created with filename: %s", filename)
|
||||
log.debug("Config defaults: %s", defaults)
|
||||
self.config = {}
|
||||
self.previous_config = {}
|
||||
self.set_functions = {}
|
||||
self._change_callback = None
|
||||
|
||||
# If defaults is not None then we need to use "defaults".
|
||||
if defaults != None:
|
||||
self.config = defaults
|
||||
def prop(func):
|
||||
"""Function decorator for defining property attributes
|
||||
|
||||
# Load the config from file in the config_dir
|
||||
if config_dir == None:
|
||||
self.config_file = deluge.common.get_default_config_dir(filename)
|
||||
else:
|
||||
self.config_file = os.path.join(config_dir, filename)
|
||||
|
||||
self.load(self.config_file)
|
||||
# Save
|
||||
self.save()
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
def __init__(self, filename, defaults=None, config_dir=None):
|
||||
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
|
||||
|
||||
def __del__(self):
|
||||
self.save()
|
||||
|
||||
def load(self, filename=None):
|
||||
"""Load a config file either by 'filename' or the filename set during
|
||||
construction of this object."""
|
||||
# Use self.config_file if filename is None
|
||||
if filename is None:
|
||||
filename = self.config_file
|
||||
try:
|
||||
# Un-pickle the file and update the config dictionary
|
||||
pkl_file = open(filename, "rb")
|
||||
filedump = cPickle.load(pkl_file)
|
||||
self.config.update(filedump)
|
||||
pkl_file.close()
|
||||
except IOError:
|
||||
log.warning("IOError: Unable to load file '%s'", filename)
|
||||
except EOFError:
|
||||
pkl_file.close()
|
||||
|
||||
def save(self, filename=None):
|
||||
"""Save configuration to either 'filename' or the filename set during
|
||||
construction of this object."""
|
||||
# Saves the config dictionary
|
||||
if filename is None:
|
||||
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:
|
||||
pkl_file = open(filename, "rb")
|
||||
filedump = cPickle.load(pkl_file)
|
||||
pkl_file.close()
|
||||
if filedump == self.config:
|
||||
# The config has not changed so lets just return
|
||||
self.save_timer = None
|
||||
return
|
||||
except (EOFError, IOError):
|
||||
log.warning("IOError: Unable to open file: '%s'", filename)
|
||||
|
||||
try:
|
||||
log.debug("Saving config file %s..", filename)
|
||||
pkl_file = open(filename, "wb")
|
||||
cPickle.dump(self.config, pkl_file)
|
||||
pkl_file.close()
|
||||
except IOError:
|
||||
log.warning("IOError: Unable to save file '%s'", filename)
|
||||
|
||||
self.save_timer = None
|
||||
|
||||
def set(self, key, value):
|
||||
"""Set the 'key' with 'value'."""
|
||||
# Sets the "key" with "value" in the config dict
|
||||
if self.config[key] == value:
|
||||
self.__save_timer = None
|
||||
|
||||
if defaults:
|
||||
self.__config = defaults
|
||||
|
||||
# Load the config from file in the config_dir
|
||||
if config_dir:
|
||||
self.__config_file = os.path.join(config_dir, filename)
|
||||
else:
|
||||
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
|
||||
|
||||
"""
|
||||
if self.__config[key] == value:
|
||||
return
|
||||
|
||||
oldtype, newtype = type(self.config[key]), type(value)
|
||||
|
||||
# Do not allow the type to change unless it is None
|
||||
oldtype, newtype = type(self.__config[key]), type(value)
|
||||
|
||||
if value is not None and oldtype != type(None) and oldtype != newtype:
|
||||
try:
|
||||
value = oldtype(value)
|
||||
except ValueError:
|
||||
log.warning("Type '%s' invalid for '%s'", newtype, key)
|
||||
return
|
||||
|
||||
log.debug("Setting '%s' to %s of %s", key, value, oldtype)
|
||||
|
||||
raise
|
||||
|
||||
log.debug("Setting '%s' to %s of %s", key, value, type(value))
|
||||
|
||||
# Make a copy of the current config prior to changing it
|
||||
self.previous_config = self.config.copy()
|
||||
self.config[key] = value
|
||||
self.__previous_config.update(self.__config)
|
||||
self.__config[key] = value
|
||||
# Run the set_function for this key if any
|
||||
try:
|
||||
gobject.idle_add(self.set_functions[key], key, value)
|
||||
gobject.idle_add(self.__set_functions[key], key, value)
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
gobject.idle_add(self._change_callback, key, value)
|
||||
gobject.idle_add(self.__change_callback, key, value)
|
||||
except:
|
||||
pass
|
||||
|
||||
# We set the save_timer for 5 seconds if not already set
|
||||
log.debug("save_timer: %s", self.save_timer)
|
||||
if not self.save_timer:
|
||||
self.save_timer = gobject.timeout_add(5000, self.save)
|
||||
|
||||
def get(self, key):
|
||||
"""Get the value of 'key'. If it is an invalid key then get() will
|
||||
return None."""
|
||||
# Attempts to get the "key" value and returns None if the key is
|
||||
# invalid
|
||||
try:
|
||||
value = self.config[key]
|
||||
log.debug("Getting '%s' as %s of %s", key, value, type(value))
|
||||
return value
|
||||
except KeyError:
|
||||
log.warning("Key does not exist, returning None")
|
||||
return None
|
||||
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]
|
||||
|
||||
def get_config(self):
|
||||
"""Returns the entire configuration as a dictionary."""
|
||||
return self.config
|
||||
|
||||
def get_previous_config(self):
|
||||
"""Returns the config prior to the last set()"""
|
||||
return self.previous_config
|
||||
|
||||
def register_change_callback(self, callback):
|
||||
"""Registers a callback that will be called when a value is changed"""
|
||||
self._change_callback = callback
|
||||
|
||||
"""
|
||||
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**
|
||||
|
||||
>>> config = Config("test.conf", default={"test": 5})
|
||||
>>> def cb(key, value):
|
||||
>>> print key, value
|
||||
>>> config.register_change_callback(cb)
|
||||
>>> config["test"] = 4
|
||||
test 4
|
||||
|
||||
"""
|
||||
self.__change_callback = callback
|
||||
|
||||
def register_set_function(self, key, function, apply_now=True):
|
||||
"""Register a function to be run when a config value changes."""
|
||||
"""
|
||||
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**
|
||||
|
||||
>>> config = Config("test.conf", default={"test": 5})
|
||||
>>> def cb(key, value):
|
||||
>>> print key, value
|
||||
>>> config.register_set_function("test", cb)
|
||||
>>> config["test"] = 4
|
||||
test 4
|
||||
|
||||
"""
|
||||
log.debug("Registering function for %s key..", key)
|
||||
self.set_functions[key] = function
|
||||
self.__set_functions[key] = function
|
||||
# Run the function now if apply_now is set
|
||||
if apply_now:
|
||||
self.set_functions[key](key, self.config[key])
|
||||
self.__set_functions[key](key, self.__config[key])
|
||||
return
|
||||
|
||||
|
||||
def apply_all(self):
|
||||
"""Runs all set functions"""
|
||||
log.debug("Running all set functions..")
|
||||
for key in self.set_functions.keys():
|
||||
self.set_functions[key](key, self.config[key])
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.config[key]
|
||||
"""
|
||||
Calls all set functions
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.set(key, value)
|
||||
**Usage**
|
||||
|
||||
>>> config = Config("test.conf", default={"test": 5})
|
||||
>>> def cb(key, value):
|
||||
>>> print key, value
|
||||
>>> config.register_set_function("test", cb)
|
||||
>>> 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
|
||||
|
||||
@prop
|
||||
def config():
|
||||
"""The config dictionary"""
|
||||
def fget(self):
|
||||
return self.__config
|
||||
def fdel(self):
|
||||
return self.save()
|
||||
return locals()
|
||||
|
|
|
@ -191,7 +191,7 @@ class Core(
|
|||
# This keeps track of the timer to set the ip filter.. We do this a few
|
||||
# seconds aftering adding a rule so that 'batch' adding of rules isn't slow.
|
||||
self._set_ip_filter_timer = None
|
||||
|
||||
|
||||
# Load metadata extension
|
||||
self.session.add_extension(lt.create_metadata_plugin)
|
||||
self.session.add_extension(lt.create_ut_metadata_plugin)
|
||||
|
@ -528,7 +528,7 @@ class Core(
|
|||
|
||||
def export_get_config(self):
|
||||
"""Get all the preferences as a dictionary"""
|
||||
return self.config.get_config()
|
||||
return self.config.config
|
||||
|
||||
def export_get_config_value(self, key):
|
||||
"""Get the config value for key"""
|
||||
|
@ -649,12 +649,12 @@ class Core(
|
|||
def export_block_ip_range(self, range):
|
||||
"""Block an ip range"""
|
||||
self.ip_filter.add_rule(range[0], range[1], 1)
|
||||
|
||||
|
||||
# Start a 2 second timer (and remove the previous one if it exists)
|
||||
if self._set_ip_filter_timer:
|
||||
gobject.source_remove(self._set_ip_filter_timer)
|
||||
self._set_ip_filter_timer = gobject.timeout_add(2000, self.session.set_ip_filter, self.ip_filter)
|
||||
|
||||
|
||||
def export_reset_ip_filter(self):
|
||||
"""Clears the ip filter"""
|
||||
self.ip_filter = lt.ip_filter()
|
||||
|
@ -722,7 +722,7 @@ class Core(
|
|||
"""Renames files in 'torrent_id'. The 'filenames' parameter should be a
|
||||
list of (index, filename) pairs."""
|
||||
self.torrents[torrent_id].rename_files(filenames)
|
||||
|
||||
|
||||
def export_rename_folder(self, torrent_id, folder, new_folder):
|
||||
"""Renames the 'folder' to 'new_folder' in 'torrent_id'."""
|
||||
self.torrents[torrent_id].rename_folder(folder, new_folder)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
:mod:'deluge.config'
|
||||
:mod:`deluge.config`
|
||||
====================
|
||||
|
||||
.. automodule:: deluge.config
|
||||
|
@ -6,3 +6,6 @@
|
|||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. automethod:: __setitem__
|
||||
.. automethod:: __getitem__
|
||||
|
|
|
@ -101,14 +101,14 @@ class Core(CorePluginBase):
|
|||
|
||||
#__init__
|
||||
core = self.plugin.get_core()
|
||||
self.config = ConfigManager("label.conf")
|
||||
self.config = ConfigManager("label.conf", defaults=CONFIG_DEFAULTS)
|
||||
self.core_cfg = ConfigManager("core.conf")
|
||||
self.set_config_defaults()
|
||||
#self.set_config_defaults()
|
||||
|
||||
#reduce typing, assigning some values to self...
|
||||
self.torrents = core.torrents.torrents
|
||||
self.labels = self.config.get("labels")
|
||||
self.torrent_labels = self.config.get("torrent_labels")
|
||||
self.labels = self.config["labels"]
|
||||
self.torrent_labels = self.config["torrent_labels"]
|
||||
|
||||
self.clean_initial_config()
|
||||
#todo: register to torrent_added event.
|
||||
|
@ -176,7 +176,7 @@ class Core(CorePluginBase):
|
|||
changed = False
|
||||
for key, value in CONFIG_DEFAULTS.iteritems():
|
||||
if not key in self.config.config:
|
||||
self.config.config[key] = value
|
||||
self.config[key] = value
|
||||
changed = True
|
||||
if changed:
|
||||
self.config.save()
|
||||
|
@ -298,13 +298,13 @@ class Core(CorePluginBase):
|
|||
|
||||
def export_get_global_options(self):
|
||||
"see : label_set_global_options"
|
||||
return dict ( (k,self.config.get(k) ) for k in CORE_OPTIONS)
|
||||
return dict ( (k,self.config[k] ) for k in CORE_OPTIONS)
|
||||
|
||||
def export_set_global_options(self, options):
|
||||
"""global_options:"""
|
||||
for key in CORE_OPTIONS:
|
||||
if options.has_key(key):
|
||||
self.config.set(key, options[key])
|
||||
self.config[key] = options[key]
|
||||
self.config.save()
|
||||
|
||||
def _status_get_label(self, torrent_id):
|
||||
|
@ -312,4 +312,3 @@ class Core(CorePluginBase):
|
|||
|
||||
if __name__ == "__main__":
|
||||
import test
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ from deluge.ui.client import aclient
|
|||
|
||||
from deluge.configmanager import ConfigManager
|
||||
config = ConfigManager("label.conf")
|
||||
GTK_ALFA = config.get("gtk_alfa")
|
||||
NO_LABEL = "No Label"
|
||||
|
||||
class LabelMenu(gtk.MenuItem):
|
||||
|
@ -85,8 +84,3 @@ class LabelMenu(gtk.MenuItem):
|
|||
for torrent_id in self.get_torrent_ids():
|
||||
aclient.label_set_torrent(None, torrent_id, label_id)
|
||||
#aclient.force_call(block=True)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -152,18 +152,18 @@ class MainWindow(component.Component):
|
|||
|
||||
def on_window_configure_event(self, widget, event):
|
||||
if not self.config["window_maximized"] and self.visible:
|
||||
self.config.set("window_x_pos", self.window.get_position()[0])
|
||||
self.config.set("window_y_pos", self.window.get_position()[1])
|
||||
self.config.set("window_width", event.width)
|
||||
self.config.set("window_height", event.height)
|
||||
self.config["window_x_pos"] = self.window.get_position()[0]
|
||||
self.config["window_y_pos"] = self.window.get_position()[1]
|
||||
self.config["window_width"] = event.width
|
||||
self.config["window_height"] = event.height
|
||||
|
||||
def on_window_state_event(self, widget, event):
|
||||
if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED:
|
||||
if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED:
|
||||
log.debug("pos: %s", self.window.get_position())
|
||||
self.config.set("window_maximized", True)
|
||||
self.config["window_maximized"] = True
|
||||
else:
|
||||
self.config.set("window_maximized", False)
|
||||
self.config["window_maximized"] = False
|
||||
if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
|
||||
if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
|
||||
log.debug("MainWindow is minimized..")
|
||||
|
@ -189,8 +189,7 @@ class MainWindow(component.Component):
|
|||
return True
|
||||
|
||||
def on_vpaned_position_event(self, obj, param):
|
||||
self.config.set("window_pane_position",
|
||||
self.config["window_height"] - self.vpaned.get_position())
|
||||
self.config["window_pane_position"] = self.config["window_height"] - self.vpaned.get_position()
|
||||
|
||||
def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp):
|
||||
args = []
|
||||
|
|
Loading…
Reference in New Issue