[SCM] GUI front-end for Debian Live. branch, master, updated. 11e67a56b24563c3f7a488602604f9ba897740c3

Chris Lamb chris at chris-lamb.co.uk
Tue Mar 4 03:01:19 UTC 2008


The following commit has been merged in the master branch:
commit f62a975316863ae347e34ff906321d762da756e5
Author: Chris Lamb <chris at chris-lamb.co.uk>
Date:   Tue Mar 4 03:00:11 2008 +0000

    Rewrite of configuration API.
    
    Signed-off-by: Chris Lamb <chris at chris-lamb.co.uk>

diff --git a/DebianLive/__init__.py b/DebianLive/__init__.py
new file mode 100644
index 0000000..e63f47e
--- /dev/null
+++ b/DebianLive/__init__.py
@@ -0,0 +1,36 @@
+from DebianLive import utils
+
+import os
+import commands
+
+class Config(object):
+    def __init__(self, dir, spec=None):
+        self.dir = dir
+
+        if spec is None:
+            # Load default field specification
+            from spec import spec
+
+        # Create skeleton lh_config dir, if it does not already exist
+        if not os.path.exists(os.path.join(self.dir, 'config')):
+            if not os.path.exists(self.dir):
+                os.makedirs(self.dir)
+            cmd = 'cd "%s"; lh_config' % os.path.abspath(self.dir)
+            result, out = commands.getstatusoutput(cmd)
+            if result != 0:
+                raise IOError, out
+
+        self.children = {}
+        for name, details in spec.iteritems():
+            elem_type = details[0]
+            elem = elem_type(self.dir, name, *details[1:])
+            self.children[name] = elem
+            setattr(self, name, elem)
+
+    def __str__(self):
+        from pprint import pformat
+        return '<DebianLive.Config dir="%s" %s>' % (self.dir, pformat(self.children))
+
+    def save(self):
+        for elem in self.children.values():
+            elem.save()
diff --git a/DebianLive/elements/__init__.py b/DebianLive/elements/__init__.py
new file mode 100644
index 0000000..5ce3bf7
--- /dev/null
+++ b/DebianLive/elements/__init__.py
@@ -0,0 +1,2 @@
+from folder_of_files import FolderOfFiles
+from key_var import KeyVar
diff --git a/DebianLive/elements/folder_of_files.py b/DebianLive/elements/folder_of_files.py
new file mode 100644
index 0000000..0addf98
--- /dev/null
+++ b/DebianLive/elements/folder_of_files.py
@@ -0,0 +1,123 @@
+import glob
+import os
+
+from os.path import join
+
+class FolderOfFiles(object):
+    """
+    Represents a folder containing a number of files.
+    """
+
+    def __init__(self, basedir, name, dir):
+        self.dir = os.path.join(basedir, 'config', dir)
+
+        self._stale = set()
+        self.files = {}
+
+        # No file has been deleted
+        self.file_deleted = False
+
+        self.load()
+
+    def __getitem__(self, k):
+        return self.files[k]
+
+    def __contains__(self, k):
+        return k in self.files
+
+    def __delitem__(self, k):
+        del self.files[k]
+
+    def __setitem__(self, k, v):
+        self._stale.add(k)
+        self.files[k] = v
+
+    def _config_exists(self, file):
+        try:
+            os.stat(join(self.dir, file))
+            return True
+        except OSError:
+            return False
+
+    def load(self):
+        """
+        Loads files.
+        """
+        self.deleted = False
+        self._stale.clear()
+        self.files.clear()
+        for name in glob.glob(join(self.dir, '*')):
+            key = name.split('/')[-1]
+            try:
+                f = open(name, 'r')
+                self.files[key] = f.read()
+                f.close()
+            except IOError, e:
+                # "Is a directory"
+                if e.errno == 21:
+                    continue
+                raise e
+
+    def save(self):
+        """
+        Update all updated files in this directory.
+        """
+        for filename in self._stale:
+            pathname = join(self.dir, filename)
+            f = open(pathname, 'w+')
+            f.write(self[filename])
+            f.close()
+
+        self._stale.clear()
+
+    def delete(self, hook_name):
+        if self._config_exists(hook_name):
+            os.remove(join(self.dir, hook_name))
+        del self[hook_name]
+        if hook_name in self._stale: self._stale.remove(hook_name)
+        self.file_deleted = True
+
+    def rename(self, orig, new):
+        """
+        Throws ValueError if 'new' already exists.
+        """
+        if self._config_exists(new):
+            raise ValueError
+        if self._config_exists(orig):
+            os.rename(join(self.dir, orig), join(self.dir, new))
+        if orig in self._stale: self._stale.remove(orig)
+        self[new] = self[orig]
+        del self[orig]
+
+    def import_file(self, source):
+        """
+        Imports the specified file into the current configuration, using a
+        unique name. The file is not saved.
+        """
+        f = open(source, 'r')
+        source_contents = f.read()
+        f.close()
+
+        target_name = self._gen_import_name(source)
+        self[target_name] = source_contents
+
+        return target_name
+
+    def _gen_import_name(self, filename):
+        """
+        Generates a unique name of the imported file.
+        """
+        # Use existing filename as the root
+        root = filename.split(os.sep)[-1]
+
+        if root in self:
+            # Keep adding a number to the end until it doesn't exist.
+            i = 1
+            while True:
+                tmpnam = "%s-%d" % (root, i)
+                if not tmpnam in self:
+                    return tmpnam
+                i += 1
+        else:
+            # Just use the root name
+            return root
diff --git a/DebianLive/elements/key_var.py b/DebianLive/elements/key_var.py
new file mode 100644
index 0000000..8c5e55e
--- /dev/null
+++ b/DebianLive/elements/key_var.py
@@ -0,0 +1,137 @@
+import re
+import os
+
+from DebianLive.utils import ListObserver
+
+SHELL_ESCAPES = (
+    (r'\ '[:-1], r'\\ '[:-1]),
+    (r'"',  r'\"'),
+    (r'`', r'\`'),
+    (r'$', r'\$'),
+    (r"'", r'\''),
+)
+
+REGEX = re.compile(r"""^\s*(\w+)=(?:(["\'])(([^\\\2]|\\.)*|)\2|((\w|\\["'])*))\s*(?:#.*)?$""")
+
+"""
+TODO
+ - Test case where:
+    >>> my_key_var = KeyVar('.', None, {'spam': list})
+    >>> print my_key_var['spam']
+    []
+    >>> print my_key_var['stale']
+    set([])
+    >>> print my_key_var['spam'] = ['spam']
+    >>> print my_key_var.stale
+    set(['spam'])
+    >>> my_key_var.save()
+    >>> print my_key_var.stale
+    set([])
+    >>> my_key_var['spam'].append('eggs')
+    >>> print my_key_var.stale
+    set([])  <-- Should be 'spam'
+"""
+
+class KeyVar(dict):
+    '''
+    Represents a POSIX shell KEY="VAR" configuration file.
+    '''
+
+    def __new__(cls, *args, **kwargs):
+        return dict.__new__(cls, *args, **kwargs)
+
+    def __init__(self, dir, name, spec):
+        self.filename = os.path.join(dir, 'config', name)
+
+        self.line_numbers = {}
+        self.stale = set()
+
+        f = open(self.filename, 'r')
+        try:
+            line_no = 1
+            for line in f:
+
+                # Check and parse key=value lines
+                match = REGEX.match(line)
+                if not match:
+                    continue
+
+                key = match.groups()[0]
+
+                # Find the correct match group
+                for m in match.groups()[2:]:
+                    if m is not None:
+                        val = m
+                        break
+
+                # Unescape value
+                for to, from_ in SHELL_ESCAPES:
+                    val = val.replace(from_, to)
+
+                # Save line number
+                self.line_numbers[key] = line_no
+
+                # Mutate to file type
+                val_type = spec.get(key, str)
+                typed_val = {
+                    int: lambda k, v: {'': None}.get(v, None),
+                    list: lambda k, v: ListObserver(v.split(), lambda: self.stale.add(k)),
+                    str: lambda k, v: v,
+                    bool: lambda k, v: {'enabled' : True, 'disabled' : False, 'yes' : True, 'no' : False}.get(v, None),
+                }[val_type](key, val)
+
+                # Save value
+                dict.__setitem__(self, key, typed_val)
+
+                line_no += 1
+        finally:
+            f.close()
+
+    def __setitem__(self, key, value):
+        self.stale.add(key)
+        dict.__setitem__(self, key, value)
+
+    def save(self):
+        """
+        Update all updated entries in the file.
+        """
+        if len(self.stale) == 0:
+            return
+
+        f = open(self.filename, 'r+')
+        lines = f.readlines()
+
+        for k in self.stale:
+            val = self[k]
+
+            # Escape value
+            if type(val) in (list, ListObserver):
+                for from_, to in SHELL_ESCAPES:
+                    val = map(lambda x: x.replace(from_, to), val)
+                val = map(str.strip, val)
+            elif type(val) is str:
+                for from_, to in SHELL_ESCAPES:
+                    val = val.replace(from_, to)
+
+            # Format value depending on its type
+            line_value = {
+                list : lambda v: " ".join(val),
+                bool : lambda v: {True: 'enabled', False: 'disabled'}.get(val, None),
+                str : lambda v: v,
+                type(None) : lambda v: "",
+            }[type(val)](val)
+
+            line = '%s="%s"\n' % (k, line_value)
+
+            try:
+                # Overwrite original line in file
+                lines[self.line_numbers[k] - 1] = line
+            except KeyError:
+                # Append line to end of file
+                lines.append("\n# The following option was added by live-magic\n")
+                lines.append(line)
+        f.close()
+
+        f = open(self.filename, 'w')
+        f.writelines(lines)
+        f.close()
diff --git a/DebianLive/spec.py b/DebianLive/spec.py
new file mode 100644
index 0000000..7139963
--- /dev/null
+++ b/DebianLive/spec.py
@@ -0,0 +1,116 @@
+from DebianLive.elements import FolderOfFiles
+from DebianLive.elements import KeyVar
+
+spec = {
+    'chroot_hooks': (FolderOfFiles, 'chroot_local-hooks'),
+    'binary_hooks': (FolderOfFiles, 'binary_local-hooks'),
+
+    'binary': (KeyVar, {
+        'LH_BINARY_FILESYSTEM': str,
+        'LH_BINARY_IMAGES': list,
+        'LH_BINARY_INDICES': bool,
+        'LH_BOOTAPPEND_LIVE': str,
+        'LH_BOOTAPPEND_INSTALL': str,
+        'LH_BOOTLOADER': str,
+        'LH_CHECKSUMS': bool,
+        'LH_CHROOT_BUILD': bool,
+        'LH_DEBIAN_INSTALLER': bool,
+        'LH_DEBIAN_INSTALLER_DAILY': bool,
+        'LH_ENCRYPTION': str,
+        'LH_GRUB_SPLASH': str,
+        'LH_HOSTNAME': str,
+        'LH_ISO_APPLICATION': str,
+        'LH_ISO_PREPARER': str,
+        'LH_ISO_PUBLISHER': str,
+        'LH_ISO_VOLUME': str,
+        'LH_JFFS2_ERASEBLOCK': str,
+        'LH_MEMTEST': str,
+        'LH_NET_ROOT_FILESYSTEM': str,
+        'LH_NET_ROOT_MOUNTOPTIONS': str,
+        'LH_NET_ROOT_PATH': str,
+        'LH_NET_ROOT_SERVER': str,
+        'LH_NET_COW_FILESYSTEM': str,
+        'LH_NET_COW_MOUNTOPTIONS': str,
+        'LH_NET_COW_PATH': str,
+        'LH_NET_COW_SERVER': str,
+        'LH_NET_TARBALL': str,
+        'LH_SYSLINUX_SPLASH': str,
+        'LH_SYSLINUX_TIMEOUT': int,
+        'LH_SYSLINUX_CFG': str,
+        'LH_SYSLINUX_MENU': bool,
+        'LH_SYSLINUX_MENU_LIVE_ENTRY': str,
+        'LH_SYSLINUX_MENU_LIVE_FAILSAFE_ENTRY': str,
+        'LH_SYSLINUX_MENU_MEMTEST_ENTRY': str,
+        'LH_USERNAME': str,
+    }),
+
+    'bootstrap': (KeyVar, {
+        'LH_ARCHITECTURE': str,
+        'LH_BOOTSTRAP_CONFIG': str,
+        'LH_BOOTSTRAP_INCLUDE': str,
+        'LH_BOOTSTRAP_EXCLUDE': str,
+        'LH_BOOTSTRAP_FLAVOUR': str,
+        'LH_BOOTSTRAP_KEYRING': str,
+        'LH_DISTRIBUTION': str,
+        'LH_MIRROR_BOOTSTRAP': str,
+        'LH_MIRROR_CHROOT': str,
+        'LH_MIRROR_CHROOT_SECURITY': str,
+        'LH_MIRROR_BINARY': str,
+        'LH_MIRROR_BINARY_SECURITY': str,
+        'LH_SECTIONS': list,
+    }),
+
+    'chroot': (KeyVar, {
+        'LH_CHROOT_FILESYSTEM': str,
+        'LH_UNION_FILESYSTEM': str,
+        'LH_EXPOSED_ROOT': bool,
+        'LH_HOOKS': list,
+        'LH_INTERACTIVE': bool,
+        'LH_KEYRING_PACKAGES': list,
+        'LH_LANGUAGE': str,
+        'LH_LINUX_FLAVOURS': list,
+        'LH_LINUX_PACKAGES': list,
+        'LH_PACKAGES': list,
+        'LH_PACKAGES_LISTS': str,
+        'LH_TASKS': str,
+        'LH_SECURITY': bool,
+        'LH_SYMLINKS': bool,
+        'LH_SYSVINIT': bool,
+    }),
+
+    'common': (KeyVar, {
+        'LH_APT': str,
+        'LH_APT_FTP_PROXY': str,
+        'LH_APT_HTTP_PROXY': str,
+        'LH_APT_PDIFFS': bool,
+        'LH_APT_PIPELINE': int,
+        'LH_APT_RECOMMENDS': bool,
+        'LH_APT_SECURE': bool,
+        'LH_BOOTSTRAP': str,
+        'LH_CACHE': bool,
+        'LH_CACHE_INDICES': bool,
+        'LH_CACHE_STAGES': list,
+        'LH_DEBCONF_FRONTEND': str,
+        'LH_DEBCONF_NOWARNINGS': bool,
+        'LH_DEBCONF_PRIORITY': str,
+        'LH_INITRAMFS': str,
+        'LH_FDISK': str,
+        'LH_LOSETUP': str,
+        'LH_MODE': str,
+        'LH_USE_FAKEROOT': str,
+        'LH_TASKSEL': str,
+        'LH_INCLUDES': str,
+        'LH_TEMPLATES': str,
+
+        'LH_BREAKPOINTS': bool,
+        'LH_DEBUG': bool,
+        'LH_FORCE': bool,
+        'LH_VERBOSE': bool,
+        'LH_QUIET': bool,
+    }),
+
+   'source': (KeyVar, {
+       'LH_SOURCE': bool,
+       'LH_SOURCE_IMAGES': list,
+    }),
+}
diff --git a/DebianLive/utils/__init__.py b/DebianLive/utils/__init__.py
new file mode 100644
index 0000000..764dfe2
--- /dev/null
+++ b/DebianLive/utils/__init__.py
@@ -0,0 +1,2 @@
+from sources_list import SourcesList
+from list_observer import ListObserver
diff --git a/DebianLive/utils/list_observer.py b/DebianLive/utils/list_observer.py
new file mode 100644
index 0000000..4be8771
--- /dev/null
+++ b/DebianLive/utils/list_observer.py
@@ -0,0 +1,68 @@
+class ListObserver(list):
+    """
+    Observed list implementation.
+
+    Calls fn_observed whenever you alter the list.
+
+    >>> my_list = ['spam', 'eggs']
+    >>> def fn(): print "Observed."
+    >>> my_list = list_observer(my_list, fn)
+    >>> my_list.append('bacon')
+    Observed.
+    >>> my_list.pop()
+    Observed.
+    bacon
+    >>>
+
+    """
+
+    def __init__(self, value, fn_observed):
+        list.__init__(self, value)
+        self.fn_observed = fn_observed
+
+    def __iter__(self):
+        return list.__iter__(self)
+
+    def __setitem__(self,key,value):
+        list.__setitem__(self, key, value)
+        self.fn_observed()
+
+    def __delitem__(self,key):
+        list.__delitem__(self, key)
+        self.fn_observed()
+
+    def __setslice__(self, i, j, sequence):
+        list.__setslice__(self, i, j, sequence)
+        self.fn_observed()
+
+    def __delslice__(self, i, j):
+        list.__delslice__(self, i, j)
+        self.fn_observed()
+
+    def append(self, value):
+        list.append(self, value)
+        self.fn_observed()
+
+    def pop(self):
+        self.fn_observed()
+        return list.pop(self)
+
+    def extend(self, newvalue):
+        self.fn_observed()
+        list.extend(self, newvalue)
+
+    def insert(self, i, element):
+        list.insert(self, i, element)
+        self.fn_observed()
+
+    def remove(self, element):
+        list.remove(self, element)
+        self.fn_observed()
+
+    def reverse(self):
+        list.reverse(self)
+        self.fn_observed()
+
+    def sort(self, cmpfunc=None):
+        list.sort(self, cmpfunc)
+        self.fn_observed()

-- 
GUI front-end for Debian Live.



More information about the debian-live-changes mailing list