Re-write the Config class

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

View file

@ -1,20 +1,20 @@
# #
# config.py # config.py
# #
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com> # Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
# #
# Deluge is free software. # Deluge is free software.
# #
# You may redistribute it and/or modify it under the terms of the # You may redistribute it and/or modify it under the terms of the
# GNU General Public License, as published by the Free Software # GNU General Public License, as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) # Foundation; either version 3 of the License, or (at your option)
# any later version. # any later version.
# #
# deluge is distributed in the hope that it will be useful, # deluge is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details. # See the GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with deluge. If not, write to: # along with deluge. If not, write to:
# The Free Software Foundation, Inc., # The Free Software Foundation, Inc.,
@ -31,171 +31,263 @@
# this exception statement from your version. If you delete this exception # this exception statement from your version. If you delete this exception
# statement from all source files in the program, then also delete it here. # statement from all source files in the program, then also delete it here.
"""Configuration class used to access/create/modify configuration files.""" """
Deluge Config Module
import cPickle """
import os.path
import cPickle as pickle
import shutil
import os
import gobject import gobject
import deluge.common import deluge.common
from deluge.log import LOG as log from deluge.log import LOG as log
class Config: def prop(func):
"""This class is used to access configuration files.""" """Function decorator for defining property attributes
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
# Load the config from file in the config_dir The decorated function is expected to return a dictionary
if config_dir == None: containing one or more of the following pairs:
self.config_file = deluge.common.get_default_config_dir(filename) fget - function for getting attribute value
else: fset - function for setting attribute value
self.config_file = os.path.join(config_dir, filename) fdel - function for deleting attribute
This can be conveniently constructed by the locals() builtin
self.load(self.config_file) function; see:
# Save http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
self.save() """
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 # This will get set with a gobject.timeout_add whenever a config option
# is set. # is set.
self.save_timer = None self.__save_timer = None
def __del__(self): if defaults:
self.save() self.__config = defaults
def load(self, filename=None): # Load the config from file in the config_dir
"""Load a config file either by 'filename' or the filename set during if config_dir:
construction of this object.""" self.__config_file = os.path.join(config_dir, filename)
# Use self.config_file if filename is None else:
if filename is None: self.__config_file = deluge.common.get_default_config_dir(filename)
filename = self.config_file
try: self.load()
# Un-pickle the file and update the config dictionary
pkl_file = open(filename, "rb") def __setitem__(self, key, value):
filedump = cPickle.load(pkl_file) """
self.config.update(filedump) See
pkl_file.close() :meth:`set_item`
except IOError: """
log.warning("IOError: Unable to load file '%s'", filename)
except EOFError: return self.set_item(key, value)
pkl_file.close()
def set_item(self, key, value):
def save(self, filename=None): """
"""Save configuration to either 'filename' or the filename set during Sets item 'key' to 'value' in the config dictionary, but does not allow
construction of this object.""" changing the item's type unless it is None
# Saves the config dictionary
if filename is None: :param key: string, item to change to change
filename = self.config_file :param value: the value to change item to, must be same type as what is currently in the config
# 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 :raises ValueError: raised when the type of value is not the same as what is currently in the config
try:
pkl_file = open(filename, "rb") **Usage**
filedump = cPickle.load(pkl_file)
pkl_file.close() >>> config = Config("test.conf")
if filedump == self.config: >>> config["test"] = 5
# The config has not changed so lets just return >>> config["test"]
self.save_timer = None 5
return
except (EOFError, IOError): """
log.warning("IOError: Unable to open file: '%s'", filename) if self.__config[key] == value:
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:
return 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: if value is not None and oldtype != type(None) and oldtype != newtype:
try: try:
value = oldtype(value) value = oldtype(value)
except ValueError: except ValueError:
log.warning("Type '%s' invalid for '%s'", newtype, key) log.warning("Type '%s' invalid for '%s'", newtype, key)
return raise
log.debug("Setting '%s' to %s of %s", key, value, oldtype) log.debug("Setting '%s' to %s of %s", key, value, type(value))
# Make a copy of the current config prior to changing it # Make a copy of the current config prior to changing it
self.previous_config = self.config.copy() self.__previous_config.update(self.__config)
self.config[key] = value self.__config[key] = value
# Run the set_function for this key if any # Run the set_function for this key if any
try: try:
gobject.idle_add(self.set_functions[key], key, value) gobject.idle_add(self.__set_functions[key], key, value)
except KeyError: except KeyError:
pass pass
try: try:
gobject.idle_add(self._change_callback, key, value) gobject.idle_add(self.__change_callback, key, value)
except: except:
pass pass
# We set the save_timer for 5 seconds if not already set # 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:
if not self.save_timer: self.__save_timer = gobject.timeout_add(5000, self.save)
self.save_timer = gobject.timeout_add(5000, self.save)
def __getitem__(self, key):
def get(self, key): """
"""Get the value of 'key'. If it is an invalid key then get() will See
return None.""" :meth:`get_item`
# Attempts to get the "key" value and returns None if the key is """
# invalid return self.get_item(key)
try:
value = self.config[key] def get_item(self, key):
log.debug("Getting '%s' as %s of %s", key, value, type(value)) """
return value Gets the value of item 'key'
except KeyError:
log.warning("Key does not exist, returning None") :param key: the item for which you want it's value
return None :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): 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): 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) 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 # Run the function now if apply_now is set
if apply_now: if apply_now:
self.set_functions[key](key, self.config[key]) self.__set_functions[key](key, self.__config[key])
return return
def apply_all(self): def apply_all(self):
"""Runs all set functions""" """
log.debug("Running all set functions..") Calls 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]
def __setitem__(self, key, value): **Usage**
self.set(key, value)
>>> 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 # 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. # seconds aftering adding a rule so that 'batch' adding of rules isn't slow.
self._set_ip_filter_timer = None self._set_ip_filter_timer = None
# Load metadata extension # Load metadata extension
self.session.add_extension(lt.create_metadata_plugin) self.session.add_extension(lt.create_metadata_plugin)
self.session.add_extension(lt.create_ut_metadata_plugin) self.session.add_extension(lt.create_ut_metadata_plugin)
@ -528,7 +528,7 @@ class Core(
def export_get_config(self): def export_get_config(self):
"""Get all the preferences as a dictionary""" """Get all the preferences as a dictionary"""
return self.config.get_config() return self.config.config
def export_get_config_value(self, key): def export_get_config_value(self, key):
"""Get the config value for key""" """Get the config value for key"""
@ -649,12 +649,12 @@ class Core(
def export_block_ip_range(self, range): def export_block_ip_range(self, range):
"""Block an ip range""" """Block an ip range"""
self.ip_filter.add_rule(range[0], range[1], 1) self.ip_filter.add_rule(range[0], range[1], 1)
# Start a 2 second timer (and remove the previous one if it exists) # Start a 2 second timer (and remove the previous one if it exists)
if self._set_ip_filter_timer: if self._set_ip_filter_timer:
gobject.source_remove(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) self._set_ip_filter_timer = gobject.timeout_add(2000, self.session.set_ip_filter, self.ip_filter)
def export_reset_ip_filter(self): def export_reset_ip_filter(self):
"""Clears the ip filter""" """Clears the ip filter"""
self.ip_filter = lt.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 """Renames files in 'torrent_id'. The 'filenames' parameter should be a
list of (index, filename) pairs.""" list of (index, filename) pairs."""
self.torrents[torrent_id].rename_files(filenames) self.torrents[torrent_id].rename_files(filenames)
def export_rename_folder(self, torrent_id, folder, new_folder): def export_rename_folder(self, torrent_id, folder, new_folder):
"""Renames the 'folder' to 'new_folder' in 'torrent_id'.""" """Renames the 'folder' to 'new_folder' in 'torrent_id'."""
self.torrents[torrent_id].rename_folder(folder, new_folder) self.torrents[torrent_id].rename_folder(folder, new_folder)

View file

@ -1,4 +1,4 @@
:mod:'deluge.config' :mod:`deluge.config`
==================== ====================
.. automodule:: deluge.config .. automodule:: deluge.config
@ -6,3 +6,6 @@
:show-inheritance: :show-inheritance:
:members: :members:
:undoc-members: :undoc-members:
.. automethod:: __setitem__
.. automethod:: __getitem__

View file

@ -101,14 +101,14 @@ class Core(CorePluginBase):
#__init__ #__init__
core = self.plugin.get_core() 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.core_cfg = ConfigManager("core.conf")
self.set_config_defaults() #self.set_config_defaults()
#reduce typing, assigning some values to self... #reduce typing, assigning some values to self...
self.torrents = core.torrents.torrents self.torrents = core.torrents.torrents
self.labels = self.config.get("labels") self.labels = self.config["labels"]
self.torrent_labels = self.config.get("torrent_labels") self.torrent_labels = self.config["torrent_labels"]
self.clean_initial_config() self.clean_initial_config()
#todo: register to torrent_added event. #todo: register to torrent_added event.
@ -176,7 +176,7 @@ class Core(CorePluginBase):
changed = False changed = False
for key, value in CONFIG_DEFAULTS.iteritems(): for key, value in CONFIG_DEFAULTS.iteritems():
if not key in self.config.config: if not key in self.config.config:
self.config.config[key] = value self.config[key] = value
changed = True changed = True
if changed: if changed:
self.config.save() self.config.save()
@ -298,13 +298,13 @@ class Core(CorePluginBase):
def export_get_global_options(self): def export_get_global_options(self):
"see : label_set_global_options" "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): def export_set_global_options(self, options):
"""global_options:""" """global_options:"""
for key in CORE_OPTIONS: for key in CORE_OPTIONS:
if options.has_key(key): if options.has_key(key):
self.config.set(key, options[key]) self.config[key] = options[key]
self.config.save() self.config.save()
def _status_get_label(self, torrent_id): def _status_get_label(self, torrent_id):
@ -312,4 +312,3 @@ class Core(CorePluginBase):
if __name__ == "__main__": if __name__ == "__main__":
import test import test

View file

@ -42,7 +42,6 @@ from deluge.ui.client import aclient
from deluge.configmanager import ConfigManager from deluge.configmanager import ConfigManager
config = ConfigManager("label.conf") config = ConfigManager("label.conf")
GTK_ALFA = config.get("gtk_alfa")
NO_LABEL = "No Label" NO_LABEL = "No Label"
class LabelMenu(gtk.MenuItem): class LabelMenu(gtk.MenuItem):
@ -85,8 +84,3 @@ class LabelMenu(gtk.MenuItem):
for torrent_id in self.get_torrent_ids(): for torrent_id in self.get_torrent_ids():
aclient.label_set_torrent(None, torrent_id, label_id) aclient.label_set_torrent(None, torrent_id, label_id)
#aclient.force_call(block=True) #aclient.force_call(block=True)

View file

@ -152,18 +152,18 @@ class MainWindow(component.Component):
def on_window_configure_event(self, widget, event): def on_window_configure_event(self, widget, event):
if not self.config["window_maximized"] and self.visible: if not self.config["window_maximized"] and self.visible:
self.config.set("window_x_pos", self.window.get_position()[0]) self.config["window_x_pos"] = self.window.get_position()[0]
self.config.set("window_y_pos", self.window.get_position()[1]) self.config["window_y_pos"] = self.window.get_position()[1]
self.config.set("window_width", event.width) self.config["window_width"] = event.width
self.config.set("window_height", event.height) self.config["window_height"] = event.height
def on_window_state_event(self, widget, event): def on_window_state_event(self, widget, event):
if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED: if event.changed_mask & gtk.gdk.WINDOW_STATE_MAXIMIZED:
if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED: if event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED:
log.debug("pos: %s", self.window.get_position()) log.debug("pos: %s", self.window.get_position())
self.config.set("window_maximized", True) self.config["window_maximized"] = True
else: else:
self.config.set("window_maximized", False) self.config["window_maximized"] = False
if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED: if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED: if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
log.debug("MainWindow is minimized..") log.debug("MainWindow is minimized..")
@ -189,8 +189,7 @@ class MainWindow(component.Component):
return True return True
def on_vpaned_position_event(self, obj, param): def on_vpaned_position_event(self, obj, param):
self.config.set("window_pane_position", self.config["window_pane_position"] = self.config["window_height"] - self.vpaned.get_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): def on_drag_data_received_event(self, widget, drag_context, x, y, selection_data, info, timestamp):
args = [] args = []