Re-write the Config class

This commit is contained in:
Andrew Resch 2008-11-05 12:35:02 +00:00
parent 89ae872394
commit cc366e9369
6 changed files with 253 additions and 166 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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__

View File

@ -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

View File

@ -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)

View File

@ -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 = []