summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornecoro <>2006-11-07 20:59:15 +0000
committernecoro <>2006-11-07 20:59:15 +0000
commit30c58a91fee72e2d5fb6b429a97343a12164e93e (patch)
tree7d59d99cb01151f122498d58a67a5a5a371c7bfc
parentd17ba4f22f3f4f38b9ea8963c3e1783cb964d348 (diff)
downloadportato-30c58a91fee72e2d5fb6b429a97343a12164e93e.tar.gz
portato-30c58a91fee72e2d5fb6b429a97343a12164e93e.tar.bz2
portato-30c58a91fee72e2d5fb6b429a97343a12164e93e.zip
new config-parser and a new layout for the config file
-rw-r--r--doc/Changelog3
-rw-r--r--etc/geneticone.cfg43
-rw-r--r--geneticone/config_parser.py274
-rw-r--r--geneticone/gui/gtk/windows.py4
-rw-r--r--geneticone/gui/gui_helper.py45
5 files changed, 344 insertions, 25 deletions
diff --git a/doc/Changelog b/doc/Changelog
index 19403d5..87595b3 100644
--- a/doc/Changelog
+++ b/doc/Changelog
@@ -1,3 +1,6 @@
+0.5.1:
+- new config parser and new config-layout
+
0.5.0:
- rewritten GTK-Frontend using Glade
- made the wrapper more powerful
diff --git a/etc/geneticone.cfg b/etc/geneticone.cfg
index 947cf18..9afc647 100644
--- a/etc/geneticone.cfg
+++ b/etc/geneticone.cfg
@@ -1,10 +1,49 @@
+#
+# Configuration file for Genetic/One
+# ==================================
+#
+# Allowed boolean values (case insensitive):
+# on <-> off
+# yes <-> no
+# true <-> false
+# 1 <-> 0
+#
+# Comments are single-line only and are started with a '#' or a ';'.
+#
+# Values can be assigned to options via a '=' or ':' - so "option = value" is the same as "option : value"
+#
+
+#
+# Main section - for general, frontend independent options
+#
[Main]
+
+; controls debug output - boolean value
+debug = True
+
+; control the same-named "emerge --update" options - boolean values
newuse = False
-keywordperversion = True
deep = False
+
+; control the name of the particular file if package.* is a directory - string values
+; allowed placeholders:
+; - $(pkg) : package-name
+; - $(cat) : category-name
+; - $(cat-1)/$(cat-2) : first/second part of the category name
usefile = geneticone
maskfile = geneticone
keywordfile = geneticone
+
+; control whether the option is inserted into package.* with a specific version or not
+keywordperversion = True
maskperversion = True
useperversion = True
-debug = True
+
+#
+# GTK-Section for options of the GTK-Frontend
+#
+[Gtk]
+
+; empty
+
+# vim:ts=4:sw=4:ft=cfg
diff --git a/geneticone/config_parser.py b/geneticone/config_parser.py
new file mode 100644
index 0000000..99e5b6c
--- /dev/null
+++ b/geneticone/config_parser.py
@@ -0,0 +1,274 @@
+# -*- coding: utf-8 -*-
+#
+# File: geneticone/config_parser.py
+# This file is part of the Genetic/One-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006 René 'Necoro' Neumann
+# This is free software. You may redistribute copies of it under the terms of
+# the GNU General Public License version 2.
+# There is NO WARRANTY, to the extent permitted by law.
+#
+# Written by René 'Necoro' Neumann <necoro@necoro.net>
+
+from helper import debug
+
+import re
+import types
+
+DELIMITER = ["=", ":"]
+COMMENT = [";","#"]
+
+# precompiled expressions
+TRUE = re.compile("((true)|(1)|(on)|(wahr)|(ja)|(yes))", re.I)
+FALSE = re.compile("((false)|(0)|(off)|(falsch)|(nein)|(no))", re.I)
+SECTION = re.compile("\s*\[(\w+)\]\s*")
+EXPRESSION = re.compile(r"\s*(\w+)\s*[:=]\s*(.*)\s*")
+
+class Value (object):
+ """Class defining a value of a key."""
+
+ def __init__ (self, value, line, bool = None):
+ """Constructor.
+
+ @param value: the value
+ @type value: string
+ @param line: the line in the config file
+ @type line: int
+ @param bool: boolean meaning of the value
+ @type bool: boolean"""
+
+ self.__value = value
+ self.line = line
+ self.boolean = bool
+
+ self.changed = False # true if we changed it
+ self.old = value # keep the original one ... so if we change it back to this one, we do not have to write
+
+ def set (self, value):
+ """Sets the value to a new one.
+
+ @param value: new value
+ @type value: string"""
+
+ self.__value = value
+
+ if value != self.old:
+ self.changed = True
+ else:
+ self.changed = False
+
+ def get (self):
+ """Returns the actual value.
+
+ @returns: the actual value
+ @rtype: string"""
+
+ return self.__value
+
+ def is_bool (self):
+ """Returns whether the actual value has a boolean meaning.
+
+ @returns: True if the actual value can be interpreted as a boolean
+ @rtype: boolean"""
+
+ return (self.boolean != None)
+
+ def __str__ (self):
+ return str(self.__value)
+
+ def __repr__ (self):
+ return self.__str__()
+
+ value = property(get,set)
+
+class ConfigParser:
+ """The newly implemented Config-Parser."""
+
+ # generates the complementary true-false-pairs
+ true_false = {
+ "true" : "false",
+ "1" : "0",
+ "on" : "off",
+ "yes" : "no",
+ "ja" : "nein",
+ "wahr" : "falsch"}
+ true_false.update(zip(true_false.values(), true_false.keys()))
+
+ def __init__ (self, file):
+ """Constructor.
+
+ @param file: the configuration file to open
+ @type file: string"""
+
+ self.file = file
+ self.__initialize()
+
+ def __initialize (self):
+ """Private method which initializes our dictionaries."""
+
+ self.vars = {"MAIN": {}}
+ self.cache = None # file cache
+ self.pos = {} # stores the positions of the matches
+
+ def _invert (self, val):
+ """Invertes a given boolean.
+
+ @param val: value to invert
+ @type val: string
+ @returns: inverted value
+ @rtype: string"""
+
+ return self.true_false[val.lower()]
+
+ def parse (self):
+ """Parses the file."""
+
+ # read into cache
+ file = open(self.file, "r")
+ self.cache = file.readlines()
+ file.close()
+
+ # implicit first section is main
+ section = "MAIN"
+ count = -1
+ for line in self.cache:
+ count += 1
+
+ ls = line.strip()
+ if not ls: continue # empty
+ if ls[0] in COMMENT: continue # comment
+
+ # look for a section
+ match = SECTION.search(line)
+ if match:
+ sec = match.group(1).upper()
+ if sec != section:
+ self.vars[sec] = {}
+ section = sec
+ continue
+
+ # look for an expression
+ match = EXPRESSION.search(line)
+ if match:
+ val = match.group(2)
+
+ # find the boolean value
+ bool = None
+ if TRUE.match(val):
+ bool = True
+ elif FALSE.match(val):
+ bool = False
+
+ # insert
+ key = match.group(1).lower()
+ self.vars[section][key] = Value(val, count, bool = bool)
+ self.pos[count] = match.span(2)
+ else: # neither comment nor empty nor expression nor section => error
+ debug("Unrecognized line:",line)
+
+ def get (self, key, section = "MAIN"):
+ """Returns the value of a given key in a section.
+
+ @param key: the key
+ @type key: string
+ @param section: the section
+ @type section: string
+
+ @returns: value
+ @rtype: string
+
+ @raises KeyError: if section or key could not be found"""
+
+ section = section.upper()
+ key = key.lower()
+ return self.vars[section][key].value
+
+ def get_boolean (self, key, section = "MAIN"):
+ """Returns the boolean value of a given key in a section.
+
+ @param key: the key
+ @type key: string
+ @param section: the section
+ @type section: string
+
+ @returns: value
+ @rtype: boolean
+
+ @raises KeyError: if section or key could not be found
+ @raises ValueError: if key does not have a boolean value"""
+
+ section = section.upper()
+ key = key.lower()
+
+ val = self.vars[section][key]
+
+ if val.is_bool():
+ return val.boolean
+
+ raise ValueError, "\"%s\" is not a boolean." % key
+
+ def set (self, key, value = "", section = "MAIN"):
+ """Sets a new value of a given key in a section.
+
+ @param key: the key
+ @type key: string
+ @param value: the new value
+ @type value: string
+ @param section: the section
+ @type section: string
+
+ @raises KeyError: if section or key could not be found"""
+
+ section = section.upper()
+ key = key.lower()
+
+ self.vars[section][key].value = value
+
+ def set_boolean (self, key, value, section = "MAIN"):
+ """Sets a new boolean value of a given key in a section.
+ Therefore it invertes the string representation of the boolean (in lowercase).
+
+ @param key: the key
+ @type key: string
+ @param value: the new value
+ @type value: boolean
+ @param section: the section
+ @type section: string
+
+ @raises KeyError: if section or key could not be found
+ @raises ValueError: if the old/new value is not a boolean"""
+
+ section = section.upper()
+ key = key.lower()
+
+ if not isinstance(value, types.BooleanType):
+ raise ValueError, "Passed value must be a boolean."
+
+ val = self.vars[section][key]
+ if val.is_bool():
+ if value is not val.boolean:
+ val.boolean = value
+ val.value = self._invert(val.value)
+ else:
+ raise ValueError, "\"%s\" is not a boolean." % key
+
+ def write (self):
+ """Writes file."""
+
+ for sec in self.vars:
+ for key in self.vars[sec]:
+ val = self.vars[sec][key]
+ if val.changed:
+ part1 = self.cache[val.line][:self.pos[val.line][0]] # key+DELIMITER
+ part2 = val.value # value
+ part3 = self.cache[val.line][self.pos[val.line][1]:] # everything behind the vale (\n in normal cases)
+ self.cache[val.line] = part1 + part2 + part3
+
+ # write
+ f = open(self.file, "w")
+ f.writelines(self.cache)
+ f.close()
+
+ # reload
+ self.__initialize()
+ self.parse()
diff --git a/geneticone/gui/gtk/windows.py b/geneticone/gui/gtk/windows.py
index 98bcba4..4b8cdda 100644
--- a/geneticone/gui/gtk/windows.py
+++ b/geneticone/gui/gtk/windows.py
@@ -209,9 +209,9 @@ class PreferenceWindow (AbstractDialog):
"""Sets all options in the Config-instance."""
for box in self.checkboxes:
- self.cfg.set(\
+ self.cfg.set_boolean(\
self.cfg.const[self.checkboxes[box]],\
- str(self.tree.get_widget(box).get_active()))
+ self.tree.get_widget(box).get_active())
for edit in self.edits:
self.cfg.set(\
diff --git a/geneticone/gui/gui_helper.py b/geneticone/gui/gui_helper.py
index 3e41501..d116877 100644
--- a/geneticone/gui/gui_helper.py
+++ b/geneticone/gui/gui_helper.py
@@ -15,13 +15,15 @@ from geneticone import backend
from geneticone.backend import flags
from geneticone.helper import *
+# parser
+from geneticone.config_parser import ConfigParser
+
# the wrapper
from wrapper import Console, Tree
# some stuff needed
from subprocess import Popen, PIPE, STDOUT
from threading import Thread
-from ConfigParser import SafeConfigParser
import pty
class Config:
@@ -42,25 +44,15 @@ class Config:
def __init__ (self, cfgFile):
"""Constructor.
- @attention: If cfgFile is a file, it is closed afterwards!
- @param cfgFile: path to config file or file-object of the config-file.
- @type cfgFile: string or file"""
+ @param cfgFile: path to config file
+ @type cfgFile: string"""
# init ConfigParser
- self._cfg = SafeConfigParser()
+ self._cfg = ConfigParser(cfgFile)
- # set correct file-obj
- if not isinstance(cfgFile, file):
- self._file = open(cfgFile) # assume string
- elif cfgFile.closed:
- self._file = open(cfgFile.name)
- else:
- self._file = cfgFile
-
# read config
- self._cfg.readfp(self._file)
- self._file.close()
+ self._cfg.parse()
# local configs
self.local = {}
@@ -75,7 +67,7 @@ class Config:
@return: the option's value
@rtype: string"""
- return self._cfg.get(section, name)
+ return self._cfg.get(name, section)
def get_boolean(self, name, section=const["main_sec"]):
"""Gets a boolean option.
@@ -87,7 +79,7 @@ class Config:
@return: the option's value
@rtype: boolean"""
- return self._cfg.getboolean(section, name)
+ return self._cfg.get_boolean(name, section)
def modify_flags_config (self):
"""Sets the internal config of the L{flags}-module.
@@ -150,16 +142,27 @@ class Config:
@param name: name of the option
@type name: string
@param val: value to set the option to
- @type val: string or boolean
+ @type val: string
+ @param section: section to look in; default is Main-Section
+ @type section: string"""
+
+ self._cfg.set(name, val, section)
+
+ def set_boolean (self, name, val, section=const["main_sec"]):
+ """Sets a boolean option.
+
+ @param name: name of the option
+ @type name: string
+ @param val: value to set the option to
+ @type val: boolean
@param section: section to look in; default is Main-Section
@type section: string"""
- self._cfg.set(section, name, val)
+ self._cfg.set_boolean(name, val, section)
def write(self):
"""Writes to the config file and modify any external configs."""
- self._file = open(self._file.name,"w")
- self._cfg.write(self._file)
+ self._cfg.write()
self.modify_external_configs()
class Database: