summaryrefslogtreecommitdiff
path: root/portato
diff options
context:
space:
mode:
authorRené 'Necoro' Neumann <necoro@necoro.net>2009-02-17 01:19:23 +0100
committerRené 'Necoro' Neumann <necoro@necoro.net>2009-02-17 01:19:23 +0100
commit7bd265f8c8537d235b24db59463c505629d9c207 (patch)
treefeda7a0668f71f3e2d296491ecbd690287880824 /portato
parent98105652b0e02d9c633a5e61ced64e30adb70cde (diff)
parenteab7389888eb97ddcdf174acce717f45157ed079 (diff)
downloadportato-7bd265f8c8537d235b24db59463c505629d9c207.tar.gz
portato-7bd265f8c8537d235b24db59463c505629d9c207.tar.bz2
portato-7bd265f8c8537d235b24db59463c505629d9c207.zip
Merge in DB branch (even though it does not offer the change-db-in-prefs feature)
Diffstat (limited to '')
-rw-r--r--portato/backend/exceptions.py4
-rw-r--r--portato/constants.py3
-rw-r--r--portato/db/__init__.py47
-rw-r--r--portato/db/database.py99
-rw-r--r--portato/db/dict.py153
-rw-r--r--portato/db/sql.py236
-rw-r--r--portato/gui/dialogs.py7
-rw-r--r--portato/gui/utils.py428
-rw-r--r--portato/gui/windows/main.py35
-rw-r--r--portato/session.py48
10 files changed, 621 insertions, 439 deletions
diff --git a/portato/backend/exceptions.py b/portato/backend/exceptions.py
index 8e442f6..f20a33e 100644
--- a/portato/backend/exceptions.py
+++ b/portato/backend/exceptions.py
@@ -18,6 +18,10 @@ class PackageNotFoundException (Exception):
"""An exception marking that a package could not be found."""
pass
+class VersionsNotFoundException (Exception):
+ """An exception marking that there could be no versions found for a specific CP."""
+ pass
+
class DependencyCalcError (Exception):
"""An error occured during dependency calculation."""
pass
diff --git a/portato/constants.py b/portato/constants.py
index 06b3595..3ab5a80 100644
--- a/portato/constants.py
+++ b/portato/constants.py
@@ -22,8 +22,6 @@ These should be set during the installation.
@type HOME: string
@var SU_COMMAND: command to execute to "su"
@type SU_COMMAND: string
-@var USE_SQL: whether to use the sqlite db
-@type USE_SQL: boolean
@var CONFIG_DIR: The configuration directory.
@type CONFIG_DIR: string
@@ -58,7 +56,6 @@ APP = "portato"
VERSION = "9999"
HOME = os.environ["HOME"]
SU_COMMAND = "gksu -D 'Portato'"
-USE_SQL = True
# config
CONFIG_DIR = "/etc/portato/"
diff --git a/portato/db/__init__.py b/portato/db/__init__.py
new file mode 100644
index 0000000..05dbfcf
--- /dev/null
+++ b/portato/db/__init__.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/db/__init__.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2009 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 __future__ import absolute_import
+
+from ..session import Session, SectionDict
+from ..helper import debug, warning
+
+_SESSION = None
+_TYPE = None
+
+def _set_type(t):
+ global _TYPE
+ _TYPE = t
+
+def Database():
+ global _SESSION, _TYPE
+
+ if _SESSION is None:
+ _SESSION = Session("db.cfg", name = "DB")
+ _SESSION.add_handler((["type"], _set_type, lambda: _TYPE), default = ["sql"])
+ _SESSION.load()
+
+ if _TYPE == "sql":
+ debug("Using SQLDatabase")
+ try:
+ from .sql import SQLDatabase
+ except ImportError:
+ warning(_("Cannot load SQLDatabase."))
+ _TYPE = "dict"
+ return Database()
+ else:
+ return SQLDatabase(SectionDict(_SESSION, "SQL"))
+
+ elif _TYPE == "dict":
+ debug("Using DictDatabase")
+ from .dict import DictDatabase
+ return DictDatabase(SectionDict(_SESSION, "dict"))
diff --git a/portato/db/database.py b/portato/db/database.py
new file mode 100644
index 0000000..7a23e5e
--- /dev/null
+++ b/portato/db/database.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/db/database.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2009 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 __future__ import absolute_import, with_statement
+
+from threading import RLock
+from functools import wraps
+
+class PkgData (object):
+ __slots__ = ("cat", "pkg", "inst", "disabled")
+
+ def __init__ (self, cat, pkg, inst = False, disabled = False):
+ self.cat = cat
+ self.pkg = pkg
+ self.inst = inst
+ self.disabled = disabled
+
+ def __iter__ (self):
+ return iter((self.cat, self.pkg, self.inst, self.disabled))
+
+ def __cmp__ (self, other):
+ return cmp(self.pkg.lower(), other.pkg.lower())
+
+ def __repr__ (self):
+ return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst}
+
+class Database (object):
+
+ ALL = _("ALL")
+
+ def __init__ (self):
+ self._lock = RLock()
+
+ @staticmethod
+ def lock (f):
+ @wraps(f)
+ def wrapper (self, *args, **kwargs):
+ with self._lock:
+ r = f(self, *args, **kwargs)
+
+ return r
+
+ return wrapper
+
+ def populate (self, category = None):
+ """Populates the database.
+
+ @param category: An optional category - so only packages of this category are inserted.
+ @type category: string
+ """
+ raise NotImplentedError
+
+ def get_cat (self, cat = None, byName = True, showDisabled = False):
+ """Returns the packages in the category.
+
+ @param cat: category to return the packages from; if None it defaults to C{ALL}
+ @type cat: string
+ @param byName: selects whether to return the list sorted by name or by installation
+ @type byName: boolean
+ @param showDisabled: should disabled packages be returned
+ @type showDisabled: boolean
+ @return: an iterator over the packages
+ @rtype: L{PkgData}<iterator>
+ """
+ raise NotImplentedError
+
+ def get_categories (self, installed = False):
+ """Returns all categories.
+
+ @param installed: Only return these with at least one installed package.
+ @type installed: boolean
+ @returns: the list of categories
+ @rtype: string<iterator>
+ """
+ raise NotImplentedError
+
+ def disable (self, cpv):
+ """Marks the CPV as disabled.
+
+ @param cpv: the cpv to mark
+ """
+ raise NotImplentedError
+
+ def reload (self, cat = None):
+ """Reloads the given category.
+
+ @param cat: category
+ @type cat: string
+ """
+ raise NotImplentedError
diff --git a/portato/db/dict.py b/portato/db/dict.py
new file mode 100644
index 0000000..f6edea6
--- /dev/null
+++ b/portato/db/dict.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/db/dict.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2009 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 __future__ import absolute_import, with_statement
+
+import re
+from collections import defaultdict
+from threading import RLock
+
+from ..backend import system
+from .database import Database, PkgData
+
+class DictDatabase (Database):
+ """An internal database which holds a simple dictionary cat -> [package_list]."""
+
+ lock = Database.lock
+
+ def __init__ (self, session):
+ """Constructor."""
+ Database.__init__(self)
+ self.session = session
+
+ self.__initialize()
+ self.populate()
+
+ def __initialize (self):
+ self._db = defaultdict(list)
+ self.inst_cats = set([self.ALL])
+ self._restrict = None
+
+ def __sort_key (self, x):
+ return x.pkg.lower()
+
+ @lock
+ def populate (self, category = None):
+
+ # get the lists
+ packages = system.find_packages(category, with_version = False)
+ installed = system.find_packages(category, system.SET_INSTALLED, with_version = False)
+
+ # cycle through packages
+ for p in packages:
+ cat, pkg = p.split("/")
+ inst = p in installed
+ t = PkgData(cat, pkg, inst, False)
+ self._db[cat].append(t)
+ self._db[self.ALL].append(t)
+
+ if inst:
+ self.inst_cats.add(cat)
+
+ for key in self._db: # sort alphabetically
+ self._db[key].sort(key = self.__sort_key)
+
+ @lock
+ def get_cat (self, cat = None, byName = True, showDisabled = False):
+ if not cat:
+ cat = self.ALL
+
+ def get_pkgs():
+ if byName:
+ for pkg in self._db[cat]:
+ if showDisabled or not pkg.disabled:
+ yield pkg
+ else:
+ ninst = []
+ for pkg in self._db[cat]:
+ if not showDisabled and pkg.disabled: continue
+
+ if pkg.inst:
+ yield pkg
+ else:
+ ninst.append(pkg)
+
+ for pkg in ninst:
+ yield pkg
+
+ try:
+ if self.restrict:
+ return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.pkg))#if pkg[1].find(self.restrict) != -1)
+ else:
+ return get_pkgs()
+
+ except KeyError: # cat is in category list - but not in portage
+ info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat)
+
+ @lock
+ def get_categories (self, installed = False):
+ if not self.restrict:
+ if installed:
+ cats = self.inst_cats
+ else:
+ cats = self._db.iterkeys()
+
+ else:
+ if installed:
+ cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst))
+ else:
+ cats = set((pkg.cat for pkg in self.get_cat(self.ALL)))
+
+ if len(cats)>1:
+ cats.add(self.ALL)
+
+ return (cat for cat in cats)
+
+ @lock
+ def reload (self, cat = None):
+ if cat:
+ del self._db[cat]
+ try:
+ self.inst_cats.remove(cat)
+ except KeyError: # not in inst_cats - can be ignored
+ pass
+
+ self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL])
+ self.populate(cat+"/*")
+ else:
+ self.__initialize()
+ self.populate()
+
+ @lock
+ def disable (self, cpv):
+ cat, pkg = cpv.split("/")
+
+ c = self._db[cat]
+ p = c[c.index(PkgData(cat, pkg))]
+ p.disabled = True
+
+ def get_restrict (self):
+ return self._restrict
+
+ @lock
+ def set_restrict (self, restrict):
+ if not restrict:
+ self._restrict = None
+ else:
+ try:
+ regex = re.compile(restrict, re.I)
+ except re.error, e:
+ info(_("Error while compiling search expression: '%s'."), str(e))
+ else: # only set self._restrict if no error occurred
+ self._restrict = regex
+
+ restrict = property(get_restrict, set_restrict)
diff --git a/portato/db/sql.py b/portato/db/sql.py
new file mode 100644
index 0000000..ca01fd0
--- /dev/null
+++ b/portato/db/sql.py
@@ -0,0 +1,236 @@
+# -*- coding: utf-8 -*-
+#
+# File: portato/db/sql.py
+# This file is part of the Portato-Project, a graphical portage-frontend.
+#
+# Copyright (C) 2006-2009 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 __future__ import absolute_import, with_statement
+
+try:
+ import sqlite3 as sql
+except ImportError:
+ from pysqlite2 import dbapi2 as sql
+
+import anydbm
+import hashlib
+import os
+
+from functools import wraps
+
+from ..constants import SESSION_DIR
+from ..helper import info, error, debug
+from ..backend import system
+from .database import Database, PkgData
+
+class SQLDatabase (Database):
+
+ FORMAT = "1"
+ FORBIDDEN = (".bzr", ".svn", ".git", "CVS", ".hg", "_darcs")
+ lock = Database.lock
+
+ def __init__ (self, session):
+ """Constructor."""
+ Database.__init__(self)
+
+ self._restrict = ""
+ self.session = session
+
+ updateFormat = False
+ if "format" not in session or session["format"] != self.FORMAT:
+ session["format"] = self.FORMAT
+ updateFormat = True
+
+ pkgdb = os.path.join(SESSION_DIR, "package.db")
+ pkgdb_existed = os.path.exists(pkgdb)
+
+ if pkgdb_existed:
+ debug("package.db already existant")
+ else:
+ debug("package.db not existant")
+
+ pkg_conn = sql.connect(os.path.join(SESSION_DIR, "package.db"))
+ pkg_conn.row_factory = sql.Row
+ if pkgdb_existed and updateFormat:
+ pkg_conn.execute("DROP TABLE packages")
+
+ pkg_conn.execute("""
+ CREATE TABLE IF NOT EXISTS packages
+ (
+ name TEXT,
+ cat TEXT,
+ inst INTEGER,
+ disabled INTEGER
+ )""")
+
+ pkg_conn.commit()
+
+ self.was_updated = self.updated()
+ if self.was_updated or not pkgdb_existed or updateFormat:
+ info(_("Cleaning database..."))
+ pkg_conn.execute("DELETE FROM packages") # empty db at beginning
+ info(_("Populating database..."))
+ self.populate(connection = pkg_conn)
+
+ pkg_conn.close()
+
+ descr_conn = sql.connect(os.path.join(SESSION_DIR, "descr.db"))
+ descr_conn.execute("""
+ CREATE TABLE IF NOT EXISTS descriptions
+ (
+ cp TEXT,
+ descr TEXT
+ )""")
+ descr_conn.close()
+
+ def updated (self):
+ changed = False
+
+ def walk (path):
+ debug("Walking %s", path)
+
+ for root, dirs, files in os.walk(path):
+ for f in files:
+ path = os.path.join(root, f)
+ yield "%s %s" % (f, os.stat(path).st_mtime)
+
+ for forbidden in self.FORBIDDEN:
+ if forbidden in dirs:
+ dirs.remove(forbidden)
+
+ overlays = system.get_global_settings("PORTDIR_OVERLAY").split()
+ hashes = {}
+ for overlay in overlays:
+ hashes[overlay] = hashlib.md5("".join(walk(overlay))).hexdigest()
+
+ timestamp = os.path.join(system.get_global_settings("PORTDIR"), "metadata/timestamp")
+ hashes["ROOT"] = hashlib.md5("%s %s" % (timestamp, os.stat(timestamp).st_mtime)).hexdigest()
+
+ dbpath = os.path.join(SESSION_DIR, "portdirs.db")
+ db_existed = os.path.exists(dbpath)
+ db = anydbm.open(dbpath, "c")
+ try:
+ if db_existed:
+ debug("portdirs.db already existant")
+ for key in set(db.keys())- set(hashes.keys()):
+ debug("Overlay '%s' has been removed", key)
+ del db[key]
+ changed = True
+
+ for key in hashes.iterkeys():
+
+ if key not in db.keys():
+ debug("Overlay '%s' has been added.", key)
+ changed = True
+
+ elif db[key] != hashes[key]:
+ debug("Overlay '%s' has been changed.", key)
+ changed = True
+
+ db[key] = hashes[key]
+ else:
+ debug("portdirs.db not existant")
+ for key in hashes.iterkeys():
+ db[key] = hashes[key]
+
+ finally:
+ db.close()
+
+ return changed
+
+ def con (f):
+ @wraps(f)
+ def wrapper (*args, **kwargs):
+ if not "connection" in kwargs:
+ con= sql.connect(os.path.join(SESSION_DIR, "package.db"))
+ con.row_factory = sql.Row
+ kwargs["connection"] = con
+
+ return f(*args, **kwargs)
+
+ return Database.lock(wrapper)
+
+ @con
+ def populate (self, category = None, connection = None):
+ def _get():
+ # get the lists
+ inst = system.find_packages(pkgSet = system.SET_INSTALLED, key=category, with_version = False)
+ for p in system.find_packages(key = category, with_version = False):
+ cat, pkg = p.split("/")
+
+ yield (cat, pkg, p in inst, False)
+
+ connection.executemany("INSERT INTO packages (cat, name, inst, disabled) VALUES (?, ?, ?, ?)", _get())
+ connection.commit()
+
+ @con
+ def get_cat (self, category = None, byName = True, showDisabled = False, connection = None):
+ sort = "ORDER BY name"
+ if not byName:
+ sort = "ORDER BY inst DESC, name"
+
+ disabled = "1=1"
+ if not showDisabled:
+ disabled = "disabled = 0"
+
+ if not category or category == self.ALL:
+ c = connection.execute("SELECT cat, name, inst, disabled FROM packages WHERE %s %s %s" % (disabled, self.restrict, sort))
+ else:
+ c = connection.execute("SELECT cat, name, inst, disabled FROM packages WHERE cat = ? AND %s %s %s" % (disabled, self.restrict ,sort), (category,))
+
+ for pkg in c:
+ yield PkgData(pkg["cat"], pkg["name"], pkg["inst"], pkg["disabled"])
+ c.close()
+
+ @con
+ def get_categories (self, installed = False, connection = None):
+
+ if installed:
+ where = "inst = 1"
+ else:
+ where = "1 = 1"
+
+ c = connection.execute("SELECT cat FROM packages WHERE disabled = 0 AND %s %s GROUP BY cat" % (where, self.restrict))
+
+ l = c.fetchall()
+ c.close()
+
+ if len(l) > 1:
+ yield self.ALL
+
+ for cat in l:
+ yield cat["cat"]
+
+ @con
+ def reload (self, cat = None, connection = None):
+ if cat:
+ connection.execute("DELETE FROM packages WHERE cat = ?", (cat,))
+ connection.commit()
+ self.populate(cat+"/*", connection = connection)
+ else:
+ connection.execute("DELETE FROM packages")
+ connection.commit()
+ self.populate(connection = connection)
+
+ @con
+ def disable (self, cpv, connection = None):
+ cat, pkg = cpv.split("/")
+ connection.execute("UPDATE packages SET disabled = 1 WHERE cat = ? AND name = ?", (cat, pkg))
+ connection.commit()
+
+ def get_restrict (self):
+ return self._restrict
+
+ @lock
+ def set_restrict (self, restrict):
+ if not restrict:
+ self._restrict = ""
+ else:
+ self._restrict = "AND name LIKE '%%%s%%'" % restrict
+
+ restrict = property(get_restrict, set_restrict)
diff --git a/portato/gui/dialogs.py b/portato/gui/dialogs.py
index 3e1fdce..d7ac41b 100644
--- a/portato/gui/dialogs.py
+++ b/portato/gui/dialogs.py
@@ -118,3 +118,10 @@ def prereq_error_dialog (e):
ret = dialog.run()
dialog.destroy()
return ret
+
+def no_versions_dialog (cp):
+ dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _("No versions of package '%s' found!") % cp)
+ ret = dialog.run()
+ dialog.destroy()
+ return ret
+
diff --git a/portato/gui/utils.py b/portato/gui/utils.py
index a779329..1ebefe8 100644
--- a/portato/gui/utils.py
+++ b/portato/gui/utils.py
@@ -13,29 +13,17 @@
from __future__ import absolute_import, with_statement
# some stuff needed
-import re
-import sys, os
+import sys
import logging
import gettext
-from collections import defaultdict
-from threading import Thread, RLock
-from functools import wraps
+from threading import Thread
import gtk
# some backend things
-from ..backend import flags, system, set_system
+from ..backend import flags, set_system
from ..helper import debug, info, set_log_level
-from ..constants import APP, LOCALE_DIR, USE_SQL, SESSION_DIR
-
-if USE_SQL:
- try:
- import sqlite3 as sql
- except ImportError:
- from pysqlite2 import dbapi2 as sql
-
- import anydbm
- import hashlib
+from ..constants import APP, LOCALE_DIR
# parser
from ..config_parser import ConfigParser
@@ -156,411 +144,3 @@ class Config (ConfigParser):
"""Writes to the config file and modify any external configs."""
ConfigParser.write(self)
self.modify_external_configs()
-
-class PkgData (object):
- __slots__ = ("cat", "pkg", "inst")
-
- def __init__ (self, cat, pkg, inst):
- self.cat = cat
- self.pkg = pkg
- self.inst = inst
-
- def __iter__ (self):
- return iter((self.cat, self.pkg, self.inst))
-
- def __cmp__ (self, other):
- return cmp(self.pkg.lower(), other.pkg.lower())
-
- def __repr__ (self):
- return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst}
-
-class DictDatabase (object):
- """An internal database which holds a simple dictionary cat -> [package_list]."""
-
- ALL = _("ALL")
-
- def __init__ (self):
- """Constructor."""
- self.__initialize()
- self._lock = RLock()
- self.populate()
-
- def lock (f):
- @wraps(f)
- def wrapper (self, *args, **kwargs):
- with self._lock:
- r = f(self, *args, **kwargs)
- return r
-
- return wrapper
-
- def __initialize (self):
- self._db = defaultdict(list)
- self.inst_cats = set([self.ALL])
- self._restrict = None
-
- def __sort_key (self, x):
- return x.pkg.lower()
-
- @lock
- def populate (self, category = None):
- """Populates the database.
-
- @param category: An optional category - so only packages of this category are inserted.
- @type category: string
- """
-
- # get the lists
- packages = system.find_packages(category, with_version = False)
- installed = system.find_packages(category, system.SET_INSTALLED, with_version = False)
-
- # cycle through packages
- for p in packages:
- cat, pkg = p.split("/")
- inst = p in installed
- t = PkgData(cat, pkg, inst)
- self._db[cat].append(t)
- self._db[self.ALL].append(t)
-
- if inst:
- self.inst_cats.add(cat)
-
- for key in self._db: # sort alphabetically
- self._db[key].sort(key = self.__sort_key)
-
- @lock
- def get_cat (self, cat = None, byName = True):
- """Returns the packages in the category.
-
- @param cat: category to return the packages from; if None it defaults to "ALL"
- @type cat: string
- @param byName: selects whether to return the list sorted by name or by installation
- @type byName: boolean
- @return: an iterator over a list of tuples: (category, name, is_installed) or []
- @rtype: (string, string, boolean)<iterator>
- """
-
- if not cat:
- cat = self.ALL
-
- def get_pkgs():
- if byName:
- for pkg in self._db[cat]:
- yield pkg
- else:
- ninst = []
- for pkg in self._db[cat]:
- if pkg.inst:
- yield pkg
- else:
- ninst.append(pkg)
-
- for pkg in ninst:
- yield pkg
-
- try:
- if self.restrict:
- return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.pkg))#if pkg[1].find(self.restrict) != -1)
- else:
- return get_pkgs()
-
- except KeyError: # cat is in category list - but not in portage
- info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat)
-
- @lock
- def get_categories (self, installed = False):
- """Returns all categories.
-
- @param installed: Only return these with at least one installed package.
- @type installed: boolean
- @returns: the list of categories
- @rtype: string<iterator>
- """
-
- if not self.restrict:
- if installed:
- cats = self.inst_cats
- else:
- cats = self._db.iterkeys()
-
- else:
- if installed:
- cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst))
- else:
- cats = set((pkg.cat for pkg in self.get_cat(self.ALL)))
-
- if len(cats)>1:
- cats.add(self.ALL)
-
- return (cat for cat in cats)
-
- @lock
- def reload (self, cat = None):
- """Reloads the given category.
-
- @param cat: category
- @type cat: string
- """
-
- if cat:
- del self._db[cat]
- try:
- self.inst_cats.remove(cat)
- except KeyError: # not in inst_cats - can be ignored
- pass
-
- self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL])
- self.populate(cat+"/*")
- else:
- self.__initialize()
- self.populate()
-
- def get_restrict (self):
- return self._restrict
-
- @lock
- def set_restrict (self, restrict):
- if not restrict:
- self._restrict = None
- else:
- try:
- regex = re.compile(restrict, re.I)
- except re.error, e:
- info(_("Error while compiling search expression: '%s'."), str(e))
- else: # only set self._restrict if no error occurred
- self._restrict = regex
-
- restrict = property(get_restrict, set_restrict)
-
-class SQLDatabase (object):
-
- ALL = _("ALL")
- FORBIDDEN = (".bzr", ".svn", ".git", "CVS", ".hg", "_darcs")
-
- def __init__ (self):
- """Constructor."""
- self._restrict = ""
- self._lock = RLock()
-
- pkgdb = os.path.join(SESSION_DIR, "package.db")
- pkgdb_existed = os.path.exists(pkgdb)
-
- if pkgdb_existed:
- debug("package.db already existant")
- else:
- debug("package.db not existant")
-
- pkg_conn = sql.connect(os.path.join(SESSION_DIR, "package.db"))
- pkg_conn.row_factory = sql.Row
- pkg_conn.execute("""
- CREATE TABLE IF NOT EXISTS packages
- (
- name TEXT,
- cat TEXT,
- inst INTEGER
- )""")
-
- pkg_conn.commit()
-
- self.was_updated = self.updated()
- if self.was_updated or not pkgdb_existed:
- info(_("Cleaning database..."))
- pkg_conn.execute("DELETE FROM packages") # empty db at beginning
- info(_("Populating database..."))
- self.populate(connection = pkg_conn)
-
- pkg_conn.close()
-
- descr_conn = sql.connect(os.path.join(SESSION_DIR, "descr.db"))
- descr_conn.execute("""
- CREATE TABLE IF NOT EXISTS descriptions
- (
- cp TEXT,
- descr TEXT
- )""")
- descr_conn.close()
-
- def updated (self):
- changed = False
-
- def walk (path):
- debug("Walking %s", path)
-
- for root, dirs, files in os.walk(path):
- for f in files:
- path = os.path.join(root, f)
- yield "%s %s" % (f, os.stat(path).st_mtime)
-
- for forbidden in self.FORBIDDEN:
- if forbidden in dirs:
- dirs.remove(forbidden)
-
- overlays = system.get_global_settings("PORTDIR_OVERLAY").split()
- hashes = {}
- for overlay in overlays:
- hashes[overlay] = hashlib.md5("".join(walk(overlay))).hexdigest()
-
- timestamp = os.path.join(system.get_global_settings("PORTDIR"), "metadata/timestamp")
- hashes["ROOT"] = hashlib.md5("%s %s" % (timestamp, os.stat(timestamp).st_mtime)).hexdigest()
-
- dbpath = os.path.join(SESSION_DIR, "portdirs.db")
- db_existed = os.path.exists(dbpath)
- db = anydbm.open(dbpath, "c")
- try:
- if db_existed:
- debug("portdirs.db already existant")
- for key in set(db.keys())- set(hashes.keys()):
- debug("Overlay '%s' has been removed", key)
- del db[key]
- changed = True
-
- for key in hashes.iterkeys():
-
- if key not in db.keys():
- debug("Overlay '%s' has been added.", key)
- changed = True
-
- elif db[key] != hashes[key]:
- debug("Overlay '%s' has been changed.", key)
- changed = True
-
- db[key] = hashes[key]
- else:
- debug("portdirs.db not existant")
- for key in hashes.iterkeys():
- db[key] = hashes[key]
-
- finally:
- db.close()
-
- return changed
-
- def lock (f):
- @wraps(f)
- def wrapper (self, *args, **kwargs):
- with self._lock:
- r = f(self, *args, **kwargs)
-
- return r
-
- return wrapper
-
- def con (f):
- @wraps(f)
- def wrapper (*args, **kwargs):
- if not "connection" in kwargs:
- con= sql.connect(os.path.join(SESSION_DIR, "package.db"))
- con.row_factory = sql.Row
- kwargs["connection"] = con
-
- return f(*args, **kwargs)
-
- return wrapper
-
- @lock
- @con
- def populate (self, category = None, connection = None):
- """Populates the database.
-
- @param category: An optional category - so only packages of this category are inserted.
- @type category: string
- """
-
- def _get():
- # get the lists
- inst = system.find_packages(pkgSet = system.SET_INSTALLED, key=category, with_version = False)
- for p in system.find_packages(key = category, with_version = False):
- cat, pkg = p.split("/")
-
- yield (cat, pkg, p in inst)
-
- connection.executemany("INSERT INTO packages (cat, name, inst) VALUES (?, ?, ?)", _get())
- connection.commit()
-
- @lock
- @con
- def get_cat (self, category = None, byName = True, connection = None):
- """Returns the packages in the category.
-
- @param cat: category to return the packages from; if None it defaults to "ALL"
- @type cat: string
- @param byName: selects whether to return the list sorted by name or by installation
- @type byName: boolean
- @return: an iterator over a list of tuples: (category, name, is_installed) or []
- @rtype: L{PkgData}<iterator>
- """
-
- sort = "ORDER BY name"
- if not byName:
- sort = "ORDER BY inst DESC, name"
-
- if not category or category == self.ALL:
- c = connection.execute("SELECT cat, name, inst FROM packages WHERE 1=1 %s %s" % (self.restrict, sort))
- else:
- c = connection.execute("SELECT cat, name, inst FROM packages WHERE cat = ? %s %s" % (self.restrict ,sort), (category,))
-
- for pkg in c:
- yield PkgData(pkg["cat"], pkg["name"], pkg["inst"])
- c.close()
-
- @lock
- @con
- def get_categories (self, installed = False, connection = None):
- """Returns all categories.
-
- @param installed: Only return these with at least one installed package.
- @type installed: boolean
- @returns: the list of categories
- @rtype: string<iterator>
- """
-
- if installed:
- where = "inst = 1"
- else:
- where = "1 = 1"
-
- c = connection.execute("SELECT cat FROM packages WHERE %s %s GROUP BY cat" % (where, self.restrict))
-
- l = c.fetchall()
- c.close()
-
- if len(l) > 1:
- yield self.ALL
-
- for cat in l:
- yield cat["cat"]
-
- @lock
- @con
- def reload (self, cat = None, connection = None):
- """Reloads the given category.
-
- @param cat: category
- @type cat: string
- """
-
- if cat:
- connection.execute("DELETE FROM packages WHERE cat = ?", (cat,))
- connection.commit()
- self.populate(cat+"/*", connection = connection)
- else:
- connection.execute("DELETE FROM packages")
- connection.commit()
- self.populate(connection = connection)
-
- def get_restrict (self):
- return self._restrict
-
- @lock
- def set_restrict (self, restrict):
- if not restrict:
- self._restrict = ""
- else:
- self._restrict = "AND name LIKE '%%%s%%'" % restrict
-
- restrict = property(get_restrict, set_restrict)
-
-if USE_SQL:
- Database = SQLDatabase
-else:
- Database = DictDatabase
diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py
index 87a13dc..8d5e5ae 100644
--- a/portato/gui/windows/main.py
+++ b/portato/gui/windows/main.py
@@ -26,18 +26,20 @@ from ...backend import flags, system # must be the first to avoid circular deps
from ... import get_listener, plugin
from ...helper import debug, warning, error, info, unique_array
from ...session import Session
+from ...db import Database
from ...constants import CONFIG_LOCATION, VERSION, APP_ICON, ICON_DIR
-from ...backend.exceptions import PackageNotFoundException, BlockedException
+from ...backend.exceptions import PackageNotFoundException, BlockedException, VersionsNotFoundException
# more GUI stuff
-from ..utils import Database, Config, GtkThread, get_color
+from ..utils import Config, GtkThread, get_color
from ..queue import EmergeQueue
from ..session import SESSION_VERSION, SessionException, OldSessionException, NewSessionException
from ..wrapper import GtkTree, GtkConsole
from ..views import LogView, HighlightView, InstalledOnlyView, LazyStoreView
from ..dialogs import (blocked_dialog, changed_flags_dialog, io_ex_dialog,
nothing_found_dialog, queue_not_empty_dialog, remove_deps_dialog,
- remove_queue_dialog, remove_updates_dialog, unmask_dialog)
+ remove_queue_dialog, remove_updates_dialog, unmask_dialog,
+ no_versions_dialog)
from ..exceptions import PreReqError
# even more GUI stuff
@@ -748,14 +750,18 @@ class MainWindow (Window):
store.clear()
if name:
- for cat, pkg, is_inst in self.db.get_cat(name, self.sortPkgListByName):
- if is_inst:
+ for pkg in self.db.get_cat(name, self.sortPkgListByName):
+ if pkg.disabled:
+ warning(_("Package '%s/%s' is disabled."), pkg.cat, pkg.pkg)
+ continue
+
+ if pkg.inst:
icon = self.icons["installed"]
elif not self.showAll:
continue # ignore not installed packages
else:
icon = None
- store.append([icon, pkg, cat])
+ store.append([icon, pkg.pkg, pkg.cat])
def build_version_list (self):
store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
@@ -809,6 +815,8 @@ class MainWindow (Window):
self.slotcol.set_visible(False)
packages = system.sort_package_list(system.find_packages(cp, masked=True))
+ if not packages:
+ raise VersionsNotFoundException(cp)
# append versions
for vers, inst, slot in ((x.get_version(), x.is_installed(), get_slot(x)) for x in packages):
@@ -1344,8 +1352,17 @@ class MainWindow (Window):
"""
store, it = selection.get_selected()
if it:
+ oldcp = self.selCP
+
self.selCP = "%s/%s" % (store.get_value(it, 2), store.get_value(it, 1))
- self.fill_version_list(self.selCP)
+ try:
+ self.fill_version_list(self.selCP)
+ except VersionsNotFoundException, e:
+ warning(_("No versions of package '%s' found!.") % self.selCP)
+ no_versions_dialog(self.selCP)
+ self.db.disable(self.selCP)
+ self.selCP = oldcp
+
return True
def cb_pkg_list_header_clicked(self, col):
@@ -1832,8 +1849,8 @@ class MainWindow (Window):
self.__save_queue = (ret == gtk.RESPONSE_YES)
self.queue.kill_emerge()
- # write session
- self.session.save()
+ # write sessions
+ Session.close()
return False
diff --git a/portato/session.py b/portato/session.py
index 07d27fc..667771a 100644
--- a/portato/session.py
+++ b/portato/session.py
@@ -13,11 +13,14 @@
from __future__ import absolute_import, with_statement
import os, os.path
+from UserDict import DictMixin
from .config_parser import ConfigParser, SectionNotFoundException
from .constants import SESSION_DIR
from .helper import debug, info
+sessionlist = []
+
class Session (object):
"""
A small class allowing to save certain states of a program.
@@ -31,13 +34,14 @@ class Session (object):
# the current session format version
VERSION = 1
- def __init__ (self, file, name="", oldfiles = []):
+ def __init__ (self, file, name="", oldfiles = [], register = True):
"""
Initialize a session with a certain file inside L{SESSION_DIR}.
@param file: the file in L{SESSION_DIR}, where the options will be saved.
@param oldfiles: old file names for the same file
@param name: short name describing the type of session
+ @param register: register in the global sessionlist, which is closed at the end
"""
self._cfg = None
@@ -60,9 +64,9 @@ class Session (object):
self._cfg = ConfigParser(file)
if name:
- i = _("Loading '%s' session from '%s'.") % (name, self._cfg.file)
+ i = _("Loading '%s' session from %s.") % (name, self._cfg.file)
else:
- i = _("Loading session from '%s'.") % self._cfg.file
+ i = _("Loading session from %s.") % self._cfg.file
info(i)
@@ -72,6 +76,9 @@ class Session (object):
if e.errno == 2: pass
else: raise
+ # register
+ if register: sessionlist.append(self)
+
# add version check
self.add_handler(([("version", "session")], self.check_version, lambda: self.VERSION))
@@ -127,6 +134,15 @@ class Session (object):
self._cfg.write()
+ @classmethod
+ def close (cls):
+ for s in sessionlist:
+ if s._name != "MAIN":
+ info(_("Saving '%s' session to %s.") % (s._name, s._cfg.file))
+ else:
+ info(_("Saving session to %s.") % s._cfg.file)
+ s.save()
+
def set (self, key, value, section = ""):
if not section: section = self._name
@@ -193,3 +209,29 @@ class Session (object):
def check_version (self, vers):
pass # do nothing atm
+
+class SectionDict (DictMixin):
+ """A class, which maps a specific section of a session to a dictionary."""
+
+ def __init__ (self, session, section):
+ self._section = section.upper()
+ self._session = session
+
+ def __getitem__ (self, name):
+ item = self._session.get(name, section = self._section)
+
+ if item is None:
+ raise KeyError, "%s not in section %s" % (name, self._section)
+ return item
+
+ def __setitem__ (self, name, value):
+ self._session.set(name, value, section = self._section)
+
+ def __delitem__ (self, name):
+ self._session.remove(name, self._section)
+
+ def keys (self):
+ return self._session._cfg.vars[self._section].keys()
+
+ def __contains__ (self, name):
+ return self._session.get(name, self._section) is not None